При загрузке данных из БД возникает исключение InvalidOperationException с сообщением: Элемент коллекции должен быть пустым перед использованием ItemsSource. Знаю, что для заполнения DataGrid можно использовать коллекции Items или ItemsSource, но одновременно их использовать нельзя: если задано значение для свойства ItemsSource и в коде C# добавляется элемент в Items, возникает исключение. |
Модификация клиентского Silverlight приложения
Улучшение пользовательского интерфейса
Разработанное Silverlight-приложение является работоспособным и может использоваться для обработки информации о сотрудниках предприятия. Однако вопросы эффективности интерфейса или взаимодействия с пользователем ( User Experience, UX ) можно улучшить. При разработке пользовательских интерфейсов UX означает общую концепцию их удобства при использовании программного обеспечения, логичность и простоту в расположении элементов управления [ [ 5 ] ].
Для улучшения UX пользовательского интерфейса разработанного Silverlight-приложения поставим следующие задачи:
- улучшить представление элементов списка listBoxEmployees в различных состояниях (не выбранный элемент, выбранный элемент, элемент, на который наведен указатель мыши);
- улучшить представление кнопок в различных состояниях (доступная, недоступная, наведен указатель мыши).
Изменение визуального поведения элемента ListBox
Для элементов пользовательского интерфейса объект ControlTemplate задает визуальную структуру и визуальное поведение элемента управления. Можно настраивать внешний вид элемента управления, предоставляя ему новый шаблон ControlTemplate без изменения его функциональности.
Для нашего приложения ставится задача создания следующего визуального представления и поведения элемента управления ListBox – listBoxEmployees:
- список не должен иметь внешней рамки;
- элементы списка должны представляться в двойной рамке со скругленными углами;
- выбранный элемент списка должен иметь жирное выделение для внешней рамки, например светло-голубым цветом;
- элемент списка, на который наведен указатель мыши, должен иметь жирное выделение для внешней рамки, например светло-фиолетовым цветом
- полоса прокрутки должна находиться справа и отдельно от элементов списка.
Перед изменением представления интерфейсного элемента ListBox целесообразно подготовить объектные ресурсы для задания цветов и кистей рисования. Ресурсы цветов и кистей организуем с помощью словаря ресурсов в файле ColorsAndBrushesRD.xaml (XAML-описание приведено в приложении).
Для реализации требуемого визуального представления и поведения элемента управления ListBox спроектируем три стиля:
- стиль, используемый при визуализации контейнеров элементов ListBox – ListBoxItemStyle;
- стиль, используемый для элемента контроля ListBox - ListBoxPhotoStyle ;
- стиль, используемый для прокручиваемой области, в которой могут содержаться другие видимые элементы – ScrollViewerPhotoStyle.
Кроме того, необходимо выделить в отдельный ресурс шаблон используемый для формирования данных отдельного элемента списка 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, который в XAML-разметке стиля представлен одноименным свойством. Данное свойство позволяет указывать состояния для элемента управления, его внешний вид в определенном состоянии, и порядок изменения состояния элемента управления. Внешний вид элемента управления, находящегося в некотором состоянии, определяется классом VisualState. Данный класс содержит коллекцию объектов Storyboard, указывающих, как изменяется внешний вид элемента управления в определенном состоянии. Состояния просмотра добавляются в элемент управления посредством вложенного свойства зависимостей VisualStateManager.VisualStateGroups элемента управления. Для проектируемого стиля необходимо задать визуальное представление для состояний CommonStates и SelectionStates.
<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, то есть, когда указатель мыши покидает элемент списка, анимация прерывается и внешняя рамка элемента списка принимаем исходный вид. XAML-разметка управления визуальным представлением элемент списка ListBoxItem для группы CommonStates приведена ниже.
<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. XAML-разметка управления визуальным представлением элемент списка ListBoxItem для группы SelectionStates спроектирована аналогично приведенной выше для группы CommonStates и имеет следующий вид.
<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>
Полный код XAML-разметки стиля ListBoxItemStyle приведен в приложении.
Стиль ListBoxPhotoStyle задается для типа ListBox и определяет элемент прокрутки ScrollViewer. Стиль элемент прокрутки задается ссылкой на статический ресурс ScrollViewerPhotoStyle. Полный код XAML-разметки стиля приведен в приложении.
Стиль 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"/>
Полный код XAML-разметки стиля ScrollViewerPhotoStyle приведен в приложении.
Шаблон данных ListBoxItemPhotoDataTemplate практически не отличается от ранее определенного шаблона для элемента listBoxEmployees, за исключением внешней и внутренней рамок. Полный код XAML-разметки шаблона данных ListBoxItemPhotoDataTemplate приведен в приложении.
Стили и шаблон для интерфейсного элемента ListBox поместим в словарь ресурсов StylesAndTemplates.xaml. Так как созданные стили ссылаются на ресурсы из файла ColorsAndBrushesRD.xaml, то данные ресурсы необходимо сделать доступными в файле StylesAndTemplates.xaml с помощью свойства MergedDictionaries словаря ResourceDictionary. Свойство MergedDictionaries представляет различные словари ресурсов в объединенных словарях.
<ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="ColorsAndBrushesRD.xaml"/> </ResourceDictionary.MergedDictionaries>
После создания стилей и шаблон для интерфейсного элемента ListBox необходимо модифицировать XAML-разметку для элемента listBoxEmployees главного окна приложения.
<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.