Опрос
Вы участвуете в программе Windows Insider?
Популярные новости
Обсуждаемые новости

23.05.2018 13:40 | dronov_va

Вторая часть статьи, посвящённой использованию простых списков в универсальных приложениях Windows 10.


1.2. Получение выбранного пункта

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

Начнём с рассмотрения свойств. Первые четыре унаследованы классом ComboBox от его родителя Selector, а пятое объявлено в самом классе ComboBox.

  • SelectedIndex - хранит целочисленный индекс выбранного в данный момент пункта списка. Если ни один пункт не выбран, хранит значение -1;
  • SelectedItem - хранит сам выбранный пункт списка в виде значения, на основе которого он был сформирован (так, если пункт списка формировался на основе строки, это свойство хранит строку, если на основе объекта класса PL - объект класса PL). Тип возвращаемого значения в любом случае - object, поэтому в большинстве случаев придётся преобразовывать полученное из свойства значение к нужному типу. Если ни один пункт в данный момент не выбран, хранит значение null;
  • SelectedValue - хранит значение, извлечённое из свойства, имя которого указано в свойстве SelectedValuePath и которое принадлежит объекту, сформировавшему выбранный в данный момент пункт списка. Если ни один пункт не выбран, хранит значение null;
  • SelectedValuePath - задаёт имя свойства, из которого будет извлекаться значение для свойства SelectedValue. Это свойство должно поддерживаться объектами, на основе которых формируются пункты списка. Имя свойства задаётся в виде строки. Тип возвращаемого значения в любом случае - object, поэтому в большинстве случаев придётся преобразовывать полученное значение к нужному типу;
  • SelectionBoxItem - аналогично свойству SelectedItem, но доступно только для чтения и хранит элемент, представляющий пункт, который отображается в списке, когда тот закрыт.



Событие SelectionChanged возникает при выборе другого пункта в раскрывающемся списке.

Закрепим полученные знания на практике. Создадим проект ComboBox4. В контейнере-сетке, что имеется на начальной странице, создадим один столбец и три строки, задав для них такие размеры, чтобы только вместить их содержимое.

Поместим на начальную страницу раскрывающийся список, зададим для него те же параметры, что и для первых помещаемых на страницы списков в предыдущих проектах, и имя cboLs.

Далее поместим на страницу надпись (TextBlock) с такими параметрами:

  • Имя - lblName;
  • Text - "" (пустая строка);
  • Width - Auto;
  • Height - Auto;
  • Row - 1;
  • Column - 0;
  • RowSpan - 1;
  • ColumnSpan - 1;
  • HorizontalAlignment - Stretch;
  • VerticalAlignment - Stretch;
  • Margin - 10 пикселов со всех сторон.



И, наконец, добавим ещё одну надпись с теми же параметрами, за исключением:

  • Имя - lblDescription;
  • Row - 2.



Добавим в проект новый файл программного кода PL.cs, в котором объявим класс PL. Код объявления этого класса можно взять из параграфа 1.1.2 (предпоследний листинг).

Переключимся на интерфейсный код начальной страницы, найдём в редакторе XAML-кода тег <ComboBox>, представляющий список, и вставим в него код, что создаст необходимые пункты. Этот код можно позаимствовать из последнего листинга в параграфе 1.1.2.

Зададим для списка cboLs ещё два параметра:

  • DisplayMemberPath - name;
  • SelectedValuePath - description.



Привяжем к событию SelectionChanged списка обработчик с таким кодом:

private void cboLs_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (this.cboLs.SelectedIndex > -1)
{
this.lblName.Text = (this.cboLs.SelectedItem as PL).name;
this.lblDescription.Text = (string)this.cboLs.SelectedValue;
}
}


Нужно помнить, что значения, получаемые из свойство SelectedItem и SelectedValue, всегда имеют тип object. Поэтому мы преобразуем полученные из них значения к нужному нам типу явно.

Запустим приложение на выполнение. Попробуем выбрать в списке какой-либо пункт и посмотрим, что появится на экране (рис. 3).


Рис. 3. Окно приложения ComboBox4




