Привязка к свойствам объектов

31

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

Например, коллекцию бизнес-объектов требуется представить в табличном виде с помощью элемента управления типа DataGrid, а данные из XML документа в списочном виде с помощью ряда элементов управления ListBox.

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

Создание отдельной коллекции специальных объектов

Создайте новый проект приложения WPF (или Silverlight), присвоив ему имя CollectionDataContext. Введите новый класс PurchaseOrder (заказ на поставку) по команде меню Project --> Add New Item. Обновите этот класс кодом, поддерживающим ряд свойств, выбранных на ваше усмотрение, а также специальным конструктором в котором устанавливаются значения этих свойств. Ниже приведен один из возможных вариантов написания класса PurchaseOrder в исходном коде на C#:

public class PurschaseOrder
	{
		public PurschaseOrder()
		{}
		
		public PurschaseOrder(int amt, double cost, string desc)
		{
			Amount = amt;
			TotalCost = cost;
			Description = desc;
		}
		
		public int Amount { get; set; }
		public double TotalCost { get; set; }
		public string Description { get; set; }
	}

Конечно, значения трех указанных выше свойств можно связать с элементом пользовательского интерфейса в Expression Blend, опираясь на единственный параметр класса PurchaseOrder. Но на практике чаще всего требуется привязывать коллекцию подобных объектов к элементам пользовательского интерфейса.

Поэтому определите второй класс PurchaseOrders (заказы на поставку); обратите внимание на имя этого класса во множественном числе. Этот класс можно добавить в тот же самый файл исходного кода на C# или же ввести его в текущий проект как еще один новый класс по команде меню Project --> Add New Item:

public class PurschaseOrders : 
		System.Collections.ObjectModel.ObservableCollection
	{
		public PurschaseOrders()
		{
			// Добавить в коллекцию несколько элементов
			this.Add(new PurschaseOrder(5, 50.00, "Cat"));
			this.Add(new PurschaseOrder(10, 80.00, "Dog"));
			this.Add(new PurschaseOrder(1, 2.50, "Butterfly"));
		}
	}

А теперь когда создана специальная коллекция бизнес-объектов, можно построить пользовательский интерфейс, в котором будут отображаться данные каждого члена коллекции. (Напомним, что в рассматриваемом здесь примере проекта нужно увязать свойства отдельных объектов типа PurchaseOrder из этой коллекции со свойствами элементов пользовательского интерфейса.)

В частности, свойство каждого объекта типа PurchaseOrder можно привязать к ряду раскрывающихся списков или же всю коллекцию к табличному виду типа DataGrid. Так или иначе, решение задачи такой привязки данных в среде Expression Blend IDE существенно упрощается с помощью панели Data.

Определение источника данных объекта на панели Data

Если в Expression Blend требуется привязать специальный объект к пользовательскому интерфейсу, например, коллекцию типа PurchaseOrders из рассматриваемого здесь примера проекта, то для этой цели следует сначала перейти к панели Data, чтобы определить новый источник данных объекта. Итак, перейдите к панели Data, располагаемой по умолчанию с правой стороны рабочего пространства Expression Blend, щелкните на кнопке раскрывающегося списка Create data source и выберите вариант Create Object Data Source (Создать источник данных объекта) из раскрывающегося списка:

Определение источника данных на панели Data

В итоге откроется новое диалоговое окно, в котором можно выбрать самые разные типы объектов NET, в том числе и любые специальные классы, включенные в состав текущего проекта.

Если вы не увидите в этом окне специальный класс, введенный вами в проект, значит, вы просто забыли создать свой проект!

Как показано на рисунке ниже все узлы стандартных библиотек свернуты для большей удобочитаемости, а специальные классы представлены сборками того же наименования. Итак, выберите коллекцию типа PurchaseOrders и обратите внимание на то, что в верхней части текущего диалогового окна автоматически указывается предполагаемое имя PurchaseOrdersDataSource нового источника данных. Это очень удобно, хотя по желанию можете переименовать его:

Выбор коллекции PurschaseOrders

Щелкнув на кнопке ОК, проанализируйте содержимое панели Data. На этой панели вы обнаружите новый источник данных объекта со всеми свойствами объектов, составляющих выбранную вами коллекцию:

Источники данных на панели Data

Если вы проанализируете теперь разметку исходного окна типа Window (или элемента управления типа UserControl на начальной веб-странице, при условии, что работаете над данным проектом на платформе Silverlight), то непременно заметите, что в среде Expression Blend IDE автоматически определен новый ресурс объекта, увязанный с объектом специальной коллекции, как показано ниже:

<Window.Resources>
		<local:PurschaseOrders x:Key="PurschaseOrdersDataSource" d:IsDataSource="True"/>
</Window.Resources>

Именно этот ресурс и будет применяться в операции привязки данных.

Привязка всей коллекции к списку

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

Если вы посмотрите на левый верхний угол панели Data, то обнаружите там две кнопки. Левая кнопка, выбираемая по умолчанию, активизирует режим отображения списком (List Mode) выбранного в настоящий момент источника данных. Когда источник данных установлен в режим отображения списком, можете перетащить отдельное свойство или объект из панели Data, чтобы отобразить его в новом списке, представленном элементом управления типа ListBox. А правая кнопка настраивает источник данных на работу в режиме отображения подробностей (Details Mode), рассматриваемом далее.

Допустим, вы находитесь в установленном по умолчанию режиме отображения списком. Если вы виберете узел PurchaseOrders на панели Data и перетащите его на монтажный стол, то обнаружите на нем один добавленный элемент управления типа ListBox, в котором отображаются все свойства каждого объекта из выбранной коллекции:

Данные всей коллекции

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

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

Назначение контекста данных

Если вы откроете редактор XAML, то заметите, что в элементе разметки <Grid>, описывающем диспетчер компоновки LayoutRoot, появился новый атрибут DataContext, которому присваивается выражение, привязывающее этот диспетчер к ресурсу объекта PurchaseOrdersDataSource, применяемому на уровне главного окна типа Window. Когда источник данных устанавливается в атрибуте DataContext диспетчера компоновки, любой порожденный элемент может использовать этот источник данных (в данном случае коллекцию типа PurchaseOrsers) при выполнении операции привязки данных.

<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource PurschaseOrdersDataSource}}">

Обратите также внимание на то, что в элементе разметки <Grid> устанавливаются два представляющих особый интерес свойства элемента управления типа ListBox: ItemsSource и ItemTemplate.

В частности свойство ItemsSource может выглядеть в разметке не совсем обычно, поскольку оно описывается только пустым расширением разметки (Binding). Но это просто означает, что источник данных привязывается к элементу управления типа ListBox в результате операции, которая на самом деле определяется связанным с ним шаблоном данных.

<ListBox HorizontalAlignment="Left" ItemTemplate="{DynamicResource PurschaseOrderTemplate}" ItemsSource="{Binding}" ...

Ресурс, указываемый в свойстве ItemTemplate, представляет собой новый ресурс объекта, помещаемый на свое место в результате перетаскивания узла PurchaseOrders из панели Data на монтажный стол.

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

Контекст данных

А теперь, когда стало ясно, что именно формируется в Expression Blend при переносе всей коллекции на монтажный стол, нажмите комбинацию клавиш <Ctrl+Z>, чтобы вернуться к исходному внешнему виду главного окна приложения. После этого убедитесь в том, что в разметке отсутствует описание элемент управления типа ListBox, но сохранилось описание источника данных объекта.

Пройди тесты
Лучший чат для C# программистов