Настройка отображения данных с привязкой данных и WPF - Использование множества элементов управления для отображения бизнес-объектов

ОГЛАВЛЕНИЕ

Использование множества элементов управления для отображения бизнес-объектов

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

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

Рис. 7. Классы для создания иерархии бизнес-объектов

public class Customer
{
   public string Name { get; set; }
   public List<Order> Orders { get; set; }

   public override string ToString()
   {
     return this.Name;
   }
}

public class Order
{
   public string Desc { get; set; }
   public List<OrderDetail> OrderDetails { get; set; }

   public override string ToString()
   {
     return this.Desc;
   }
}

public class OrderDetail
{
   public string Product { get; set; }
   public int Quantity { get; set; }
}

Код XAML окна, отображающего эти объекты, показан наРис. 8. Он очень похож на код XAML сРис. 6, но между существуют важные отличия, на которые стоит обратить внимание. Чего в коде XAML не наблюдается, так это конструктора окна, создающего объекты данных и устанавливающего DataContext, вместо установки кодом XAML ссылки на него, как на ресурс. Обратите внимание, что ни в одном из элементов управления свойство DataContext не установлено прямо. Все они наследуют то же свойство DataContext, являющееся экземпляром List<Customer>.

Рис. 8. Код XAML для привязки иерархических бизнес-объектов к интерфейсу пользователя

<Grid Margin="4">
  <Grid.RowDefinitions>
   <RowDefinition Height="Auto" />
   <RowDefinition />
   <RowDefinition />
  </Grid.RowDefinitions>

  <!-- CUSTOMERS -->
  <DockPanel Grid.Row="0">
   <TextBlock DockPanel.Dock="Top" FontWeight="Bold" Text="Customers"
   />
   <ComboBox 
    IsSynchronizedWithCurrentItem="True" 
    ItemsSource="{Binding Path=.}" 
    />
  </DockPanel>

  <!-- ORDERS -->
  <DockPanel Grid.Row="1">
   <TextBlock DockPanel.Dock="Top" FontWeight="Bold" Text="Orders" />
   <ListBox 
    IsSynchronizedWithCurrentItem="True" 
    ItemsSource="{Binding Path=CurrentItem.Orders}" 
    />
  </DockPanel>

  <!-- ORDER DETAILS -->
  <DockPanel Grid.Row="2">
   <TextBlock DockPanel.Dock="Top" FontWeight="Bold" 
     Text="Order Details" />
   <ItemsControl
    ItemsSource="{Binding Path=CurrentItem.Orders.CurrentItem.
    OrderDetails}"
    >
    <ItemsControl.ItemTemplate>
     <DataTemplate>
      <TextBlock>
       <Run>Product:</Run>
       <TextBlock Text="{Binding Path=Product}" />
       <Run>(</Run>
       <TextBlock Text="{Binding Path=Quantity}" />
       <Run>)</Run>
      </TextBlock>
     </DataTemplate>
    </ItemsControl.ItemTemplate>
   </ItemsControl>
  </DockPanel>
</Grid>

Другим существенным различием при привязке к бизнес-объектам вместо кода XML является то, что элементу ItemsControl, размещающему сведения о заказе, не нужно проводить привязку к SelectedItem списка заказа. Этот подход был необходим в случае привязки XML по причине отсутствия универсального способа сослаться на текущий элемент списка, элементы которого происходят из локального запроса XPath.

При привязке к бизнес-объектам вместо XML привязка ко вложенным уровням выбранных элементов является тривиальной задачей. Принадлежащая элементу ItemsControl привязка ItemsSource использует эту удобную функцию, дважды указывая CurrentItem в пути привязки: один раз для выбранного клиента, второй раз для выбранного заказа. Свойство CurrentItem является членом базового представления ICollectionView, являющегося оберткой источника данных, как уже говорилось выше.

Существует еще один интересный момент, относящийся к различию в подходах к работе XML и бизнес-объекта. Поскольку пример XML привязывает к XmlElements, необходимо предоставить шаблоны DataTemplate, чтобы объяснить, как визуализировать клиентов и заказы. При привязке к специальным бизнес-объектам можно избежать этого лишнего труда, просто переопределив метод ToString классов Customer («Клиент») и Order («Заказ») и позволив WPF отобразить выходные данные этого метода для данных объектов. Этого фокуса достаточно лишь для объектов, у которых могут быть простые текстовые представления. При работе со сложными объектами данных использование этой удобного приема может не иметь смысла.