Освоить основные приемы разработки эффективного интерфейса пользователя с помощью стилей и шаблонов.
В корпоративных базах данных часто возникает необходимость хранения изображений, в частности, фотографий сотрудников. Для хранения фотографий в базе данных SQL Server 2008 можно использовать тип данных больших значений varbinary(max), который обеспечивает хранение до 231-1 байт данных.
Модифицируем базу данных Personal, используемую в лекции 6. Переименуем её, задав имя Personal_Picture, и добавим в таблицу Employee поле Picture типа varbinary(max). Тип данных varbinary(max) обеспечивает хранение двоичных данных в строке.
Модификация серверной части .
(рис 8.1) Модель данных Personal_PictureВ серверной части необходимо построить заново службу WCF Data Services - Person.svc, а на клиенте – обновить ссылку на службу данных.
Для реализации функциональности отображения фотографий на странице приложения необходимо:
В базе данных Personal_Picture данные о фотографии сотрудника хранятся в поле Picture, которое имеет тип varbinary(max), а в модели данных Person представляется массивом байт Byte[]. На странице Silverlight-приложения фотографии могут отображаться с помощью элемента контроля Image, для которого необходимо сформировать экземпляр класса BitmapImage. Таким образом, для реализации
public class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
BitmapImage bi = new BitmapImage();
if (value != null)
{
if (value != null)
bi.SetSource(new MemoryStream((Byte[])value));
return bi;
}
return bi;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException("Отсутствует ConvertBack");
}
}
Метод Convert() осуществляет прямое преобразование массива байт Byte[] в объект класса BitmapImage, то есть изменяет источник данных перед их передачей целевому объекту для отображения в пользовательском интерфейсе. Параметрами метода является:
Возвращаемое значение метода Convert() – object, которое в нашем случае является объектом bi класса BitmapImage.
В методе Convert() вначале создается экземпляр bi класса BitmapImage.
BitmapImage bi = new BitmapImage();
Если входное значение параметра value определено, то задается источник для объекта bi класса BitmapImage на основании потока MemoryStream из массива байт.
if (value != null)
{
if (value != null)
bi.SetSource(new MemoryStream((Byte[])value));
return bi;
}
Обратное преобразование по логике программы не требуется, поэтому метод ConvertBack() определяет только исключение, если к нему будет обращение.
Для отображения фотографии в списке listBoxEmployees необходимо модифицировать шаблон DataTemplate этого списка. Добавим в шаблон сетку с двумя колонками.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
.
</Grid>
В первую колонку поместим интерфейсный элемент Image для размещения фотографии.
<Image Grid.Column="0"
Source="{Binding Path=Picture, Mode=OneWay,
Converter={StaticResource ImageConverter}}"
Height="87" Width="60" Margin="5"
Name="Image"
Stretch="Uniform" />
В качестве источника для интерфейсного элемента Image зададим привязку к полю Picture объекта Employee, режим односторонней привязки (Mode=OneWay), а также конвертор, ссылающийся на статический ресурс. Предварительно необходимо определить пространство имен, где расположен класс конвертора
xmlns:converter="clr-namespace:DataBindingPersonal.Converters"
и затем определить ресурс для основного окна приложения
<UserControl.Resources>
<converter:ImageConverter x:Key="ImageConverter" />
</UserControl.Resources>
Во вторую колонку сетки поместим текстовые поля с данными по сотруднику.
<StackPanel Grid.Column="1" Orientation="Vertical">
<TextBlock Text="{Binding Path=EmployeeSurname}" Margin="5,5,5,5" />
<TextBlock Text="{Binding Path=EmployeeName}" Margin="5" />
<TextBlock Text="{Binding Path=EmployeePatronymic}" Margin="5" />
</StackPanel>
Модифицированная
<ListBox Grid.Row="1" Name="listBoxEmployees" HorizontalAlignment="Center"
Margin="11,2,29,9" Padding="3" Width="300" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding Path=Picture, Mode=OneWay,
Converter={StaticResource ImageConverter}}"
Height="87" Width="60" Margin="5"
Name="Image" Stretch="Uniform" />
<StackPanel Grid.Column="1" Orientation="Vertical">
<TextBlock Text="{Binding Path=EmployeeSurname}" Margin="5,5,5,5" />
<TextBlock Text="{Binding Path=EmployeeName}" Margin="5" />
<TextBlock Text="{Binding Path=EmployeePatronymic}" Margin="5" />
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Для отображения фотографии в детальных данных по сотруднику добавим в StackPanel интерфейсный элемент Image.
<Image Name="ImageEmployee"
Source="{Binding Path=Picture, Mode=OneWay, Converter={StaticResource ImageConverter}}"
Margin="5" Width="210" Height="225" Stretch="UniformToFill" />
Задание источника данных для элемента ImageEmployee аналогично тому, как это было сделано для списка listBoxEmployees.
Реализация функциональности изменения фотографии сотрудника осуществлена введением дополнительной кнопки и обработчика события Click - ChangeFoto_Click.
<Button Content="Изменить фото"
Margin="5,100,5,5" Height="23"
Name="ButtonChangeFoto" Width="117"
Click="ChangeFoto_Click"
IsEnabled="False" />
В обработчике события ChangeFoto_Click используется экземпляр класса диалогового окна OpenFileDialog, с помощью которого осуществляется считывание графического (*.jpg) файла и формируется поток байтов.
Stream stream = (Stream)openFileDialog.File.OpenRead();
На основе потока stream формируется массив байтов.
byte[] bytes = new byte[stream.Length]; stream.Read(bytes, 0, (int)stream.Length);
Полученный массив байтов присваивается выделенному элементу списка listBoxEmployees.
(listBoxEmployees.SelectedItem as Employee).Picture = bytes;
Полный код обработчика ChangeFoto_Click приведен ниже.
private void ChangeFoto_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog { Filter = "JPEG files|*.jpg" };
if (openFileDialog.ShowDialog() == true)
{
using (Stream stream = (Stream)openFileDialog.File.OpenRead())
{
byte[] bytes = new byte[stream.Length];
stream.Read(bytes, 0, (int)stream.Length);
(listBoxEmployees.SelectedItem as Employee).Picture = bytes;
}
}
}
Модифицированная страница приложения приведена на рис 8.2.
(рис 8.2) Страница приложения в дизайнереСозданные ранее методы (см. лекцию 6), обеспечивающие функциональность приложения (сохранить данные – Save_Click, отмерить редактирование – Undo_Click, создать новую запись – New_Click, загрузить данные из базы – Open_Click, редактировать данные по сотруднику – Edit_Click, удалить данные по сотруднику – Delete_Click ) не требуют модификации.
После компиляции и запуска страница приложения имеет вид, приведенный на рис 8.3.
(рис 8.3) Страница приложенияРазработанное Silverlight-приложение является работоспособным и может использоваться для обработки информации о сотрудниках предприятия. Однако вопросы эффективности интерфейса или взаимодействия с пользователем ( ].
Для улучшения UX пользовательского интерфейса разработанного Silverlight-приложения поставим следующие задачи:
Для элементов пользовательского
Для нашего приложения ставится задача создания следующего визуального представления и поведения элемента управления ListBox – listBoxEmployees:
Перед изменением представления интерфейсного элемента ListBox целесообразно подготовить объектные ресурсы для задания цветов и кистей рисования. Ресурсы цветов и кистей организуем с помощью словаря ресурсов в файле ColorsAndBrushesRD.
Для реализации требуемого визуального представления и поведения элемента управления ListBox спроектируем три стиля:
Кроме того, необходимо выделить в отдельный ресурс шаблон используемый для формирования данных отдельного элемента списка ListBox – ListBoxItemPhotoDataTemplate.
Стиль ListBoxItemStyle создается для типа ListBoxItem (TargetType="ListBoxItem") и в нем определяются четыре свойства: Padding, VerticalAlignment, HorizontalAlignment и Template.
Style x:Key="ListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Padding" Value="1"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Template">
<Setter.Value>
. . .
<!—Определение вложенного свойства ControlTemplate -->
. . .
</Setter.Value>
</Setter>
</Style>
Основная задача при формировании визуального представления и поведения элемента управления ListBoxItem сводится к заданию вложенного свойства ControlTemplate. Это определяется тем, что визуальная структура и визуальное поведение интерфейсного элемента задается в его шаблоне ControlTemplate.
<ControlTemplate TargetType="ListBoxItem">
<Border
Name="Border"
Padding="5"
Width="200" Height="126"
CornerRadius="5"
Background="{StaticResource ListBoxItemPhotoActiveBGSolidBrush}">
<!—Определение визуального поведения в различных состояниях -->. . .
<Border
BorderBrush="{StaticResource PhotoSelectedBGSolidBrush}"
BorderThickness="1"
CornerRadius="5">
<ContentPresenter x:Name="contentPresenter" />
</Border>
</Border>
</ControlTemplate>
Шаблон ControlTemplate содержит две рамки. Внешняя рамка имеет имя Border, а внутренняя - задана без имени. Свойства заливки фона Background, для внешней рамки, и контура BorderBrush, для внутренней рамки, заданы в расширенной разметке ссылкой на статические ресурсы, определенные в файле ColorsAndBrushesRD.xaml.
Свойство ContentPresenter внутренней рамки интерфейсного элемента ListBoxItem определяет где должно отображаться содержание ( Content ) данного элемента.
Визуальное поведение задает способ отображения элемента управления в определенных состояниях. Для элемента управления ListBoxItem имеются следующие состояния, которые приведены в табл.7.1
| VisualState | VisualStateGroup | Описание |
|---|---|---|
| Normal | CommonStates | Состояние по умолчанию. |
| MouseOver | CommonStates | Указатель мыши расположен в элементе управления. |
| Disabled | CommonStates | Элемент отключен. |
| Focused | FocusStates | На элементе установлен фокус. |
| Unfocused | FocusStates | Элемент не имеет фокус. |
| Selected | SelectionStates | Элемент выбран. |
| Unselected | SelectionStates | Элемент не выбран. |
| SelectedUnfocused | SelectionStates | Элемент выбран, но не имеет фокус. Это состояние отсутствует в шаблоне элемента управления ListBoxItem по умолчанию. Однако можно изменить ControlTemplate объекта ListBoxItem для использования этого состояния. |
| BeforeLoaded | LayoutStates | ListBoxItem еще не загружен. |
| AfterLoaded | LayoutStates | ListBoxItem загружен. |
| BeforeUnloaded | LayoutStates | ListBoxItem еще не выгружен. |
Для
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
. . .
<!—Описание состояний и поведения в этих состояниях -->. . .
. . .
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
. . .
<!—Описание состояний и поведения в этих состояниях -->. . .
. . .
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
Каждый объект VisualStateGroup содержит коллекцию взаимоисключающих объектов VisualState. Элемент управления всегда находится в точности одном состоянии в каждой группе VisualStateGroup.
Для группы CommonStates определим функциональность для состояний Normal и MouseOver. При наведении указателя мыши на элемент списка ListBoxItem (состояние MouseOver ) свойство ColorAnimation определяет процесс анимации значения свойства Background (Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)") для объекта Border (Storyboard.TargetName="Border") от исходного значения к целевому (To="{StaticResource PhotoSelectedBGColor}") в указанном интервале Duration (Duration="0:0:0").
При переходе в состояние Normal, то есть, когда указатель мыши покидает элемент списка, анимация прерывается и внешняя рамка элемента списка принимаем исходный вид.
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Duration="0:0:0"
To="{StaticResource PhotoSelectedBGColor}"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
Storyboard.TargetName="Border" />
</Storyboard>
</VisualState>
Для группы SelectionStates определим функциональность для состояний Unselected и Selected.
<VisualState x:Name="Unselected" />
<VisualState x:Name="Selected">
<Storyboard>
<ColorAnimation Duration="0:0:0"
To="{StaticResource ItemSelectedBGColor}"
Storyboard.TargetProperty= (Border.Background).(SolidColorBrush.Color)"
Storyboard.TargetName="Border" />
</Storyboard>
</VisualState>
Полный код
Стиль ListBoxPhotoStyle задается для типа ListBox и определяет элемент прокрутки ScrollViewer. Стиль элемент прокрутки задается ссылкой на статический ресурс ScrollViewerPhotoStyle. Полный код
Стиль ScrollViewerPhotoStyle задается для типа ScrollViewer (TargetType="ScrollViewer") и в нем определяются четыре свойства: HorizontalScrollBarVisibility, VerticalScrollBarVisibility, BorderThickness и Template.
<Style x:Key="ScrollViewerPhotoStyle" TargetType="ScrollViewer">
<Setter Property="HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<!—Определение вложенного свойства ControlTemplate -->
</Setter.Value>
</Setter>
</Style>
Шаблон ControlTemplate содержит два контейнера Grid. Для внешней сетки (Grid) задается свойство ScrollContentPresenter, определяющее где и как будет отображаться содержимое элемента управления ScrollViewer. Вложенная сетка имеет две колонки.
<ControlTemplate TargetType="ScrollViewer">
<Grid>
<ScrollContentPresenter
x:Name="ScrollContentPresenter"
Cursor="{TemplateBinding Cursor}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Grid.Column="1" Grid.Row="1"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!—Определение свойства ScrollBar -->
</Grid>
</Grid>
</ControlTemplate>
Элемент управления ScrollBar предоставляет полосу прокрутки с перемещаемым элементом, позиция которого соответствует значению.
<ScrollBar
x:Name="VerticalScrollBar"
Grid.Row="0" Grid.Column="1"
IsTabStop="False"
Maximum="{TemplateBinding ScrollableHeight}"
Height="250" Minimum="0"
Orientation="Vertical"
VerticalAlignment="Center"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
Value="{TemplateBinding VerticalOffset}"
ViewportSize="{TemplateBinding ViewportHeight}"
Width="18" Margin="10" HorizontalAlignment="Center"
Opacity="0.5"/>
Полный код
Шаблон данных ListBoxItemPhotoDataTemplate практически не отличается от ранее определенного шаблона для элемента listBoxEmployees, за исключением внешней и внутренней рамок. Полный код
Стили и шаблон для интерфейсного элемента ListBox поместим в словарь ресурсов StylesAndTemplates.xaml. Так как созданные стили ссылаются на ресурсы из файла ColorsAndBrushesRD.xaml, то данные ресурсы необходимо сделать доступными в файле StylesAndTemplates.xaml с помощью свойства MergedDictionaries словаря ResourceDictionary. Свойство MergedDictionaries представляет различные словари ресурсов в объединенных словарях.
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ColorsAndBrushesRD.xaml"/>
</ResourceDictionary.MergedDictionaries>
После создания стилей и шаблон для интерфейсного элемента ListBox необходимо модифицировать
<ListBox Grid.Row="1" Name="listBoxEmployees" HorizontalAlignment="Center"
Margin="45,2,76,9" Padding="3" Width="263"
ItemContainerStyle="{StaticResource ListBoxItemStyle}"
Style="{StaticResource ListBoxPhotoStyle}"
ItemTemplate="{StaticResource ListBoxItemPhotoDataTemplate}"
Background="{x:Null}" />
После компиляции и запуска страница приложения имеет вид, приведенный на рис 8.4.
(рис 8.4) Страница приложения с модифицированным интерфейсным элементом списка
В разрабатываемом приложении для визуального представления и поведения всех кнопок – элементов управления Button целесообразно изменить рамки кнопок, сделав их более четкими в состоянии доступности, а также изменить рамки в состоянии, когда на кнопку наведен указатель мыши.
Стиль ButtonStyle создается для типа Button (TargetType="Button") и в нем определяются шесть свойств: Background, Foreground, Padding, BorderThickness, BorderBrush и Template. Для свойств Background, Foreground, Padding и BorderThickness задаются конкретные значения, а для свойства BorderBrush определяется кисть градиентной заливки. Полный код
Вложенное свойство ControlTemplate определено для типа Button (TargetType="Button"). Оно содержит контейнер Grid, для которого задается свойства Cursor (Cursor="Hand"). Свойство Cursor будет определять изменение вида курсора при наведении указателя мыши на кнопку. Вложенное свойство зависимостей VisualStateManager.VisualStateGroups элемента управления определяет визуальное представление элемента управления в различных состояниях. Рамка ( Border ) с именем Background определяет визуальное представление, которое будет изменяеться в различных состояниях кнопки.
Свойство ContentPresenter определяет, что и как будет формироваться на панели кнопки. Три прямоугольника используются для представления внутреннего содержания кнопки в режимах получения фокуса ( FocusVisualElement ), наведения указателя мыши ( MouseOverVisualElement ) и в недоступном состоянии ( DisabledVisualElement ). Полный код
Для элемента управления Button имеются следующие состояния, которые приведены в табл.7.2
| Имя VisualState | Имя VisualStateGroup | Описание |
|---|---|---|
| Normal | CommonStates | Состояние по умолчанию. |
| MouseOver | CommonStates | Указатель мыши расположен в элементе управления. |
| Pressed | CommonStates | Элемент управления нажат. |
| Disabled | CommonStates | Элемент управления отключен. |
| Focused | FocusStates | Элемент управления имеет фокус. |
| Unfocused | FocusStates | Элемент управления не имеет фокуса. |
Для проектируемого стиля необходимо задать визуальное представление для состояний CommonStates и FocusStates.
Для группы состояний CommonStates в состоянии Normal применяется визуальное представление по умолчанию, а для состояний MouseOver, Pressed и Disabled задается как цветовая ( ColorAnimation ), так числовая ( DoubleAnimation ) анимация свойств кнопки.
Для группы состояний FocusStates в состоянии Unfocused применяется визуальное представление по умолчанию, а для состояния Focused задается как цветовая анимация свойств кнопки – изменяется прозрачность.
Полный код
Стиль для интерфейсного элемента Buttun добавим в словарь ресурсов StylesAndTemplates.xaml. далее необходимо модифицировать
<Button Name="ButtonNew"
Content="Создать"
Margin="5"
Padding="10,5,10,5"
Height="25"
Click="New_Click"
ToolTipService.ToolTip="Создать новую запись"
IsEnabled="False"
Style="{StaticResource ButtonStyle}" />
Визуальное представление кнопки на основе спроектированного стиля
(рис 8.5) Визуальное представление кнопки в различных состоянияхПосле компиляции и запуска страница приложения имеет вид, приведенный на рис 8.6.
(рис 8.6) Страница приложения с модифицированным интерфейсным элементом кнопки
Конвертор данных, шаблон данных, стиль, объектный ресурс, пользовательский интерфейс, элемент пользовательского интерфейса, словарь ресурсов, визуальное состояние класса,
IValueConverter, Convert, ConvertBack, DataTemplate, OpenFileDialog, stream, ControlTemplate,
Рассмотрены подходы по включению в элементы управления Silverlight- приложения графических объектов, построение сложных стилей и шаблонов для списков и кнопок с целью повышения качества интерфейса пользователя.
Освоить основные приемы разработки эффективного интерфейса пользователя с помощью стилей и шаблонов.
В корпоративных базах данных часто возникает необходимость хранения изображений, в частности, фотографий сотрудников. Для хранения фотографий в базе данных SQL Server 2008 можно использовать тип данных больших значений varbinary(max), который обеспечивает хранение до 231-1 байт данных.
Модифицируем базу данных Personal, используемую в лекции 6. Переименуем её, задав имя Personal_Picture, и добавим в таблицу Employee поле Picture типа varbinary(max). Тип данных varbinary(max) обеспечивает хранение двоичных данных в строке.
Модификация серверной части .
(рис 8.1) Модель данных Personal_PictureВ серверной части необходимо построить заново службу WCF Data Services - Person.svc, а на клиенте – обновить ссылку на службу данных.
Для реализации функциональности отображения фотографий на странице приложения необходимо:
В базе данных Personal_Picture данные о фотографии сотрудника хранятся в поле Picture, которое имеет тип varbinary(max), а в модели данных Person представляется массивом байт Byte[]. На странице Silverlight-приложения фотографии могут отображаться с помощью элемента контроля Image, для которого необходимо сформировать экземпляр класса BitmapImage. Таким образом, для реализации
public class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
BitmapImage bi = new BitmapImage();
if (value != null)
{
if (value != null)
bi.SetSource(new MemoryStream((Byte[])value));
return bi;
}
return bi;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException("Отсутствует ConvertBack");
}
}
Метод Convert() осуществляет прямое преобразование массива байт Byte[] в объект класса BitmapImage, то есть изменяет источник данных перед их передачей целевому объекту для отображения в пользовательском интерфейсе. Параметрами метода является:
Возвращаемое значение метода Convert() – object, которое в нашем случае является объектом bi класса BitmapImage.
В методе Convert() вначале создается экземпляр bi класса BitmapImage.
BitmapImage bi = new BitmapImage();
Если входное значение параметра value определено, то задается источник для объекта bi класса BitmapImage на основании потока MemoryStream из массива байт.
if (value != null)
{
if (value != null)
bi.SetSource(new MemoryStream((Byte[])value));
return bi;
}
Обратное преобразование по логике программы не требуется, поэтому метод ConvertBack() определяет только исключение, если к нему будет обращение.
Для отображения фотографии в списке listBoxEmployees необходимо модифицировать шаблон DataTemplate этого списка. Добавим в шаблон сетку с двумя колонками.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
.
</Grid>
В первую колонку поместим интерфейсный элемент Image для размещения фотографии.
<Image Grid.Column="0"
Source="{Binding Path=Picture, Mode=OneWay,
Converter={StaticResource ImageConverter}}"
Height="87" Width="60" Margin="5"
Name="Image"
Stretch="Uniform" />
В качестве источника для интерфейсного элемента Image зададим привязку к полю Picture объекта Employee, режим односторонней привязки (Mode=OneWay), а также конвертор, ссылающийся на статический ресурс. Предварительно необходимо определить пространство имен, где расположен класс конвертора
xmlns:converter="clr-namespace:DataBindingPersonal.Converters"
и затем определить ресурс для основного окна приложения
<UserControl.Resources>
<converter:ImageConverter x:Key="ImageConverter" />
</UserControl.Resources>
Во вторую колонку сетки поместим текстовые поля с данными по сотруднику.
<StackPanel Grid.Column="1" Orientation="Vertical">
<TextBlock Text="{Binding Path=EmployeeSurname}" Margin="5,5,5,5" />
<TextBlock Text="{Binding Path=EmployeeName}" Margin="5" />
<TextBlock Text="{Binding Path=EmployeePatronymic}" Margin="5" />
</StackPanel>
Модифицированная
<ListBox Grid.Row="1" Name="listBoxEmployees" HorizontalAlignment="Center"
Margin="11,2,29,9" Padding="3" Width="300" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding Path=Picture, Mode=OneWay,
Converter={StaticResource ImageConverter}}"
Height="87" Width="60" Margin="5"
Name="Image" Stretch="Uniform" />
<StackPanel Grid.Column="1" Orientation="Vertical">
<TextBlock Text="{Binding Path=EmployeeSurname}" Margin="5,5,5,5" />
<TextBlock Text="{Binding Path=EmployeeName}" Margin="5" />
<TextBlock Text="{Binding Path=EmployeePatronymic}" Margin="5" />
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Для отображения фотографии в детальных данных по сотруднику добавим в StackPanel интерфейсный элемент Image.
<Image Name="ImageEmployee"
Source="{Binding Path=Picture, Mode=OneWay, Converter={StaticResource ImageConverter}}"
Margin="5" Width="210" Height="225" Stretch="UniformToFill" />
Задание источника данных для элемента ImageEmployee аналогично тому, как это было сделано для списка listBoxEmployees.
Реализация функциональности изменения фотографии сотрудника осуществлена введением дополнительной кнопки и обработчика события Click - ChangeFoto_Click.
<Button Content="Изменить фото"
Margin="5,100,5,5" Height="23"
Name="ButtonChangeFoto" Width="117"
Click="ChangeFoto_Click"
IsEnabled="False" />
В обработчике события ChangeFoto_Click используется экземпляр класса диалогового окна OpenFileDialog, с помощью которого осуществляется считывание графического (*.jpg) файла и формируется поток байтов.
Stream stream = (Stream)openFileDialog.File.OpenRead();
На основе потока stream формируется массив байтов.
byte[] bytes = new byte[stream.Length]; stream.Read(bytes, 0, (int)stream.Length);
Полученный массив байтов присваивается выделенному элементу списка listBoxEmployees.
(listBoxEmployees.SelectedItem as Employee).Picture = bytes;
Полный код обработчика ChangeFoto_Click приведен ниже.
private void ChangeFoto_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog { Filter = "JPEG files|*.jpg" };
if (openFileDialog.ShowDialog() == true)
{
using (Stream stream = (Stream)openFileDialog.File.OpenRead())
{
byte[] bytes = new byte[stream.Length];
stream.Read(bytes, 0, (int)stream.Length);
(listBoxEmployees.SelectedItem as Employee).Picture = bytes;
}
}
}
Модифицированная страница приложения приведена на рис 8.2.
(рис 8.2) Страница приложения в дизайнереСозданные ранее методы (см. лекцию 6), обеспечивающие функциональность приложения (сохранить данные – Save_Click, отмерить редактирование – Undo_Click, создать новую запись – New_Click, загрузить данные из базы – Open_Click, редактировать данные по сотруднику – Edit_Click, удалить данные по сотруднику – Delete_Click ) не требуют модификации.
После компиляции и запуска страница приложения имеет вид, приведенный на рис 8.3.
(рис 8.3) Страница приложенияРазработанное Silverlight-приложение является работоспособным и может использоваться для обработки информации о сотрудниках предприятия. Однако вопросы эффективности интерфейса или взаимодействия с пользователем ( ].
Для улучшения UX пользовательского интерфейса разработанного Silverlight-приложения поставим следующие задачи:
Для элементов пользовательского
Для нашего приложения ставится задача создания следующего визуального представления и поведения элемента управления ListBox – listBoxEmployees:
Перед изменением представления интерфейсного элемента ListBox целесообразно подготовить объектные ресурсы для задания цветов и кистей рисования. Ресурсы цветов и кистей организуем с помощью словаря ресурсов в файле ColorsAndBrushesRD.
Для реализации требуемого визуального представления и поведения элемента управления ListBox спроектируем три стиля:
Кроме того, необходимо выделить в отдельный ресурс шаблон используемый для формирования данных отдельного элемента списка ListBox – ListBoxItemPhotoDataTemplate.
Стиль ListBoxItemStyle создается для типа ListBoxItem (TargetType="ListBoxItem") и в нем определяются четыре свойства: Padding, VerticalAlignment, HorizontalAlignment и Template.
Style x:Key="ListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Padding" Value="1"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Template">
<Setter.Value>
. . .
<!—Определение вложенного свойства ControlTemplate -->
. . .
</Setter.Value>
</Setter>
</Style>
Основная задача при формировании визуального представления и поведения элемента управления ListBoxItem сводится к заданию вложенного свойства ControlTemplate. Это определяется тем, что визуальная структура и визуальное поведение интерфейсного элемента задается в его шаблоне ControlTemplate.
<ControlTemplate TargetType="ListBoxItem">
<Border
Name="Border"
Padding="5"
Width="200" Height="126"
CornerRadius="5"
Background="{StaticResource ListBoxItemPhotoActiveBGSolidBrush}">
<!—Определение визуального поведения в различных состояниях -->. . .
<Border
BorderBrush="{StaticResource PhotoSelectedBGSolidBrush}"
BorderThickness="1"
CornerRadius="5">
<ContentPresenter x:Name="contentPresenter" />
</Border>
</Border>
</ControlTemplate>
Шаблон ControlTemplate содержит две рамки. Внешняя рамка имеет имя Border, а внутренняя - задана без имени. Свойства заливки фона Background, для внешней рамки, и контура BorderBrush, для внутренней рамки, заданы в расширенной разметке ссылкой на статические ресурсы, определенные в файле ColorsAndBrushesRD.xaml.
Свойство ContentPresenter внутренней рамки интерфейсного элемента ListBoxItem определяет где должно отображаться содержание ( Content ) данного элемента.
Визуальное поведение задает способ отображения элемента управления в определенных состояниях. Для элемента управления ListBoxItem имеются следующие состояния, которые приведены в табл.7.1
| VisualState | VisualStateGroup | Описание |
|---|---|---|
| Normal | CommonStates | Состояние по умолчанию. |
| MouseOver | CommonStates | Указатель мыши расположен в элементе управления. |
| Disabled | CommonStates | Элемент отключен. |
| Focused | FocusStates | На элементе установлен фокус. |
| Unfocused | FocusStates | Элемент не имеет фокус. |
| Selected | SelectionStates | Элемент выбран. |
| Unselected | SelectionStates | Элемент не выбран. |
| SelectedUnfocused | SelectionStates | Элемент выбран, но не имеет фокус. Это состояние отсутствует в шаблоне элемента управления ListBoxItem по умолчанию. Однако можно изменить ControlTemplate объекта ListBoxItem для использования этого состояния. |
| BeforeLoaded | LayoutStates | ListBoxItem еще не загружен. |
| AfterLoaded | LayoutStates | ListBoxItem загружен. |
| BeforeUnloaded | LayoutStates | ListBoxItem еще не выгружен. |
Для
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
. . .
<!—Описание состояний и поведения в этих состояниях -->. . .
. . .
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
. . .
<!—Описание состояний и поведения в этих состояниях -->. . .
. . .
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
Каждый объект VisualStateGroup содержит коллекцию взаимоисключающих объектов VisualState. Элемент управления всегда находится в точности одном состоянии в каждой группе VisualStateGroup.
Для группы CommonStates определим функциональность для состояний Normal и MouseOver. При наведении указателя мыши на элемент списка ListBoxItem (состояние MouseOver ) свойство ColorAnimation определяет процесс анимации значения свойства Background (Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)") для объекта Border (Storyboard.TargetName="Border") от исходного значения к целевому (To="{StaticResource PhotoSelectedBGColor}") в указанном интервале Duration (Duration="0:0:0").
При переходе в состояние Normal, то есть, когда указатель мыши покидает элемент списка, анимация прерывается и внешняя рамка элемента списка принимаем исходный вид.
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Duration="0:0:0"
To="{StaticResource PhotoSelectedBGColor}"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
Storyboard.TargetName="Border" />
</Storyboard>
</VisualState>
Для группы SelectionStates определим функциональность для состояний Unselected и Selected.
<VisualState x:Name="Unselected" />
<VisualState x:Name="Selected">
<Storyboard>
<ColorAnimation Duration="0:0:0"
To="{StaticResource ItemSelectedBGColor}"
Storyboard.TargetProperty= (Border.Background).(SolidColorBrush.Color)"
Storyboard.TargetName="Border" />
</Storyboard>
</VisualState>
Полный код
Стиль ListBoxPhotoStyle задается для типа ListBox и определяет элемент прокрутки ScrollViewer. Стиль элемент прокрутки задается ссылкой на статический ресурс ScrollViewerPhotoStyle. Полный код
Стиль ScrollViewerPhotoStyle задается для типа ScrollViewer (TargetType="ScrollViewer") и в нем определяются четыре свойства: HorizontalScrollBarVisibility, VerticalScrollBarVisibility, BorderThickness и Template.
<Style x:Key="ScrollViewerPhotoStyle" TargetType="ScrollViewer">
<Setter Property="HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<!—Определение вложенного свойства ControlTemplate -->
</Setter.Value>
</Setter>
</Style>
Шаблон ControlTemplate содержит два контейнера Grid. Для внешней сетки (Grid) задается свойство ScrollContentPresenter, определяющее где и как будет отображаться содержимое элемента управления ScrollViewer. Вложенная сетка имеет две колонки.
<ControlTemplate TargetType="ScrollViewer">
<Grid>
<ScrollContentPresenter
x:Name="ScrollContentPresenter"
Cursor="{TemplateBinding Cursor}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Grid.Column="1" Grid.Row="1"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!—Определение свойства ScrollBar -->
</Grid>
</Grid>
</ControlTemplate>
Элемент управления ScrollBar предоставляет полосу прокрутки с перемещаемым элементом, позиция которого соответствует значению.
<ScrollBar
x:Name="VerticalScrollBar"
Grid.Row="0" Grid.Column="1"
IsTabStop="False"
Maximum="{TemplateBinding ScrollableHeight}"
Height="250" Minimum="0"
Orientation="Vertical"
VerticalAlignment="Center"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
Value="{TemplateBinding VerticalOffset}"
ViewportSize="{TemplateBinding ViewportHeight}"
Width="18" Margin="10" HorizontalAlignment="Center"
Opacity="0.5"/>
Полный код
Шаблон данных ListBoxItemPhotoDataTemplate практически не отличается от ранее определенного шаблона для элемента listBoxEmployees, за исключением внешней и внутренней рамок. Полный код
Стили и шаблон для интерфейсного элемента ListBox поместим в словарь ресурсов StylesAndTemplates.xaml. Так как созданные стили ссылаются на ресурсы из файла ColorsAndBrushesRD.xaml, то данные ресурсы необходимо сделать доступными в файле StylesAndTemplates.xaml с помощью свойства MergedDictionaries словаря ResourceDictionary. Свойство MergedDictionaries представляет различные словари ресурсов в объединенных словарях.
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ColorsAndBrushesRD.xaml"/>
</ResourceDictionary.MergedDictionaries>
После создания стилей и шаблон для интерфейсного элемента ListBox необходимо модифицировать
<ListBox Grid.Row="1" Name="listBoxEmployees" HorizontalAlignment="Center"
Margin="45,2,76,9" Padding="3" Width="263"
ItemContainerStyle="{StaticResource ListBoxItemStyle}"
Style="{StaticResource ListBoxPhotoStyle}"
ItemTemplate="{StaticResource ListBoxItemPhotoDataTemplate}"
Background="{x:Null}" />
После компиляции и запуска страница приложения имеет вид, приведенный на рис 8.4.
(рис 8.4) Страница приложения с модифицированным интерфейсным элементом списка
В разрабатываемом приложении для визуального представления и поведения всех кнопок – элементов управления Button целесообразно изменить рамки кнопок, сделав их более четкими в состоянии доступности, а также изменить рамки в состоянии, когда на кнопку наведен указатель мыши.
Стиль ButtonStyle создается для типа Button (TargetType="Button") и в нем определяются шесть свойств: Background, Foreground, Padding, BorderThickness, BorderBrush и Template. Для свойств Background, Foreground, Padding и BorderThickness задаются конкретные значения, а для свойства BorderBrush определяется кисть градиентной заливки. Полный код
Вложенное свойство ControlTemplate определено для типа Button (TargetType="Button"). Оно содержит контейнер Grid, для которого задается свойства Cursor (Cursor="Hand"). Свойство Cursor будет определять изменение вида курсора при наведении указателя мыши на кнопку. Вложенное свойство зависимостей VisualStateManager.VisualStateGroups элемента управления определяет визуальное представление элемента управления в различных состояниях. Рамка ( Border ) с именем Background определяет визуальное представление, которое будет изменяеться в различных состояниях кнопки.
Свойство ContentPresenter определяет, что и как будет формироваться на панели кнопки. Три прямоугольника используются для представления внутреннего содержания кнопки в режимах получения фокуса ( FocusVisualElement ), наведения указателя мыши ( MouseOverVisualElement ) и в недоступном состоянии ( DisabledVisualElement ). Полный код
Для элемента управления Button имеются следующие состояния, которые приведены в табл.7.2
| Имя VisualState | Имя VisualStateGroup | Описание |
|---|---|---|
| Normal | CommonStates | Состояние по умолчанию. |
| MouseOver | CommonStates | Указатель мыши расположен в элементе управления. |
| Pressed | CommonStates | Элемент управления нажат. |
| Disabled | CommonStates | Элемент управления отключен. |
| Focused | FocusStates | Элемент управления имеет фокус. |
| Unfocused | FocusStates | Элемент управления не имеет фокуса. |
Для проектируемого стиля необходимо задать визуальное представление для состояний CommonStates и FocusStates.
Для группы состояний CommonStates в состоянии Normal применяется визуальное представление по умолчанию, а для состояний MouseOver, Pressed и Disabled задается как цветовая ( ColorAnimation ), так числовая ( DoubleAnimation ) анимация свойств кнопки.
Для группы состояний FocusStates в состоянии Unfocused применяется визуальное представление по умолчанию, а для состояния Focused задается как цветовая анимация свойств кнопки – изменяется прозрачность.
Полный код
Стиль для интерфейсного элемента Buttun добавим в словарь ресурсов StylesAndTemplates.xaml. далее необходимо модифицировать
<Button Name="ButtonNew"
Content="Создать"
Margin="5"
Padding="10,5,10,5"
Height="25"
Click="New_Click"
ToolTipService.ToolTip="Создать новую запись"
IsEnabled="False"
Style="{StaticResource ButtonStyle}" />
Визуальное представление кнопки на основе спроектированного стиля
(рис 8.5) Визуальное представление кнопки в различных состоянияхПосле компиляции и запуска страница приложения имеет вид, приведенный на рис 8.6.
(рис 8.6) Страница приложения с модифицированным интерфейсным элементом кнопки
Конвертор данных, шаблон данных, стиль, объектный ресурс, пользовательский интерфейс, элемент пользовательского интерфейса, словарь ресурсов, визуальное состояние класса,
IValueConverter, Convert, ConvertBack, DataTemplate, OpenFileDialog, stream, ControlTemplate,
Рассмотрены подходы по включению в элементы управления Silverlight- приложения графических объектов, построение сложных стилей и шаблонов для списков и кнопок с целью повышения качества интерфейса пользователя.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.