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

29.04.2018 18:41 | dronov_va

Вниманию читателей предлагается новая статья из цикла, посвящённого программированию универсальных приложений Windows 10 в среде Microsoft Visual Studio. Подразумевается, что читатели уже прочитали опубликованные ранее статьи "Разработка универсальных приложений Windows 10. Самое первое приложение", "Разработка универсальных приложений Windows 10. Размещение элементов на страницах", "Разработка универсальных приложений Windows 10. Привязка данных" и уже имеют понятие об универсальных приложениях и принципах их разработки.

Платформа универсальных приложений Windows (UWP) предоставляет большой набор элементов управления. Особое место среди них занимают всевозможные списки: как простые, наподобие обычного (ListBox) и раскрывающегося (ComboBox) списков, так и сложные, в число которых входят таблица (ListView) и перечень (GridView). Эти элементы управления предосталяют разработчику богатые средства для создания их пунктов, изменения их представления и поведения, поэтому заслуживают, чтобы им уделили особое внимание.

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


1. ComboBox: раскрывающийся список

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


1.1. Задание пунктов списка

Задать пункты, которые будут присутствовать в раскрывающемся списке, мы можем как с помощью привязки данных (за подробностями обращайтесь к статье "Разработка универсальных приложений Windows 10. Привязка данных"), так и указав все необходимые пункты непосредственно в этом списке. Пункты могут представлять собой как обычные строки текста, так и объекты какого-либо класса.


1.1.1. Задание пунктов списка посредством привязки данных

Поскольку мы уже владеем привязкой данных, давайте создадим два наших первых раскрывающихся списка с её применением. Создадим в Visual Studio новый проект и дадим ему имя ComboBox1. Откроем файл с интерфейсным кодом начальной страницы, укажем у имеющегося на ней контейнера-сетки Grid набор из двух строк и одного столбца, для которых укажем такие размеры, чтобы только полностью вместить их содержимое.

Внимание!
В Visual Studio Community 2017 версии 15.6.6, которой пользовался автор статьи, по какой-то причине не работают визуальные инструменты для задания строк и столбцов у контейнера-сетки. Кнопки панели Свойства, посредством которых вызываются эти инструменты, почему-то всегда недоступны. Поэтому формировать строки и столбцы у сетки придётся, вводя соответствующий XAML-код вручную.

В частности, в нашем случае нам понадобится добавить в тег <Grid>, создающий контейнер-сетку, такой фрагмент кода:

<Grid . . .>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
</Grid>


Поместим на страницу раскрывающийся список ComboBox (в панели Панель элементов он находится в "ветви" Все элементы управления XAML). Зададим для него такие параметры:

  • Width - Auto;
  • Height - Auto;
  • Row - 0;
  • Column - 0;
  • RowSpan - 1;
  • ColumnSpan - 1;
  • HorizontalAlignment - Left;
  • VerticalAlignment - Top;
  • Margin - 10 пикселов со всех сторон.



Для хранения перечня пунктов, которые будут присутствовать в новом списке, мы используем объект-коллекцию обобщённого класса List<T> из пространства имён System.Collection.Generic.

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

public sealed partial class MainPage : Page
{
public List<string> data;

public MainPage()
{
this.InitializeComponent();

this.data = new List<string>();
this.data.Add("XAML");
this.data.Add("C#");
this.data.Add("Visual Basic .NET");
this.data.Add("C++");
}
}


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

Ради простоты и экономии системных ресурсов мы создадим необъектную одномоментную привязку. Вернёмся к файлу с интерфейсным кодом страницы и найдём в редакторе XAML-кода тег <ComboBox>, создающий наш раскрывающийся список. Добавим непосредственно в него следующий фрагмент:

<ComboBox . . . ItemsSource="{x:Bind data, Mode=OneTime}"/>

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

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

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

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

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

public PL(string name, string description)
{
this.name = name;
this.description = description;
}
}


Он содержит два свойства - name и description, - в которых будут храниться, соответственно, название и описание языка программирования. Обратим внимание, что для хранения данных мы объявили именно свойства, а не поля; почему - мы узнаем чуть позже. Также мы объявили конструктор, который примет в качестве параметров эти самые имя и описание и занесёт их в свойства вновь созданного объекта.

Откроем файл функционального кода начальной страницы, объявим в классе страницы поле data2, в котором будет храниться коллекция объектов, и добавим в конструктор класса код, который создаст и заполнит эту коллекцию. После чего объявление класса страницы будет выглядеть следующим образом (часть кода пропущена):

public sealed partial class MainPage : Page
{
. . .
public List<PL> data2;

public MainPage()
{
this.InitializeComponent();

. . .

this.data2 = new List<PL>();
this.data2.Add(new PL("JavaScript", "Интерпретируемый язык для Web-программирования"));
this.data2.Add(new PL("TypeScript", "Интерпретируемый язык Web-программирования"));
this.data2.Add(new PL("Python", "Интерпретируемый язык программирования общего назначения"));
this.data2.Add(new PL("F#", "Компилируемый язык функционального программирования"));
}
}


