Опубликован: 08.07.2011 | Уровень: для всех | Доступ: платный
Лекция 7:

Модификация клиентского 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

Таблица 8.1. Визуальные состояния класса ListBoxItem
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.

Страница приложения с модифицированным интерфейсным элементом списка

увеличить изображение
Рис. 8.4. Страница приложения с модифицированным интерфейсным элементом списка
Александр Петров
Александр Петров

При загрузке данных из БД возникает исключение InvalidOperationException с сообщением: Элемент коллекции должен быть пустым перед использованием ItemsSource. Знаю, что для заполнения DataGrid можно использовать коллекции Items или ItemsSource, но одновременно их использовать нельзя: если задано значение для свойства ItemsSource и в коде C# добавляется элемент в Items, возникает исключение. 
Вопрос, как отследить и отключить добавление элемента в Items?

Максим Спиридонов
Максим Спиридонов

В пятой лекции на второй странице в компиляторе выскакивает ошибка в строчке :

ObjectQuery<Employee> employees = DataEntitiesEmployee.Employees;

Ошибка CS0029

Не удается неявно преобразовать тип "System.Data.Entity.DbSet<WpfApplProject.Employee>" в "System.Data.Entity.Core.Objects.ObjectQuery<WpfApplProject.Employee>".

в using прописал все как положено, здесь похоже именно с преобразованием типов проблемы

Igor Chelyadinski
Igor Chelyadinski
Беларусь, Минск, №54, 2013
Валентина Алешина
Валентина Алешина
Россия