Студопедия
Случайная страница | ТОМ-1 | ТОМ-2 | ТОМ-3
АрхитектураБиологияГеографияДругоеИностранные языки
ИнформатикаИсторияКультураЛитератураМатематика
МедицинаМеханикаОбразованиеОхрана трудаПедагогика
ПолитикаПравоПрограммированиеПсихологияРелигия
СоциологияСпортСтроительствоФизикаФилософия
ФинансыХимияЭкологияЭкономикаЭлектроника

Представление цвета в WPF

Размер и выравнивание | Встроенные контейнеры компоновки | Прокрутка и декорирование содержимого | Элементы управления содержимым | Списковые элементы управления | Прочие элементы управления | Логические ресурсы | Базовые концепции привязки данных | Практическое использование привязки данных | Конвертеры значений |


Читайте также:
  1. Cквозь реальные цвета.
  2. Аномалии развития, прорезывания зубов, изменение их цвета
  3. Быть здоровым и процветающим поколениям!
  4. Глава 7 Интерпретация и представление результатов........................................221 1 страница
  5. Глава 7 Интерпретация и представление результатов........................................221 10 страница
  6. Глава 7 Интерпретация и представление результатов........................................221 11 страница
  7. Глава 7 Интерпретация и представление результатов........................................221 12 страница

WPFпозволяет работать с двумя цветовыми моделями:

1. RGB – распространённая цветовая модель, в которой каждый компонент цвета (красный, зелёный, синий) представлен одним байтом. Дополнительно может использоваться альфа-канал (кодируемый одним байтом), чтобы задать прозрачность цвета (0 – полностью прозрачный, 255 – полностью непрозрачный).

2. scRGB –в этой модели каждый компонент цвета (альфа-канал, красный, зелёный, синий) представлен с помощью 16- или 32-битных чисел с плавающей точкой в диапазоне от 0 до 1.

Структура System.Windows.Media.Color хранит информацию о цвете. Свойства структуры позволяют прочитать или задать отдельную цветовую компоненту в любой из двух цветовых моделей, а статические методы – создать цвет на основе компонент или произвести простейшие операции с цветом:

Color c1 = Color.FromRgb(10, 20, 30);

Color c2 = Color.FromArgb(250, 10, 20, 32);

Color c3 = Color.FromScRgb(0.4f, 0.5f, 0.7f, 0.2f);

Color c4 = (c1 + c2)*2;

byte red = c4.R;

bool flag = Color.AreClose(c1, c2);

Класс System.Windows.Media.Colors содержит набор именованных цветов в виде статических свойств для чтения. Класс System.Windows.SystemColors предоставляет аналогичный набор для стандартных цветов системы[19]:

Color c1 = Colors.IndianRed;

Color c2 = SystemColors.ControlColor;

При установке цвета в разметке XAMLможно использовать строки следующего формата:

· ИмяЦвета – одно из имён свойств в классе Colors;

· #rgbили #rrgggbb– аналог вызова Color.FromRgb(0xrr, 0xgg, 0xbb);

· #argb или #aarrgggbb – аналог Color.FromArgb(0xaa, 0xrr, 0xgg, 0xbb);

· sc# a r g b – аналогColor.FromScRgb(a, r, g, b) (числа ).

<Button Background="Red" />

<Button Background="#64A" />

<Button Background="#FF00674A" />

<ButtonBackground="sc# 0.1 0.1 0.5 0.3" />

Ради справедливости отметим, что в приведённом выше примере на самом деле используется не цвет, а соответствующая кистьSolidColorBrush:

<!-- эквивалент<Button Background="Red" /> -->

<Button>

<Button.Background>

<SolidColorBrush Color="Red" />

</Button.Background>

</Button>

Лучшие кисти

Кисть – это объект, используемый для заполнения фона, переднего плана, границы, линии. Любая кисть является потомком абстрактного класса System.Windows.Media.Brush.Имеется несколько стандартных классов кистей:

· SolidColorBrush – закрашивает область сплошным цветом;

· LinearGradientBrush– рисует область, используя линейное градиентное заполнение, представляющее собой плавный переход от одного цвета к другому (и, необязательно, к следующему, потом ещё к одному и т.д.);

· RadialGradientBrush– закрашивает область, используя радиальное градиентное заполнение.

· ImageBrush– рисует область, используя изображение, которое может растягиваться, масштабироваться или многократно повторяться.

· DrawingBrush– рисует область, используя объект Drawing (этот объект может включать заданные фигуры и битовые карты).

· VisualBrush– заполняет область, используя объект Visual.

SolidColorBrush –самая простая из кистей. Во всех предыдущих примерах разметки использовалась именно она. Свойство SolidColorBrush.Colorопределяет цвет сплошной заливки. Анализатор XAMLспособен автоматически создать SolidColorBrush на основе строки с представлением цвета. Также отметим, что в классе SystemColors задан набор статических кистей SolidColorBrush, соответствующих системным цветам.

<Button Background="{x:Static SystemColors.ControlBrush}" />

Кисть LinearGradientBrush создаёт заполнение, которое представляет собой переход от одного цвета к другому.Ниже приведён простейший пример градиента, который закрашивает прямоугольник по диагонали от синего (в левом верхнем углу) к белому (в правом нижнем углу) цвету:

<Rectangle Width="150" Height="100">

<Rectangle.Fill>

<LinearGradientBrush>

<GradientStop Color="Blue" Offset="0" />

<GradientStop Color="White" Offset="1" />

</LinearGradientBrush>

</Rectangle.Fill>

</Rectangle>

ГрадиентвLinearGradientBrushстроитсяпоследующимправилам. Вокруг заполняемой области очерчивается виртуальный прямоугольник, у которого левый верхний угол имеет координаты , а правый нижний – . В этих координатах при помощи свойств StartPoint и EndPointзадаётся вектор градиента (по умолчанию StartPoint=0,0 и EndPoint=1,1)[20]. Коллекция GradientStopsсодержит опорные точки градиента – объекты GradientStop с указанием на цвет и смещение относительно вектора градиента (0 – начало вектора, 1 – конец вектора)[21]:

<LinearGradientBrush StartPoint="0.5,0" EndPoint="1,1">

<GradientStop Color="Blue" Offset="0" />

<GradientStop Color="Azure" Offset="0.4" />

<GradientStop Color="White" Offset="1" />

</LinearGradientBrush>

Свойство LinearGradientBrush.SpreadMethod управляет тем, как будет распространяться градиент за пределы вектора градиента. По умолчанию оно имеет значение Pad–области вне градиента заполняются соответствующим сплошным цветом. Допустимы также значения Reflect (для обращения градиента) и Repeat (для дублирования той же цветовой последовательности).