Вернёмся к интерфейсному коду начальной страницы, но пока не будем вносить в него никаких правок, а немного подумаем. Мы уже знаем, что при формировании пунктов списка платформа UWP берёт элементы указанной коллекции, преобразует их в строковый вид, вызвав у них метод ToString(), и использует возвращённые этим методом строки в качестве надписей для создаваемых пунктов. Но у объектов объявленного нами класса PL метод ToString(), унаследованный от родителя, возвращает строковое имя класса. А это нам совсем не нужно.

Нам придётся указать раскрывающемуся списку, чтобы он извлекал строковые значения, из которых будут формироваться надписи для пунктов, из определённого свойства класса, представляющего пункт, в нашем случае - свойства name класса PL. Для этого мы применим свойство DisplayMemberPath, унавледованное классом ComboBox от родительского класса ItemsControl. Мы присвоим ему имя нужного свойства, представленное в виде строки.

Отметим, что список может извлекать данные только из свойств, но никак не из полей. Именно поэтому мы использовали для хранения данных в классе PL полноценные свойства, а не поля.

Отыщем в редакторе XAML-кода тег <ComboBox>, создающий второй список. Допишем в него код, устанавливающий привязку свойства ItemsSource к полю data2 класса страницы и указывающий списку извлекать текст надписей для пунктов из свойства name:

<ComboBox . . . ItemsSource="{x:Bind data2, Mode=OneTime}" DisplayMemberPath="name"/>

Кстати, значение свойства DisplayMemberPath мы можем указать непосредственно в панели Свойства. Соответствующий параметр находится в категории Общие, в её дополнительной секции, а значение для параметра заносится вручную.

Сохраним проект и запустим приложение на выполнение. Мы увидим два раскрывающихся списка, каждый из которых перечисляет названия языков программирования (рис. 1).


Рис. 1. Приложение ComboBox1 с двумя раскрывающимися списками




1.1.2. Задание пунктов непосредственно в списке

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

Класс ComboBox поддерживает свойство Items, также унаследованное от родительского класса ItemsControl. Это свойство хранит объект класса-коллекции ItemCollection, который и представляет перечень пунктов списка.

Задать коллекцию ItemCollection, которая будет хранить пункты списка, можно двумя способами. Мы изучим их на конкретном примере - проекте ComboBox2, который сразу же и создадим. У контейнера-сетки, находящегося на начальной странице, мы создадим один столбец и две строки, для которых укажем такие размеры, чтобы только вместить их содержимое (необходимый для этого XAML-код был приведён в параграфе 1.1.1).

Первый способ: формирование коллекции ItemCollection непосредственно в интерфейсном XAML-коде в виде списка строковых значений. Он подойдёт, если пункты списка должны формироваться на основе обычных строк.

Поместим на начальную страницу раскрывающийся список и укажем для него те же параметры, что и у первого списка созданного ранее проекта ComboBox1 (см. параграф 1.1.1). Найдём в редакторе XAML-кода представляющий тег ComboBox, создающий этот список, и внесём в него следующие исправления:

<ComboBox . . .>
<ComboBox.Items>
<x:String>ComboBox</x:String>
<x:String>ListBox</x:String>
<x:String>ListView</x:String>
<x:String>GridView</x:String>
</ComboBox.Items>
</ComboBox>


Здесь мы создали в списке четыре пункта, перечисляющие все доступные в UWP классы списков. Каждый пункт мы записали в виде строки, заключив его в тег <x:String>.

Кстати, тег <ComboBox.Items> из приведённого ранее кода мы можем убрать:

<ComboBox . . .>
<x:String>ComboBox</x:String>
<x:String>ListBox</x:String>
<x:String>ListView</x:String>
<x:String>GridView</x:String>
</ComboBox>


Компилятор Visual Studio "поймёт", что все элементы, находящиеся внутри тега <ComboBox>, должны быть помещены в коллекцию Items.

Если мы теперь запустим приложение на выполнение, то увидим, что в пока единственном раскрывающемся списке присутствуют заданные нами четыре пункта.

Второй способ: программное добавление пунктов в коллекцию. Для этих целей класс ItemCollection поддерживает следующие методы:

  • Add(<Объект>) - добавляет указанный <Объект>, который может быть любого типа, в коллекцию;
  • Insert(<Индекс>, <Объект>) - вставляет указанный <Объект>, который может быть любого типа, в позицию, заданную целочисленным <Индексом>;
  • RemoveAt(<Индекс>) - удаляет элемент коллекции, расположенный по указанному <Индексу>;
  • Clear() - очищает коллекцию.



Для примера давайте программно добавим в первый список ещё один пункт. Сначала зададим для списка имя, скажем, cbo1. После чего переключимся на функциональный код начальной страницы и добавим в конструктор следующее выражение, поместив его после вызова метода InitializeComponent():

this.cbo1.Items.Add("ItemsControl");

Запустим приложение на выполнение и убедимся, что в списке появился новый пункт, который мы добавили программно.

Аналогичным образом мы можем добавлять в коллекцию Items не простые строки, а объекты какого-либо класса.