1.3. Прочие возможности раскрывающегося списка

Теперь рассмотрим другие полезные нам свойства и события класса ComboBox, представляющего раскрывающийся список. Начнём со свойств.

  • Header - указывает текст надписи, которая будет выводиться над списком. Свойство имеет тип object, так что мы можем использовать в качестве текста надписи значение любого типа. Используя это свойство, мы избежим необходимости применять для создания такой надписи отдельный элемент интерфейса;
  • MaxDropDownHeight - задаёт высоту списка в пунктах в виде целого числа. Знаение по умолчанию - Double.PositiveInfinity (то есть высота не ограничена);
  • PlaceholderText - задаёт текст внутренней подсказки, которая будет выводиться непосредственно в раскрывающемся списке, когда в нём не выбран ни один пункт. Значение по умолчанию - пустая строка (внутренняя подсказка отсутствует);
  • PlaceholderForeground - задаёт цвет текста внутренней подсказки в виде объекта класса Brush;
  • IsDropDownOpen - хранит значение False, если список в данный момент закрыт, и True, если он открыт. Значение по умолчанию - False (изначально список закрыт);
  • IsTextSearchEnabled - хранит значение True, если список поддерживает режим поиска пункта по первым набранным пользователем символам, и False, если такой режим не поддерживается. Значение по умолчанию - True (то есть изначально такой поиск поддерживается);
  • SelectionChangedTrigger - указывает момент времени, в который возникает событие SelectionChanged. Значение свойства должно представлять собой один из элементов перечисления ComboBoxSelectionChangedTrigger: Committed (событие возникает только при выборе пункта; поведение по умолчанию) или Always (событие возникает и при выборе пункта, и при перемещении курсора мыши на другой пункт без его выбора).



Теперь рассмотрим события.

  • DropDownOpened - возникает при открытии раскрывающегося списка;
  • DropDownClosed - возникает при закрытии раскрывающегося списка.



Все эти программные инструменты нельзя отнести к числу часто используемых (за исключением, может быть, свойства Header), однако в некоторых случаях они могут оказаться полезными. Так, если набор пунктов в раскрывающемся списке формируется динамически в процессе работы приложения, его можно создавать непосредственно при открытии списка, в обработчике события DropDownOpened.


1.4. Настройка представления пунктов раскрывающегося списка с помощью шаблонов

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

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

Именно о шаблонах и пойдёт речь в последующих параграфах.


1.4.1. Задание одного шаблона для всех пунктов списка

Чаще всего один шаблон задаётся сразу для всех пунктов списка. Для его указания применяется свойство ItemTemplate, унаследованное от класса-родителя ItemsControl. А сам шаблон, заносимый в это свойство, должен представляться объектом класса DataTemplate.

Практически всегда шаблон формируется в интерфейсном XAML-коде. В тег <DataTemplate>, формирующий этот шаблон, записывается код, создающий элемент интерфейса, который задаст представление для пункта списка. Если представление пункта создаётся несколькими элементами интерфейса, они предварительно должны быть помещены в какой-либо контейнер.

Чтобы вывести в определённом элементе интерфейса значение нужного свойства класса - пункта списка, следует создать объектную привязку между свойством элемента интерфейса, применяемом для вывода значения, и свойством класса-пункта, из которого будет извлечено выводимое значение. Из параметров привязки здесь указывается только имя связываемого свойства класса-пункта, которое ставится сразу после ключевого слова Binding. Отметим ещё раз, что следует создать именно объектную привязку, так как необъектная в таких случаях почему-то не работает.

Откроем созданный ранее проект ComboBox4, если уже закрыли его. Давайте создадим для пунктов списка шаблон, который выведет, помимо имени класса списка (оно хранится в свойстве name класса PL), ещё и его описание (извлекаемое из свойства description).

Прежде всего, обязательно удалим из тега <ComboBox> атрибут DisplayMemberPath, задающий значение одноимённого свойства. Если это не сделать, при попытке открыть список мы получим ошибку исполнения.

Далее добавим в этот тег код, который создаст шаблон:

<ComboBox . . .>
. . .
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding name}" FontSize="20" FontWeight="Bold"/>
<TextBlock Text="{Binding description}" FontSize="14" FontStyle="Italic" HorizontalAlignment="Right"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>


Здесь для формирования шаблона мы использовали контейнер-стопку StackPanel в изначальной вертикальной ориентации. В нём мы поместили две надписи, из которых верхняя выведет имя класса (полужирным шрифтом размером 20 пикселов), а нижняя - описание (курсивным шрифтом размером 14 пикселов с выравниванием по правому краю). Для указания текста надписи в классе TextBlock применяется свойство Text - его-то мы и связали с нужным свойством класса-пункта PL, создав объектную привязку.

Сохраним изменения и запустим приложение. Откроем список - и сразу убедимся, что пункты выводятся в том виде, в котором мы их и задумывали (рис. 4).


Рис. 4. Окно исправленного приложения ComboBox4. Для вывода пунктов списка применяется шаблон



Интересно, что, если мы выберем какой-либо пункт и закроем список, выбранный пункт будет выведен в нём также с применением заданного шаблона (рис. 5).


Рис. 5. Окно исправленного приложения ComboBox4. Выбранный пункт списка выводится также с применением заданного шаблона




1.4.2. Задание разных шаблонов для разных пунктов списка

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

Сначала нужно объявить класс, который будет выполнять проверку условия, на основе которого будет выбираться нужный шаюлон, и, собственно, выборку шаблона. Этот класс должен быть потомком класса DataTemplateSelector из пространства имён Windows.UI.Xaml.Controls. В этом классе следует переопределить обе разновидности перегруженного метода SelectTemplateCore():

protected virtual DataTemplate SelectTemplateCore(Object <Объект-пункт>)
protected virtual DataTemplate SelectTemplateCore(Object <Объект-пункт>, DependencyObject <Контейнер пункта>)

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

После чего объект класса, выбирающего шаблон, следует присвоить свойству ItemTemplateSelector раскрывающегося списка.

Практиковаться мы будем на всё том же проекта ComboBox4. Откроем его, если уже закрыли.

Сначала немного исправим код класса PL. Давайте добавим к описанию каждого класса-списка признак, говорящий, является ли этот список простым или сложным. Этот признак будет иметь вид логического поля simple. После внесения необходимых правок код класса будет выглядет так:

public class PL
{
public string name { get; set; }
public string description { get; set; }
public bool simple { get; set; }

public PL() { }
}


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

<ComboBox . . .>
<local:PL name="ComboBox" description="Раскрывающийся список" simple="True"/>
<local:PL name="ListBox" description="Обычный список" simple="True"/>
<local:PL name="ListView" description="Таблица" simple="False"/>
<local:PL name="GridView" description="Перечень" simple="False"/>
. . .
</ComboBox>


Добавим в проект новый файл программного кода MyDTS.cs, в котором объявим класс MyDTS, реализующий выбор шаблона в зависимости от значения свойства simple класса PL:

public class MyDTS : Windows.UI.Xaml.Controls.DataTemplateSelector
{
public Windows.UI.Xaml.DataTemplate SimpleTemplate { get; set; }
public Windows.UI.Xaml.DataTemplate ComplexTemplate { get; set; }

protected override Windows.UI.Xaml.DataTemplate SelectTemplateCore(object item)
{
var list = item as PL;
if (list == null)
{
return this.SimpleTemplate;
}
else
{
return list.simple ? this.SimpleTemplate : this.ComplexTemplate;
}
}

protected override Windows.UI.Xaml.DataTemplate SelectTemplateCore(object item,
Windows.UI.Xaml.DependencyObject container)
{
return this.SelectTemplateCore(item);
}
}


В этом классе мы объявили свойства SimpleTemplate и ComplexTemplate; первое свойство будет хранить шаблон, используемый для представления пунктов, у которых свойство simple хранит True, а второе - шаблон, применяемый для вывода пунктов со значением False в свойстве simple. Нужные шаблоны мы занесём в эти свойства в XAML-коде.