Рис. 28.Демонстрация различных значений SpreadMethod[22].

Кисть RadialGradientBrush работает подобно LinearGradientBrush, но использует градиент, который исходит из начальной точки в радиальном направлении. Координаты начальной точки задаёт свойство GradientOrigin, которое по умолчанию равно [23]. Градиент распространяется до границы круга градиента, который описывается тремя свойствами: Center,RadiusX и RadiusY.В классе RadialGradientBrush имеются свойства MappingMode и SpreadMethod.

Ниже показан пример использования RadialGradientBrush для создания иллюзии глубины фигуры:

<Ellipse Margin="5" Height="200" Width="200"

Stroke="Black" StrokeThickness="1">

<Ellipse.Fill>

<RadialGradientBrush RadiusX="1" RadiusY="1"

GradientOrigin="0.7,0.3">

<GradientStop Color="White" Offset="0" />

<GradientStop Color="Blue" Offset="1" />

</RadialGradientBrush>

</Ellipse.Fill>

</Ellipse>

Рис. 29.Использование RadialGradientBrush.

КистьImageBrushпозволяетзаполнитьобластьизображением, которое идентифицируется свойством ImageSource. Изображение может быть либо битовым (формата BMP, PNG, GIF, JPEG, ICON), либо векторным. В первом случае картинка идентифицируется URI, который обычно указывает на двоичный ресурс сборки. Во втором случае используется объект DrawingImage.

Для настройки ImageBrush можно использовать следующие свойства:

· Stretch – правило растяжения картинки, если она не совпадает с заполняемой областью: None– не растягивать, Fill –заполнить, исказив пропорции,Uniform–сохранить пропорции, заполнить сколько получиться, UniformToFill –сохранить пропорции, заполнить всё, обрезав лишнее;

· AlignmentX и AlignmentY–правила выравнивания картинки, если она меньше заполняемой области;

· Viewbox– фрагмент, который вырезается из картинки для использования в кисти;

· ViewboxUnits– способ определения координат Viewbox (Absolute– абсолютные, RelativeToBoundBox – относительные при помощи виртуального прямоугольника);

· Viewport –фрагмент закрашиваемой области, на который отображается картинка кисти.Свойство применяется, когда картинкой нужно «замостить»большую область.

· ViewportUnits– способ определения координат Viewport;

· TileMode– способ заполнения картинкой кисти большой области:None– без заполнения, Tile– простое заполнение, FlipX, FlipY, FlipXY– заполнения, с отражением по указанной оси.

В следующем примере кистьImageBrush используется для заполнения фонаGrid. Из изображения информационной иконки вырезается четверть, которая повторяется на фоне двадцать раз (четыре строки по пять фрагментов):

<Grid>

<Grid.Background>

<ImageBrush ImageSource="info.png" Viewbox="0,0 0.5,0.5"

Viewport="0,0 0.2,0.25" TileMode="Tile" />

</Grid.Background>

</Grid>

Рис. 30.Кисть ImageBrush.

Кисть DrawingBrushиспользует для заполнения области объект Drawing, помещённый в одноимённое свойство кисти. Класс Drawing представляет двухмерные рисунки. Подробно работа с Drawing будет разобрана в одном из следующих параграфов. Отметим, что кисти ImageBrush, DrawingBrush и рассматриваемая далее VisualBrush унаследованы от общего предка – класса TileBrush.Этот класс определяет свойства, связанные с заполнением области картинкой (Viewbox, Viewport, TileMode).

VisualBrush– разновидность кисти, позволяющая брать визуальное со­держимое элемента и использовать его для заполнения любой поверхности. Например, с помощью VisualBrush можно скопировать внешний вид кнопки. Однако такая «кнопка» не будет реагировать на нажатия или получать фокус– это просто копия внешнего вида элемента.

Интересно, что VisualBrushне просто копирует визуальное представление, а отслеживает изменения в копируемом элементе. В следующем примере поверхность окна меняется, когда пользователь редактирует текст в поле ввода:

<Canvas>

<Canvas.Background>

<VisualBrush Visual="{Binding ElementName=txt}"

TileMode="FlipXY" Viewport="0,0 0.4,0.5" />

</Canvas.Background>

<TextBox Name="txt" FontSize="20" Width="180"

Canvas.Left="20" Canvas.Top="20" />

</Canvas>

Рис. 31.Кисть VisualBrush.

Прозрачность

В WPF поддерживается истинная прозрачность. Каждый элемент и кисть содержит свойство Opacity, определяющее степень прозрачности и принимающее вещественные значения из диапазона , Например, Opacity=0.9создаёт эффект 90% видимости и 10% прозрачности. Также можно использовать цвет (и соответствующую сплошную кисть) с альфа-каналом, меньшим максимума.

Все элементы содержат свойство OpacityMask, которое принимает любую кисть. Альфа-канал кисти определяет степень прозрачности (другие цветовые компоненты значения не имеют). Применение OpacityMask с кистями, содержащими градиентный переход от сплошного к прозрачному цвету создаёт эффект постепенного «исчезновения» поверхности. Если поместить в OpacityMaskкисть DrawingBrush, можно создать прозрачную область заданной формы.

В следующем примере OpacityMask используется в сочетании с VisualBrush для создания популярного эффекта отражения. По мере набора текстаVisualBrush рисует ниже отражение этого текста. VisualBrushзакрашивает прямоугольник, использующий свойство OpacityMask для постепенного затухания отражения.

<Grid>

<Grid.RowDefinitions>

<RowDefinition Height="30" />

<RowDefinition Height="*" />

</Grid.RowDefinitions>

<TextBox Name="txt" FontSize="20" FontWeight="Bold" />

<Rectangle Grid.Row="1" RenderTransformOrigin="1,0.5">

<Rectangle.Fill>

<VisualBrush Visual="{Binding ElementName=txt}" />

</Rectangle.Fill>

<Rectangle.OpacityMask>

<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">

<GradientStop Offset="0.3" Color="Transparent" />

<GradientStop Offset="1" Color="#44000000" />

</LinearGradientBrush>

</Rectangle.OpacityMask>

<Rectangle.RenderTransform>

<ScaleTransform ScaleY="-1" />

</Rectangle.RenderTransform>

</Rectangle>

</Grid>

Рис. 32.Эффект отражения.

10. трансформации и эффекты

Трансформация – это заданное изменение координатной системы, в которой отображается элемент.Описание таких трансформаций на плоскости, как масштабирование, отражение и поворот, можно выполнить в терминах числовых матриц размером . Чтобы представить в матричной форме операцию сдвига координатной системы, используют однородные координаты.

Однородными координатами точки является тройка вида . Если дана тройкачисел , соответствующая точка на плоскости находится после применения нормировки –деления на : . Тройки вида описывают в однородных координатах бесконечно удалённую точку.

