Советы и приемы при работе с ListView - Создание иерархического представления

ОГЛАВЛЕНИЕ

Создание иерархического представления

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

Предположим, необходимо создать иерархическую таблицу данных, в которой клиенты, заказы и сведения о заказах отображаются соответственно текущим отношениям таблицы. Как тут получить данные и привязать их к элементу управления? Рассмотрите код на рис. 3. Для простой загрузки данных в объектную модель, удобную для хранения иерархии данных, можно использовать LINQ-SQL . Обратите внимание, что при выполнении запроса в LINQ-SQL на деле извлекаются только напрямую запрошенные данные. Другими словами, извлекается только первый уровень графа, связанные объекты не загружаются одновременно с ним.

Рис.3 Загрузка правильных данных

Public Class DataCache
{
  public IEnumerable GetCustomers()
  {
  NorthwindDataContext db = new NorthwindDataContext();
  DataLoadOptions opt = new DataLoadOptions();
  opt.LoadWith<Customer>(c => c.Orders);
  opt.LoadWith<Order>(o => o.Order_Details);
  db.LoadOptions = opt;

  var data = from c in db.Customers
  select new { c.CompanyName, c.Orders };

  return data.ToList();
  }

  public int GetCustomersCount()
  {
  // Return the number of customers
  NorthwindDataContext db = new NorthwindDataContext();
  return db.Customers.Count();  
  }

  public IEnumerable GetCustomers(int maxRows, int startRowIndex)
  {
  if (maxRows < 0)
  return GetCustomers();

  NorthwindDataContext db = new NorthwindDataContext();
  DataLoadOptions opt = new DataLoadOptions();
  opt.LoadWith<Customer>(c => c.Orders);
  opt.LoadWith<Order>(o => o.Order_Details);
  db.LoadOptions = opt;

  var data = (from c in db.Customers
  select new { 
  c.CompanyName, 
  c.Orders 
  }).Skip(startRowIndex).Take(maxRows);
  return data.ToList();
  }
}
NorthwindDataContext db = new NorthwindDataContext();
DataLoadOptions opt = new DataLoadOptions();
opt.LoadWith<Customer>(c => c.Orders);
opt.LoadWith<Order>(o => o.Order_Details);
db.LoadOptions = opt;

Класс DataLoadOptions можно использовать для изменения поведения ядра LINQ-SQL, чтобы данные, на которые дается ссылка указанным отношением, загружались бы немедленно. Код на рис. 3 гарантирует, что заказы загружаются вместе с клиентами, а сведения – вместе с заказами.

Метод LoadWith загружает данные в соответствии с указанным отношением. Метод AssociateWith можно использовать для фильтрации предварительно выбранных связанных объектов следующим образом:

opt.AssociateWith<Customer>(
  c => c.Orders.Where(o => o.OrderDate.Value.Year == 1997));

В данном примере при выборе данных клиента предварительно выбираются только заказы, сделанные в 1997 году. Когда необходимо предварительно выбрать связанные данные или применить фильтр, используется метод AssociateWith. Разработчик должен сам обеспечить отсутствие циклических ссылок между таблицами – например, при загрузке заказов для клиента, а затем загрузке клиента для заказа, как показано здесь:

DataLoadOptions opt = new DataLoadOptions();
opt.LoadWith<Customer> (c => c.Orders);
opt.LoadWith<Order> (o => o.Customer); 

Теперь, когда все данные готовы, можно подумать и о привязке. В данном случае неплохо работает двухуровневый элемент управления ListView. ListView верхнего уровня привязывается к коллекции объектов Customer («Клиент»), а самый глубокий из вложенных элементов управления ListView – к свойству Orders («Заказы») каждого привязанного объекта Customer. В коде на рис. 4 показана разметка для трехуровневого иерархического представления, где клиенты отображаются на первом уровне и визуализируются свойством ItemTemplate внешнего элемента управления ListView. Затем внедренный элемент управления ListView привязывается к заказам. И, наконец, шаблон ItemTemplate встроенного элемента управления ListView содержит элемент управления GridView для перечисления сведений о каждом заказе.

Рис. 4 Трехуровневая иерархия

<asp:ListView ID="ListView1" runat="server" 
  DataSourceID="ObjectDataSource1"
  ItemPlaceholderID="lvItemPlaceHolder">

  <LayoutTemplate>
  <asp:PlaceHolder runat="server" ID="lvItemPlaceHolder" />
  </LayoutTemplate>

  <ItemTemplate>
  <asp:Panel runat="server" ID="panelCustomerInfo"
  cssclass="customerInfo"> 
  <%# Eval("CompanyName") %>
  </asp:Panel>  
  <asp:panel runat="server" ID="panelCustomerDetails"
  cssclass="customerDetails">
  <asp:ListView runat="server" 
  DataSource='<%# Eval("Orders") %>' 
  ItemPlaceholderID="lvOrdersItemPlaceHolder">

  <LayoutTemplate>
  <ul>
  <asp:PlaceHolder runat="server" 
  ID="lvOrdersItemPlaceHolder" />
  </ul>
  </LayoutTemplate>

  <ItemTemplate>
  <li>
  Order #<%# Eval("OrderID") %> 
  <span class="orderDate"> 
  placed on <%#
  ((DateTime)Eval("OrderDate")).ToString
  ("ddd, dd MMM yyyy") %> 
  </span>
  <span class="orderEmployee"> 
  managed by <b><%# Eval("Employee.LastName") %></b>
  </span>
  <asp:GridView runat="server" 
  DataSource='<%# Eval("Order_Details") %>' 
  SkinID="OrderDetailsGridSkin" >
  </asp:GridView>
  </li>
  </ItemTemplate>
  </asp:ListView>
  </asp:panel>
  </ItemTemplate>
</asp:ListView>