Реализация первой разновидности метода SelectTemplateCore() вернёт значение из свойства SimpleTemplate для "пустого" пункта списка и для пункта, чьё свойство simple хранит True, и значение из свойства ComplexTemplate для пункта, у которого в свойстве simple находится False. Что касается реализации второй разновидности этого метода, то она просто вызовет тот же метод первой разновидности и вернёт возвращённый им результат. Как видим, всё просто.

Вернёмся к интерфейсному коду начальной страницы, удалим из тега <ComboBox> код, задающий значение свойства ItemTemplate (то есть написанный нами ранее "единый" шаблон), и добавим в него код, записывающий значение в свойство ItemTemplateSelector:

<ComboBox . . .>
. . .
<ComboBox.ItemTemplateSelector>
<local:MyDTS>
<local:MyDTS.SimpleTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="S" FontSize="40" Grid.RowSpan="2" Foreground="Green" Margin="0,0,10,0"/>
<TextBlock Text="{Binding name}" FontSize="20" FontWeight="Bold" Grid.Column="1"/>
<TextBlock Text="{Binding description}" FontSize="14" FontStyle="Italic" Grid.Column="1" Grid.Row="1"/>
</Grid>
</DataTemplate>
</local:MyDTS.SimpleTemplate>
<local:MyDTS.ComplexTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="C" FontSize="40" Grid.RowSpan="2" Foreground="Red" Margin="0,0,10,0"/>
<TextBlock Text="{Binding name}" FontSize="20" FontWeight="Bold" Grid.Column="1"/>
<TextBlock Text="{Binding description}" FontSize="14" FontStyle="Italic" Grid.Column="1" Grid.Row="1"/>
</Grid>
</DataTemplate>
</local:MyDTS.ComplexTemplate>
</local:MyDTS>
</ComboBox.ItemTemplateSelector>
</ComboBox>


Оба шаблона очень похожи и относительно просты, несмотря на объёмистый код. Для размещения интерфейсных элементов в каждом из них мы использовали контейнер-сетку из двух столбцов и двух строк. Обе строки первого столбца занимает надпись, в которой шрифтом размером в 40 пикселов выводится зелёная буква "S" для простых списков и красная "C" для сложных. Строки второго столбца занимают надписи, выводящие имя и описание класса-списка. Опять же, не забываем предварить имя тега <MyDTS>, создающего объект одноимённого класса, префиксом local, так как этот класс находится в пространстве имён приложения.

Давайте посмотрим, что у нас получилось. Запустим приложение и откроем раскрывающийся список (рис. 6).


Рис. 6. Окно исправленного приложения ComboBox4. Различные пункты списка выводятся с применением разных шаблонов



Часто шаблоны, задающие представление для элементов интерфейса, помещают в ресурсы страницы или приложения. Мы также можем сделать это:

<Page . . .>
<Page.Resources>
<DataTemplate x:Key="SimpleTemplate">
. . .
</DataTemplate>
<DataTemplate x:Key="ComplexTemplate">
. . .
</DataTemplate>
</Page.Resources>

<Grid . . .>
<ComboBox . . .>
. . .
<ComboBox.ItemTemplateSelector>
<local:MyDTS SimpleTemplate="{StaticResource SimpleTemplate}"
ComplexTemplate="{StaticResource ComplexTemplate}"/>
</ComboBox.ItemTemplateSelector>
</ComboBox>
. . .
</Grid>
</Page>


И всё будет прекрасно работать.


Дополнительные материалы




Окончание следует...


Владимир Дронов, MSInsider.Ru Team
Май 2018

Комментарии

Комментариев нет...
Для возможности комментировать войдите в 1 клик через

По теме

Акции MSFT
420.55 0.00
Акции торгуются с 17:30 до 00:00 по Москве
Все права принадлежат © ms insider @thevista.ru, 2022
Сайт является источником уникальной информации о семействе операционных систем Windows и других продуктах Microsoft. Перепечатка материалов возможна только с разрешения редакции.
Работает на WMS 2.34 (Страница создана за 0.033 секунд (Общее время SQL: 0.014 секунд - SQL запросов: 51 - Среднее время SQL: 0.00027 секунд))
Top.Mail.Ru