В терминах однородных координат основные трансформации можно выразить следующим образом:

1. Масштабирование (включая отражения): .

2. Поворот на угол : .

3. Сдвиг: .

Комбинация трансформаций выполняется как умножение матриц.

В WPFтрансформации представлены классами, унаследованными от абстрактного класса System.Windows.Media.Transform. Набор предопределённых трансформаций перечислен в табл. 8.

Таблица 8

Классы трансформаций

Имя класса Описание Важныесвойства
TranslateTransform Смещает координатную систему на указанную величину X, Y
RotateTransform Поворачивает координатную систему вокруг заданной точки Angle,CenterX,CenterY
ScaleTransform Масштабирует координатную систему. Можно применять разную степень масштабирования по измерениям X и Y ScaleX,ScaleY,CenterX,CenterY
SkewTransform Деформирует координатную систему, наклоняя еёоси на заданное число градусов AngleX,AngleY,CenterX,CenterY
MatrixTransform Выполняет трансформацию, используя указанную матрицу вида Matrix
TransformGroup Комбинирует несколько трансформаций. Порядок трансформаций в группе имеет значение Children

Укажем некоторые возможности задания трансформаций в WPF:

· КлассUIElementопределяетсвойстваRenderTransformиRenderTransformOrigin. RenderTransform– это трансформация, выполняемая после процесса компоновки непосредственно перед отображением элемента. RenderTransformOrigin задаёт стартовую (неподвижную) точку трансформации. По умолчанию это точка имеет координаты (0,0) (координаты точки относительные, в терминах виртуального ограничивающего прямоугольника).

· Класс FrameworkElementсодержит свойство LayoutTransform для трансформации, применяемой до процесса компоновки.

· Класс Brushимеет свойства RelativeTransform и Transform,позволяющие выполнить трансформацию кисти до и после её применения.

Следующий пример демонстрирует использованиетрансформаций.

<StackPanel Orientation="Horizontal">

<Button Height="30" Width="60" Content="Rotate">

<Button.LayoutTransform>

<RotateTransform Angle="-45" CenterX="30" CenterY="15"/>

</Button.LayoutTransform>

</Button>

<Button Height="30" Width="60" Content="Skew">

<Button.RenderTransform>

<SkewTransform AngleX="30" AngleY="0" />

</Button.RenderTransform>

</Button>

<Button Height="30" Width="60" Content="Matrix">

<Button.LayoutTransform>

<MatrixTransform Matrix="1,0.5,1,-1,20,10" />

</Button.LayoutTransform>

</Button>

</StackPanel>

Рис. 33. Примеры трансформаций.

Кроме трансформаций, WPFподдерживает применение к элементам эффектов, таких как размытие, отбрасывание тени, сияние. Эффекты в WPFделятся на две категории – битовые эффекты и эффекты пиксельных шейдеров.

Битовые эффекты представлены классами, унаследованными от класса System.Windows.Media.Effects.BitmapEffect. Все битовые эффекты обрабатываются без использования ресурсов видеокарты[24]. Набор предопределённыхбитовых эффектов перечислен в табл. 9.

Таблица 9

Классы битовых эффектов

Имя класса Описание Важныесвойства
BlurBitmapEffect Размывает содержимое элемента Radius, KernelType
BevelBitmapEffect Добавляет выпуклую рамку вокруг содержимого BevelWidth, Relief, EdgeProfile, LightAngle, Smoothness
EmbossBitmapEffect Создаёт эффект «тиснения», выделяя границы и линии LightAngle, Relief
OuterGlowBitmapEffect Добавляет цветное сияние вокруг содержимого GlowColor, GlowSize, Noise, Opacity
DropShadowBitmapEffect Добавляет прямоугольную тень за элементом Color, Direction, Noise, Opacity, ShadowDepth, Softness
BitmapEffectGroup Применяет комбинацию битовых эффектов. Порядок эффектов имеет значение, поскольку каждый применяется поверх существующих Children

Эффекты пиксельных шейдеров используют одноимённую технологию графических процессоров. Они представлены следующими классами:

· BlurEffect – эффект размытия, подобный BlurBitmapEffect;

· DropShadowEffect – эффект тени, подобный DropShadowBitmapEffect;

· ShaderEffect – любой эффект пиксельных шейдеров, описанный при помощи объекта PixelShader.

Для применения эффектов пиксельных шейдеров класс Visual определяет свойство VisualEffect, а класс UIElement – свойство Effect. Следующая разметка демонстрирует эффектыBlurEffect и DropShadowEffect.

<StackPanel Orientation="Horizontal">

<Button Height="40" Width="80" Content="Blur" Margin="10">

<Button.Effect>

<BlurEffect Radius="3" />

</Button.Effect>

</Button>

<Button Height="40" Width="80" Content="Shadow" Margin="10">

<Button.Effect>

<DropShadowEffect ShadowDepth="5" Direction="300"

Color="Blue" />

</Button.Effect>

</Button>

</StackPanel>

Рис. 34. Применение эффектов.

11. Классы drawing иvisual

Абстрактный класс System.Windows.Media.Drawing представляет двухмерные рисунки– другими словами, содержит всю информацию, которая нужна для отображения части векторного или битового изображения.

Набор стандартных наследников Drawing перечислен в табл. 10.

Таблица 10

Классы Drawing

Имя класса Описание Важныесвойства
GeometryDrawing Обёртывает геометрию заполняющей кистью и пером, очерчивающим контур Geometry, Brush, Pen
ImageDrawing Обёртывает графический образ (обычно битовую карту из файла) прямоугольником, определяющим его границы ImageSource, Rect
VideoDrawing Комбинирует MediaPlayer, используемый для воспроизведения видео, с прямоугольником, задающим его границы Player, Rect
GlyphRunDrawing Обёртывает низкоуровневый текстовый объект, известный как GlyphRun, с кистью, рисующей его GlyphRun, ForegroundBrush
DrawingGroup Комбинирует коллекцию объектов Drawing любого типа. DrawingGroup позволяет создавать составные рисунки и применять эф­фекты ко всей коллекции сразу BitmapEffect, BitmapEffectInput, Children, ClipGeometry, GuidelineSet, Opacity, OpacityMask, Transform

Классы-наследники Drawing не являются элементами, а, значит, не могут быть помещены в пользовательский интерфейс. Для отображения рисунка нужно использовать один из трёх классов, перечисленных ниже:

· DrawingImage – этот класс унаследован от ImageSource и позволяет разместить рисунок внутри элемента Image;

· DrawingBrush – унаследован от Brush и позволяет обернуть рисунок кистью, которую можно использовать для заполнения любой поверхности;