Добавим в проект новый файл программного кода PL.cs, в котором объявим класс PL, аналогичный тому, что мы объявили в параграфе 1.1.1. Только его код будет несколько иным:

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

public PL() { }
public PL(string name, string description)
{
this.name = name;
this.description = description;
}
}


Мы добавили в класс ещё один конструктор - не принимающий параметров. Без такого конструктора мы не сможем создать объект этого класса в XAML-коде.

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

В редакторе XAML-кода найдём тег <ComboBox>, создающий второй список, и исправим его следующим образом:

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


Пространство имён local - это пространство имён самого приложения. Поскольку класс PL объявлен в нём, нам обязательно следует предварить им имя данного класса, разделив их двоеточием. И не забываем о свойстве DisplayMemberPath.

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

this.cbo2.Items.Add(new PL("ItemsControl", "Класс - родитель всех списков"));

Осталось только запустить приложение и удостовериться, что всё работает как надо.

Как видим, мы можем создавать пункты списков как в XAML-коде, то есть декларативно, так и в функциональном коде, программно. Последнее может пригодиться, если содержимое списков меняется по ходу выполнения приложения.

Нужно отметить очень важную деталь. Любое значение, которое добавляется в коллекцию Items списка, неявно преобразуется в объект класса ComboBoxItem. В дальнейшем все пункты списка представляются и обрабатываются как объекты этого класса.


1.1.3. Создание в списках пунктов со сложным содержимым

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

Сделать это довольно просто. Если пункты списка формируются в XAML-коде, следует записать необходимое количество тегов <ComboBoxItem>, создающих объекты данного класса, и указать их параметры, воспользовавшись соответствующими свойствами. Если же пункты формируются программно, нужно создать объекты класса ComboBoxItem, применив его конструктор, который не принимает параметров, опять же, задать их параметры и добавить в коллекцию Items списка.

Создадим третий по счёту проект, дав ему имя ComboBox3. В контейнере-сетке начальной страницы создадим один столбец и одну строку, задав для них такие размеры, чтобы лишь вместить содержимое. И поместим на страницу раскрывающийся список, указав для него те же параметры, что мы давали всем созданным ранее спискам, ширину (параметр Width), равную 300, и имя cbo.

Можно добавить в список все необходимые теги <ComboBoxItem> вручную. Но можно воспользоваться помощью Visual Studio, для чего достаточно найти в визуальном редакторе нужный список, щёлкнуть на нём правой кнопкой мыши и выбрать в появившемся на экране контекстном меню пункт Добавить ComboBoxItem. Сразу же после этого в теге <ComboBox>, представляющем список, появится такой код, созданный средой разработки:

<ComboBoxItem Content="ComboBoxItem" Height="100" Width="100"/>

Свойство Content задаёт содержимое пункта, свойство Height - его высоту, а свойство Width - ширину. Все эти свойства унаследованы от класса-родителя ContentControl.

Добавим в список ещё два объекта класса ComboBoxItem, чтобы список содержал три пункта. После чего изменим код, создающий список, следующим образом:

<ComboBox . . .>
<ComboBoxItem Content="По левому краю"/>
<ComboBoxItem>
<ComboBoxItem.Content>
<TextBlock Text="По середине" HorizontalAlignment="Center"/>
</ComboBoxItem.Content>
</ComboBoxItem>
<ComboBoxItem>
<ComboBoxItem.Content>
<TextBlock Text="По правому краю" HorizontalAlignment="Right"/>
</ComboBoxItem.Content>
</ComboBoxItem>
</ComboBox>


В качестве содержимого первого пункта списка мы задали обычную строку "По левому краю". Эту строку мы указали непосредственно в свойстве Content пункта списка, как раз и предназначенном для задания его содержимого.

А в качестве содержимого второго и третьего пунктов мы задали элементы интерфейса - надписи TextBlock. Для этих надписей мы указали разные значения выравнивания.

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

Параметры ширины и высоты (свойства Width и Height) явно мы нигде не задаём. В этом случае пункт по горизонтали растянется на всю ширину списка и примет такую высоту, чтобы вместить содержимое.

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

ComboBoxItem cbi;
cbi = new ComboBoxItem();
cbi.Content = "[Выравнивание по умолчанию]";
cbi.FontWeight = Windows.UI.Text.FontWeights.Bold;
cbi.FontStyle = Windows.UI.Text.FontStyle.Italic;
this.cbo.Items.Add(cbi);


Для программно созданного пункта списка мы указываем полужирный курсивный шрифт надписи.

Запустим приложение на выполнение и полюбуемся на наш раскрывающийся список. Выглядит он действительно впечатляюще - см. рис. 2. (Мы даже увеличили ширину списка для того, чтобы различное выравнивание у его пунктов было сразу заметно.)


Рис. 2. Раскрывающийся список, пункты которого имеют сложное содержимое, в приложении ComboBox3




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




Продолжение следует...


Владимир Дронов, 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.031 секунд (Общее время SQL: 0.014 секунд - SQL запросов: 51 - Среднее время SQL: 0.00028 секунд))
Top.Mail.Ru