· DrawingVisual– унаследован от Visual и позволяет поместить рисунок в низкоуровневый визуальный объект.

Следующий фрагмент разметки демонстрирует создание объекта GeometryDrawing и размещение его в элементе Image, который, в свою очередь, помещён на кнопку.

<Button Height="100" Width="210">

<Image>

<Image.Source>

<DrawingImage>

<DrawingImage.Drawing>

<GeometryDrawing>

<GeometryDrawing.Geometry>

<GeometryGroup>

<EllipseGeometry Center="50,50" RadiusX="45" RadiusY="20"/>

<EllipseGeometry Center="50,50" RadiusX="20" RadiusY="20" />

</GeometryGroup>

</GeometryDrawing.Geometry>

<GeometryDrawing.Brush>

<LinearGradientBrush>

<GradientStop Color="Blue" Offset="0" />

<GradientStop Color="#CCF" Offset="1" />

</LinearGradientBrush>

</GeometryDrawing.Brush>

<GeometryDrawing.Pen>

<Pen Thickness="4" Brush="Black" />

</GeometryDrawing.Pen>

</GeometryDrawing>

</DrawingImage.Drawing>

</DrawingImage>

</Image.Source>

</Image>

</Button>

Рис. 35. Кнопка с изображением, содержащим GeometryDrawing.

В приложениях интенсивной графики, работающих с большим числом графических примитивов, использование фигур, геометрий и рисунков не оправдано с точки зрения производительности. В таких ситуациях следует применять низкоуровневую модель визуального слоя (visual layer). Базовая идея в том, чтобы определить графический элемент как объект Visual, который чрезвычайно облегчён и требует меньших накладных расходов, чем Geometry или Path.

Класс Visualявляется абстрактным, поэтому используются его классы-наследники. К ним относится UIElement (корень элементной модели WPF), Viewport3DVisual (отображениетрёхмерныхмоделей) и ContainerVisual (базовый контейнер, содержащийдругие объекты Visual). Наиболее полезный класс – это DrawingVisual(наследникContainerVisual), добавляющий поддержку, необходимую для рисования графического содержимого.

Чтобы нарисовать содержимое в DrawingVisual, вызывается его экземплярный метод RenderOpen(). Этот метод возвращает объектDrawingContext.Класс DrawingContext состоит из методов (перечисленных в табл. 11), которые добавляют графические детали к создаваемому визуальному объекту. Завершив рисование, следует вызвать у контекста экземплярный метод Close().Ниже показан пример создания элемента, содержащего чёрный треугольник:

DrawingVisualvisual = newDrawingVisual();

using (DrawingContext dc = visual.RenderOpen())

{

Pen drawingPen = newPen(Brushes.Black, 3);

dc.DrawLine(drawingPen, newPoint(0, 50), newPoint(50, 0));

dc.DrawLine(drawingPen, newPoint(50, 0), newPoint(100, 50));

dc.DrawLine(drawingPen, newPoint(0, 50), newPoint(100, 50));

}

Фактически, вызов методовDrawingContext не выполняет рисования, а определяет внешний вид визуального элемента. После вызоваClose() готовый рисунок доступен только для чтения через свойство DrawingVisual.Drawing.

Таблица 11

Методы класса DrawingContext

Имя метода Описание
DrawLine(), DrawRectangle(), DrawRoundedRectangle(), DrawEllipse() Рисует указанную фигуру в указанной точке, с указанным заполнением и контуром
DrawGeometry(), DrawDrawing() Рисует объекты Geometry и Drawing
DrawText() Рисует текст в указанном месте. Детали оформления текста передаются методу в объекте FormattedText
DrawImage() Рисует битовое изображение в указанной области
DrawVideo() Рисует видео-содержимое (помещённое в объект MediaPlayer)
Pop() Отменяет действие последнего вызова метода Push XXX ()
PushClip() Ограничивает рисование определённой областью. Содержимое, выходящее за её пределы, не рисуется
PushEffect() Применяет BitmapEffect к последующим операциям рисования
PushOpacity(), PushOpacityMask() Применяет новые установки прозрачности или маску прозрачности, чтобы сделать последующие операции рисования частично прозрачными
PushTransform() Устанавливает объект Transform, который будет применён к последующим операциям рисования

Чтобы отобразить визуальный объект, понадобится помощь полноценного элемента WPF, который добавит его в визуальное дерево. Ниже описаны шаги, необходимые для размещения визуальных объектов в элементе.

· Вызов методов AddVisualChild() и AddLogicalChild() элемента для регистрации визуального объекта. Эти действия не являются необходимыми для того, чтобы визуальные элементы отобразились. Они нужны, чтобы гарантировать корректное отслеживание элементов в визуальном и логическом дереве, а также для взаимодействия с другими средствами WPF, такими как проверка попадания.

· Переопределение свойства VisualChildrenCount и возврат количества добавленных визуальных объектов.

· Переопределение метода GetVisualChild() и добавление кода, необходимого для возврата вашего визуального элемента по номеру индекса.

ПереопределениеVisualChildrenCount и GetVisualChild()выполняет графическое замещение элемента. Например, если вы переопределите эти два метода в пользовательском окне, то не увидите остального содержимого этого окна. Вместо этого вы увидите только добавленные визуальные объекты.По этой причине принято создавать выделенный пользовательский класс, который служит оболочкой для визуальных объектов, которые нужно отобразить.

14. СТИЛИИ триггеры

Стиль – это коллекция значений свойств, которые могут быть применены к элементу. В WPFстили играют ту же роль, которую CSS играет в HTML-разметке. Подобно CSS, стили WPF позволяют определять общий набор характеристик форматирования и применять их по всему приложению для обеспечения согласованности. Стили могут работать автоматически, предназначаться для элементов конкретного типа и каскадироваться через дерево элементов.

Рассмотрение стилей начнём с конкретного примера, в котором определяется и применяется стиль для кнопок.

<StackPanel Orientation="Horizontal" Margin="10">

<StackPanel.Resources>

<Style x:Key="buttonStyle">

<Setter Property="Button.FontSize" Value="22"/>

<Setter Property="Button.Background" Value="Purple"/>

<Setter Property="Button.Foreground" Value="White"/>

<Setter Property="Button.Height" Value="50"/>

<Setter Property="Button.Width" Value="50"/>

<Setter Property="Button.RenderTransformOrigin" Value=".5,.5"/>

<Setter Property="Button.RenderTransform">

<Setter.Value>

<RotateTransform Angle="10"/>

</Setter.Value>

</Setter>

</Style>

</StackPanel.Resources>

<Button Style="{StaticResource buttonStyle}">1</Button>

<Button Style="{StaticResource buttonStyle}">2</Button>

<Button Style="{StaticResource buttonStyle}">3</Button>

</StackPanel>

Рис. 37.Кнопки, к которым применён стиль.

Любой стиль – это объект класса System.Windows.Style. Основным свойством стиля является коллекция Setters, в которой каждый элемент задаётзначение для некоторого свойства зависимостей (стили не работают с обычными свойствами – только со свойствами зависимостей). В разметке стилядочерние элементыс именем<Setter> автоматически помещаются в коллекцию Setters.

Чтобы задать значение для свойства зависимостей, нужно указать имя этого свойства. В нашем примере имя включает префикс –класс элемента. Если требуется применить один стиль к визуальным элементам разных типов, для префиксаиспользуется имя общего типа-предка:

<StackPanel Orientation="Horizontal" Margin="10">

<StackPanel.Resources>

<Style x:Key="controlStyle">

<Setter Property="Control.FontSize" Value="22"/>

<Setter Property="Control.Background" Value="Purple"/>

<Setter Property="Control.Foreground" Value="White"/>

<Setter Property="Control.Height" Value="50"/>

<Setter Property="Control.Width" Value="50"/>

</Style>

</StackPanel.Resources>

<Button Style="{StaticResource controlStyle}" Content="1"/>

<TextBox Style="{StaticResource controlStyle}" Text="Hi"/>

<Expander Style="{StaticResource controlStyle}" Content="3"/>

</StackPanel>

Свойство стиля TargetType позволяет указать конкретный тип, к которому применяется стиль. В этом случае префикс в установщиках свойств использовать необязательно:

<Stylex:Key="baseStyle"TargetType="{x:TypeButton}">

<Setter Property="FontSize" Value="22"/>

<Setter Property="Background" Value="Purple"/>

<Setter Property="Foreground" Value="White"/>

</Style>

Определяя стиль, можно построить его на основе стиля-предка. Для этого следует воспользоваться свойством стиля BasedOn. Стили также могут содержатьлокальные логические ресурсы (коллекция Resources):

<Stylex:Key="baseStyleWH"TargetType="{x:TypeButton}"

BasedOn="{StaticResource baseStyle}">

<Style.Resources>

<sys:Double x:Key="size">50</sys:Double>

</Style.Resources>

<Setter Property="Height" Value="{StaticResource size}"/>

<Setter Property="Width" Value="{StaticResource size}"/>

</Style>

Кроме установки свойств зависимостей, стиль позволяет задать обработчики событий. Дляэтогоприменяетсяэлемент<EventSetter>:

<Style x:Key="buttonStyle" TargetType="{x:Type Button}">

<Setter Property="FontSize" Value="22"/>

<EventSetter Event="MouseEnter" Handler="button_MouseEnter"/>

</Style>

Как показывает первый пример параграфа, для применения стилей в элементе управления следует установить свойство Style, которое определено в классе FrameworkElement[25]. Хотя стиль можно определить в самом элементе управления, обычно для этого используются ресурсы окна или приложения. Если при описании стиля в ресурсе задать TargetType, но не указывать ключ, стиль будет автоматически применяться ко всем элементам указанного типа.

<StackPanel Orientation="Horizontal" Margin="10">

<StackPanel.Resources>

<Style TargetType="{x:Type Button}">

<Setter Property="FontSize" Value="22"/>

<Setter Property="Background" Value="Purple"/>

<Setter Property="Foreground" Value="White"/>

<Setter Property="Height" Value="50"/>

<Setter Property="Width" Value="50"/>

</Style>

</StackPanel.Resources>

 

<!-- эта кнопка будет иметь указанный выше стиль -->

<Button Content="1"/>

 

<!-- у этой кнопки такой же стиль, но задана своя ширина -->

<Button Width="100" Content="2"/>

 

<!-- убрали стиль у кнопки, присвоивStyle значение null -->

<Button Style="{x:Null}" Content="3"/>

</StackPanel>

При описании стилей часто применяются триггеры. Триггер – это способ описания реакции на изменение свойства зависимостей, альтернативный применению обработчиков событий. Триггеры имеют два достоинства. Во-первых, их легко задать декларативно. Во-вторых, для триггера указывается только условие старта, условие завершения действия триггера указывать не надо.

Каждый триггер является экземпляром класса, который наследуется от System.Windows.TriggerBase. В WPFдоступно пять видов триггеров:

· Триггер свойства – класс Trigger. Это триггер самого простого типа. Он следит за появлением изменений в свойстве зависимостей.

· Триггер события – класс EventTrigger. Триггер следит за наступлением указанного события.

· Триггер данных – класс DataTrigger.Этот триггер работает со связыванием данных. Он похож на триггер свойства, но следит за появлением изменений не в свойстве зависимостей, а в любых связанных данных.

· Мультитриггер – класс MultiTrigger. Данный триггер объединяет несколько триггеров свойств. Он стартует, если выполняются все условия объединяемых триггеров.

· Мультитриггер данных – класс MultiDataTrigger. Подобен мультитриггеру, но объединяет несколько триггеров данных.

Для декларации и хранения триггеров у классов FrameworkElement, Style, DataTemplateиControlTemplateимеется коллекция Triggers. Правда, FrameworkElementподдерживает только триггеры событий.

Рассмотрим применение триггеров свойств при описании стиля. У триггера свойства настраиваются:

§ Имя свойства, с которым связан триггер;

§ Значение свойства, которое задаёт условие старта триггера;

§ Коллекция Setters, описывающая действие триггера.

Взяв за основу первый пример этого параграфа, модифицируем стиль при помощи триггера так, чтобы поворот кнопки происходил при перемещении над ней указателя мыши:

<Style x:Key="buttonStyle" TargetType="{x:Type Button}">

<Style.Triggers>

<Trigger Property="IsMouseOver" Value="True">

<Setter Property="Foreground" Value="Black"/>

<Setter Property="RenderTransform">

<Setter.Value>

<RotateTransform Angle="10"/>

</Setter.Value>

</Setter>

</Trigger>

</Style.Triggers>

<Setter Property="FontSize" Value="22"/>

<Setter Property="Background" Value="Purple"/>

<Setter Property="Foreground" Value="White"/>

<Setter Property="Height" Value="50"/>

<Setter Property="Width" Value="50"/>

<Setter Property="RenderTransformOrigin" Value=".5,.5"/>

</Style>

В следующем примере триггер используется для того, чтобы изменить внешний вид TextBoxпри наступлении ошибки проверки. Обратите внимание на использование привязки данных и работу с присоединёнными свойствами.

<Style x:Key="textBoxStyle" TargetType="{x:Type TextBox}">

<Style.Triggers>

<Trigger Property="Validation.HasError" Value="True">

<Setter Property="Background" Value="Red"/>

<Setter Property="ToolTip"

Value="{Binding RelativeSource={RelativeSource Self},

Path=(Validation.Errors)[0].ErrorContent}"/>

</Trigger>

</Style.Triggers>

</Style>

В стилях могут применяться не только триггеры свойств, но и триггеры данных, которые позволяют отследить изменения в обычном свойстве любого объекта.NET (а не только в свойствах зависимостей). Вместо свойства Propertyв триггерах данных применяется свойство Binding, содержащее привязку данных. Ниже демонстрируется пример стиля с триггером данных – элемент управления TextBox делается неактивным, если в него ввести «disabled»[26].

<StackPanel>

<StackPanel.Resources>

<Style TargetType="{x:Type TextBox}">

<Style.Triggers>

<DataTrigger Binding="{Binding

RelativeSource={RelativeSource Self},

Path=Text}"

Value="disabled">

<Setter Property="IsEnabled" Value="False"/>

</DataTrigger>

</Style.Triggers>

<Setter Property="Background"

Value="{Binding RelativeSource={RelativeSource Self},

Path=Text}"/>

</Style>

</StackPanel.Resources>

<TextBox Margin="3"/>

<TextBox Margin="3"/>

<TextBox Margin="3"/>

</StackPanel>

Рис. 38.Демонстрация стиля с триггером данных.

В предыдущих примерах коллекция Triggers содержала только один триггер. В эту коллекцию можно поместить любое количество триггеров– они будут работать независимо друг от друга. Если необходимо создать триггер, стартующий при наступлении совокупности изменений, следует воспользоваться классом MultiTrigger (или MultiDataTrigger– для триггеров данных). Ниже описан триггер стиля, при применении которого кнопка поворачивается, если над ней находится указатель мыши,и она имеет фокус ввода.

<Style.Triggers>

<MultiTrigger>

<MultiTrigger.Conditions>

<Condition Property="IsMouseOver" Value="True"/>

<Condition Property="IsFocused" Value="True"/>

</MultiTrigger.Conditions>

<Setter Property="RenderTransform">

<Setter.Value>

<RotateTransform Angle="10"/>

</Setter.Value>

</Setter>

</MultiTrigger>

</Style.Triggers>

Некоторые элементы управления позволяют не просто применить стиль, а выбирать один из стилей в зависимости от определённых условий. Для организации выбора стиля служит класс System.Windows.Controls.StyleSelector. Программисту следует создать наследник этого класса и перекрыть виртуальный метод SelectStyle(). Следующий пример демонстрирует селектор стиля, который может использоваться в списке ListView, чтобы организовать визуальное чередование строк:

publicclassListViewItemStyleSelector: StyleSelector

{

publicoverrideStyle SelectStyle(object item,

DependencyObject container)

{

var st = newStyle {TargetType = typeof (ListViewItem)};

var backGroundSetter =

newSetter {Property = Control.BackgroundProperty};

var listView =

ItemsControl.ItemsControlFromItemContainer(container) asListView;

int index = listView.ItemContainerGenerator.

IndexFromContainer(container);

backGroundSetter.Value = index % 2 == 0?

Brushes.LightBlue:Brushes.Beige;

st.Setters.Add(backGroundSetter);

return st;

}

}

Применение стилей позволяет изменять внешний вид приложения во время его работы. Для этого необходимо выполнить следующие действия:

1. Объявить все стили, образующие требуемый внешний вид, в отдельномXAML-файле словаря ресурсов (ResourceDictionary).

2. Использовать динамические ссылки для связывания элемента управления и стиля из ресурсов:<Label Style="{DynamicResource HeadingStyle}"/>.

3. При помощи кода, подобного следующему фрагменту, по необходимости загружать и подменять ресурсы приложения:

ResourceDictionary resources = null;

using (FileStream fs = newFileStream("Skin.xaml", FileMode.Open))

{

resources = (ResourceDictionary)XamlReader.Load(fs);

}

Application.Current.Resources = resources;

15. ПРИвязка к коллекциям и ШАБЛОНЫ ДАННЫХ

Рассмотрим следующий пример. Пусть имеется класс, описывающий задание для программиста, и классс набором заданий:

publicclassTask

{

publicstring Name { get; set; }

publicstring Note { get; set; }

publicint Priority { get; set; }

publicTaskType Type { get; set; }

}

 

publicenumTaskType { Coding, Testing, Support }

 

publicclassTasks: List<Task>

{

public Tasks()

{

Add(newTask{ Name = "Data loading",

Note = "Need to test work with DB",

Priority = 2, Type = TaskType.Testing });

Add(newTask{ Name = "Log class",

Note = "Finish this work",

Priority = 2, Type = TaskType.Coding });

Add(newTask{ Name = "IoC Usage",

Note = "Find more info",

Priority = 4, Type = TaskType.Coding });

Add(newTask{ Name = "Urgent bug fixing",

Note = "Problem with class C",

Priority = 1, Type = TaskType.Support });

Add(newTask{ Name = "UI development",

Note = "Make markup for Main Window",

Priority = 1, Type = TaskType.Coding });

Add(newTask{ Name = "Help Doc",

Note = "Write technical documentation",

Priority = 3, Type = TaskType.Support });

Add(newTask{ Name = "New project!",

Note = "Plan the meeting",

Priority = 1, Type = TaskType.Support });

}

}

Необходимо создать приложение WPF, отображающее задания. Список заданий будет показан в элементе ListBox, связанном с объектом классаTasks. Класс ItemsControl, являющийся предком всех списковых элементов управления, определяет два свойства, которые будут использоваться при привязке:

· ItemsSource– указывает на коллекцию, содержащую все объекты, которые будут показаны в списке. В свойство ItemsSource можно поместить любой объект, реализующий интерфейс IEnumerable или его универсальную версию.

· DisplayMemberPath–путь к свойству, которое будет применяться для создания отображаемого текста каждого элемента коллекции.

Детальная информация о выбранном задании будет отображаться в наборе текстовых полей. Для этого применяется привязка к свойству DataContext контейнера, обрамляющего поля.

<Window x:Class="TaskView.MainWindow" Title="List of Tasks"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<StackPanel Margin="5" Orientation="Horizontal">

<ListBox Name="lstTasks" DisplayMemberPath="Name"Width="250"/>

<Grid Margin="5" DataContext="{Binding ElementName=lstTasks,

Path=SelectedItem}">

<Grid.ColumnDefinitions>

<ColumnDefinition Width="Auto" />

<ColumnDefinition Width="7" />

<ColumnDefinition Width="200" />

</Grid.ColumnDefinitions>

<Grid.RowDefinitions>

<RowDefinition Height="Auto" />

<RowDefinition Height="11" />

<RowDefinition Height="Auto" />

<RowDefinition Height="11" />

<RowDefinition Height="Auto" />

</Grid.RowDefinitions>

<TextBlock Grid.Row="0" Grid.Column="0" Text="Name:" />

<TextBox Grid.Row="0" Grid.Column="2"

Text="{Binding Name}" />

<TextBlock Grid.Row="2" Grid.Column="0" Text="Note:" />

<TextBox Grid.Row="2" Grid.Column="2"

Text="{Binding Note}" />

<TextBlock Grid.Row="4" Grid.Column="0" Text="Priority:" />

<TextBox Grid.Row="4" Grid.Column="2"

Text="{Binding Priority}" />

</Grid>

</StackPanel>

</Window>

 

// файлCode Behind

namespace TaskView

{

publicpartialclassMainWindow: Window

{

public MainWindow()

{

InitializeComponent();

lstTasks.ItemsSource = newTasks();

}

}

}

Рис. 39.Привязка списку, форма «главная-подробности».

Поддержка, которую даёт интерфейс IEnumerable, ограничена привязкой только для чтения – изменения, производимые в коллекции после привязки, в списковом элементе управления не отображаются. Чтобы включить отслеживание изменений, нужно использовать коллекцию с интерфейсом INotifyCollectionChanged. WPF включает единственную коллекцию, реализующую этот интерфейс, – это класс ObservableCollection<T>.

// изменённыйклассMainWindow

publicpartialclassMainWindow: Window

{

privatereadonlyObservableCollection<Task> tasks;

public MainWindow()

{

InitializeComponent();

tasks = newObservableCollection<Task>(newTasks());

lstTasks.ItemsSource = tasks;

}

}

Изменим внешний вид списка заданий при помощи шаблона данных. Шаблоны данных (datatemplates) – механизм для настройки отображенияобъектов определённого типа. В WPFшаблон данных – это объект класса System.Windows.DataTemplate. Основное свойство шаблона –VisualTree. Оно содержит визуальный элемент, определяющий внешний вид шаблона. Часто этим визуальным элементом является контейнер компоновки. В разметке XAMLдля задания VisualTree достаточно поместить в DataTemplate дочерний элемент. При формировании VisualTree обычно используется привязка данных для извлечения информации из объекта, для которого применяется шаблон. Сам шаблон данных, как правило, размещают в ресурсах окна или приложения.

С учётом вышесказанного определим шаблон данных в ресурсах окна и используем свойство списка ItemTemplateдля применения шаблона к каждому элементу списка:

<!-- определяем шаблон в ресурсах окна -->

<Window.Resources>

<DataTemplate x:Key="taskTemplate">

<Border Name="border" BorderBrush="Aqua" BorderThickness="1"

CornerRadius="2" Padding="5" Margin="5">

<TextBlock FontSize="14" FontWeight="Bold"

Text="{Binding Name}" />

</Border>

</DataTemplate>

</Window.Resources>

 

<!-- изменённаянастройка ListBox -->

<ListBox Name="lstTasks" HorizontalContentAlignment="Stretch"

ItemTemplate="{StaticResource taskTemplate}" Width="250"/>

Рис. 40.Список, к которому применён шаблон данных.

У классаDataTemplate имеется свойство DataType, определяющее тип данных, к которому будет применяться шаблон. Если задано это свойство, WPFбудет использовать шаблон в любой ситуации, где до этого выводилась строка с результатом ToString():

<!-- не указываем ключ ресурса, если шаблон в ресурсах -->

<!--подключено xmlns:TaskView="clr-namespace:TaskView" -->

<DataTemplate DataType="{x:Type TaskView:Task}">

<!-- описание шаблона не изменилось -->

</DataTemplate>

 

<!-- вListBoxнезадаём свойствоItemTemplate -->

<ListBox Name="lstTasks" Width="250" HorizontalContentAlignment="Stretch"/>

В шаблон данных можно поместить триггеры данных. Следующий пример показывает использование триггера для того, чтобы изменить цвет окантовки задач из группы TaskType.Support:

<!-- эта разметка – часть шаблона DataTemplate -->

<DataTemplate.Triggers>

<DataTrigger Binding="{Binding Path=Type}" Value="Support">

<Setter TargetName="border" Property="BorderBrush"

Value="Brown" />

</DataTrigger>

</DataTemplate.Triggers>

Обратитевнимание–элемент<Setter>содержитустановкуTargetName. Как ясно из контекста,TargetName используется, чтобы обратиться к именованному дочернему элементу визуального представления шаблона данных. Дочерний элемент должен быть описан до триггера.

В WPFсписковые элементыуправления поддерживают возможность выбора для объекта одного из нескольких шаблонов данных. Предположим, что в примере со списком заданий требуется особым образом отображать задания с приоритетом, равным 1. Определим в ресурсах окна ещё один шаблон данных:

<DataTemplate x:Key="importantTask">

<Border BorderBrush="Red" BorderThickness="2"CornerRadius="2"

Padding="5" Margin="5">

<TextBlock FontSize="18" Foreground="Red" Text="{Binding Name}"/>

</Border>

</DataTemplate>

Выбор шаблона выполняется в коде, с помощью создания подкласса для DataTemplateSelector и переопределения метода SelectTemplate():

publicclassTaskTemplateSelector: DataTemplateSelector

{

publicoverrideDataTemplate SelectTemplate(object item,

DependencyObject container)

{

if (item!= null&& item isTask)

{

var task = item asTask;

Window window = Application.Current.MainWindow;

return task.Priority == 1?

window.FindResource("importantTask")asDataTemplate:

window.FindResource(newDataTemplateKey(typeof(Task)))

asDataTemplate;

}

returnnull;

}

}

ЗатемможнообъявитьобъектTaskTemplateSelectorвкачествересурсаиназначитьэтотресурссвойствуListBox.ItemTemplateSelector. ОбъектListBoxвызываетметодSelectTemplate()длякаждогоэлементавбазовойколлекции.

<ListBoxName="lstTasks" Width="250"

HorizontalContentAlignment="Stretch"

ItemTemplateSelector="{StaticResource selector}"/>

Рис. 41.Селектор шаблонов в действии.

При работе с иерархическими элементами управления (например, TreeView) вместо шаблона данных на основе DataTemplate следует использовать HiererchicalDataTemplate.У такого шаблона имеется свойство ItemsSource, которое нужно связать с дочерней коллекцией, и свойство ItemTemplate – дочернийшаблон данных (DataTemplate или HiererchicalDataTemplate).

16. представления Данных

Примеры параграфа используют в качестве основы приложение для просмотра списка заданий с простым шаблоном данныхдля типа Task:

<DataTemplate DataType="{x:Type TaskView:Task}">

<Border BorderBrush="Aqua" BorderThickness="1" CornerRadius="2"

Padding="5" Margin="5">

<TextBlock FontSize="14" FontWeight="Bold" Text="{Binding Name}"/>

</Border>

</DataTemplate>

При выполнении привязки коллекции к списковому элементу управления WPFиспользует специальный объект, созданный на основе коллекции и называемый представлением коллекции (collectionview). Представление позволяет фильтровать, сортировать и группировать данные коллекциии даёт возможность навигации по коллекции.

Представления построены на основе интерфейсаICollectionViewиз пространства имён System.ComponentModel. Этот интерфейс унаследован от IEnumerableи INotifyCollectionChanged.Его элементы отписаны в табл. 14.

Таблица 14

Элементы интерфейса ICollectionView

Имя Описание
CanFilter, CanGroup, CanSort Булевы свойства, которые указывают на поддержку представлением фильтрации, группировки и сортировки
Contains() Метод для проверки вхождения элемента в представление
Culture Объект, описывающий региональные стандарты (используется при сортировке представления)
CurrentChanging, CurrentChanged События, генерируемые при изменении позиции текущего элемента
CurrentItem, CurrentPosition Текущий элемент представления и его позиция
Filter Функция фильтрации, описанная как Predicate<object>
GroupDescriptions Коллекция объектов GroupDescription, описывающих условия группировки данных
Groups Коллекция групп, имеющихся в представлении
IsCurrentAfterLast, IsCurrentBeforeFirst Булевы свойства, указывающие на то, что текущий элемент представления вышел за пределы инкапсулируемой коллекции
IsEmpty Свойство равно true, если представление не содержит данных
MoveCurrentTo(), MoveCurrentToFirst(), MoveCurrentToLast(), MoveCurrentToNext(), MoveCurrentToPosition(), MoveCurrentToPrevious() Эти методы предназначены для изменения позиции текущего элемента представления
Refresh() Метод для повторного создания представления по коллекции
SortDescriptions Коллекция объектов SortDescription, описывающих критерии сортировки данных представления
SourceCollection Коллекция, инкапсулируемая представлением

Существуетчетырестандартныхкласса, реализующихICollectionView: CollectionView, ListCollectionView, BindingListCollectionView (все три из пространстваимён System.Windows.Data) и класс ItemCollection (пространство имён System.Windows.Controls). Выбор класса для представления диктуется источником данных:

· Если источник данных реализует интерфейсIBindingList, создаётся объект BindingListCollectionView. Это происходит, в частности, когда источником является ADO.NET-объект DataTable. Представления BindingListCollectionView не поддерживают фильтрацию через свойство Filter, но определяют специальное строковое свойство CustomFilter.

· Если источник данных реализует IList, создаётся объект представления ListCollectionView.

· Если источник данных реализует только интерфейсIEnumerable, создаётся простейший объект представления CollectionView. Представления этого типа не поддерживают группировку.

Реализуем при помощи представлений фильтр для списка задач. В случае, когда списковый элемент управления уже связан с коллекцией данных, получить представление можно из свойстваItemsсписка (тип этого свойства –ItemCollection).Добавимкнопку установки фильтра высокоприоритетных заданий и кнопку сброса фильтра. Обработчики кнопок показаны ниже:

// обработчик для кнопки установки фильтра

privatevoidbtnImportant_Click(objectsender, RoutedEventArgse)

{

// получаем представление у элемента управления lstTasks

ICollectionView view = lstTasks.Items;

// устанавливаемфильтр, используяPredicate<object>

view.Filter = newPredicate<object>(t => ((Task)t).Priority == 1);

}

// обработчикдлякнопкисбросафильтра

privatevoid btnReset_Click(object sender, RoutedEventArgs e)

{

ICollectionView view = lstTasks.Items;

// чтобысброситьфильтр, достаточноприсвоитьемуnull

view.Filter = null;

}

Используем представление, чтобы выполнить сортировку заданий по приоритету. Отдельный критерий сортировки описывается при помощи структуры System.ComponentModel.SortDescription. При этом указывается имя поля сортировки и направление сортировки (по возрастанию или по убыванию). Представление хранит все критерии сортировки в коллекции SortDescriptions. Ниже показан код обработчика для кнопки сортировки:

privatevoid btnSort_Click(object sender, RoutedEventArgs e)

{

ICollectionView view = lstTasks.Items;

view.SortDescriptions.Add(

newSortDescription("Priority", ListSortDirection.Ascending));

}

Заметим, что если используется представление типа ListCollectionView, сортировку можно выполнить при помощи свойства CustomSort, которое принимает объект IComparer.

Представления позволяют группировать данные коллекции. Для этого нужно добавить объектыPropertyGroupDescription (пространство имён System.Windows.Data) в коллекцию GroupDescriptions:

privatevoid btnGroup_Click(object sender, RoutedEventArgs e)

{

ICollectionView view = lstTasks.Items;

view.GroupDescriptions.Add(

newPropertyGroupDescription {PropertyName = "Type"});

}

Когда используется группировка, для каждой группы создаётся отдельный объект GroupItem, и все эти объекты добавляются в список. Сделатьгруппы видимыми можно, отформатировав элемент GroupItem. Класс ItemsControlимеет свойство GroupStyle, которое предоставляет коллекцию объектов GroupStyle[27]. Несмотря на имя, класс GroupStyle стилем не является. Он представляет собой удобный пакет, содержащий несколько полезных параметров для конфигурированияGroupItem:

· ContainerStyle– стиль, который должен применяться к элементу GroupItem;

· ContainerStyleSelector – селекторстилядляGroupItem;

· HeaderTemplate–шаблон для отображения содержимогов начале каждой группы;

· HeaderTemplateSelector – селектор шаблона HeaderTemplate;

· HidesIfEmpty – булево свойство, используемое для сокрытия пустых групп.

Чтобы добавить заголовок для группы, нужно установить свойство GroupStyle.HeaderTemplate. Свойство является обычным шаблоном данных. Если в шаблоне будет использоваться привязка данных, её нужно выполнять относительно предназначенного для группы объекта PropertyGroupDescription:

<ListBox Name="lstTasks" Width="250" HorizontalContentAlignment="Stretch">

<ListBox.GroupStyle>

<GroupStyle>

<GroupStyle.HeaderTemplate>

<DataTemplate>

<TextBlock Text="{Binding Path=Name}"

FontSize="14" FontWeight="Bold"


Дата добавления: 2015-11-14; просмотров: 54 | Нарушение авторских прав


<== предыдущая страница | следующая страница ==>
Проверка данных| Простейшее приложение WPF

mybiblioteka.su - 2015-2024 год. (0.209 сек.)