Привязка к иерархическим данным¶
В типичном иерархическом источнике данных бизнес-объект имеет свойство, в котором хранится коллекция дочерних объектов данных. Два подхода позволяют вам предоставлять дочерние данные в контролы TreeList и TreeView в режиме иерархической привязки:
-
Дочерний селектор полезен в следующих случаях: - Когда родительский и дочерний бизнес-объекты относятся к разным типам - Когда вы не можете указать путь к дочерним данным
Динамическая загрузка данных¶
При привязке к иерархическому источнику данных TreeList и TreeView контролируют загрузку узлов по требованию по умолчанию: дочерние узлы динамически загружаются при развертывании родительского узла.
Установите для свойства AllowDynamicDataLoading
значение false
, чтобы загрузить все узлы одновременно после привязки контрола к источнику данных.
Если вы используете динамическую загрузку узлов, контролы TreeList и TreeView не имеют доступа к узлам (и их нижележащим данным), которые не были загружены. Это накладывает следующие ограничения на функции проверки узлов и фильтрации/поиска:
-
Когда вы проверяете родительский узел в рекурсивном режиме (см.
AllowRecursiveNodeChecking
), контрол проверяет этот узел вместе с загруженными в данный момент дочерними узлами. Контрол не проверяет дочерние узлы, которые не были загружены. -
Когда вы выполняете поиск данных во встроенном окне поиска или фильтруете данные с помощью строки автоматической фильтрации, контрол выполняет поиск данных только по загруженным в данный момент узлам.
Укажите путь к дочерним данным¶
Один из способов предоставить дочерние данные в контрол TreeList/TreeView заключается в указании имени свойства, которое хранит дочерние данные в бизнес-объекте. Используйте свойство ChildrenFieldName
для этой цели. Например, если бизнес-объект хранит дочерние данные в коллекции Items
, установите для свойства ChildrenFieldName
значение "Items".
Вам также необходимо присвоить свойству HasChildrenFieldName
имя свойства, которое возвращает true
, если бизнес-объект имеет дочерние данные, и false
, в противном случае. Эта информация необходима для отображения или скрытия кнопок расширения узла.
Если вы не укажете свойство HasChildrenFieldName
, контрол отображает кнопки расширения для дочерних узлов до тех пор, пока пользователь не попытается развернуть эти узлы.
В следующем примере демонстрируется, как привязать контрол TreeList к данным. Объект Employee в примере содержит коллекцию Subordinates, которая содержит дочерние элементы. Свойство ChildrenFieldName
указывает имя свойства (строка "Subordinates"), в котором хранятся дочерние данные.
xmlns:mxtl="https://schemas.eremexcontrols.net/avalonia/treelist"
...
<mxtl:TreeListControl Grid.Column="0" Width="400" Name="treeList1" >
<mxtl:TreeListControl.Columns>
<mxtl:TreeListColumn Name="colName" FieldName="Name" Header="Name" />
<mxtl:TreeListColumn Name="colBirthdate" FieldName="Birthdate" Header="Birthdate"/>
</mxtl:TreeListControl.Columns>
</mxtl:TreeListControl>
using CommunityToolkit.Mvvm.ComponentModel;
Employee p1 = new Employee() { Name = "Mark Douglas", Birthdate=new DateTime(1990, 01, 5) };
Employee p2 = new Employee() { Name = "Mary Watson", Birthdate = new DateTime(1985, 12, 17) };
Employee p3 = new Employee() { Name = "Alex Wude", Birthdate = new DateTime(2000, 10, 7) };
Employee p4 = new Employee() { Name = "Sam Louis", Birthdate = new DateTime(1975, 8, 27) };
Employee p5 = new Employee() { Name = "Dan Miller", Birthdate = new DateTime(1981, 3, 6) };
p1.Subordinates.Add(p2);
p1.Subordinates.Add(p3);
p2.Subordinates.Add(p4);
p3.Subordinates.Add(p5);
ObservableCollection<Employee> employees = new ObservableCollection<Employee>() { p1 };
treeList1.ChildrenFieldName = "Subordinates";
treeList1.HasChildrenFieldName = "HasChildren";
treeList1.ItemsSource = employees;
public partial class Employee : ObservableObject
{
[ObservableProperty]
public string name="";
[ObservableProperty]
public DateTime? birthdate=null;
public ObservableCollection<Employee> Subordinates { get; } = new();
public bool HasChildren { get { return Subordinates.Count > 0; } }
}
Укажите выбор дочерних элементов¶
Другим подходом к предоставляет дочерние данные в режиме иерархической привязки является реализация children selector. Селектор указывает, доступны ли дочерние данные в бизнес-объекте, и возвращает эти дочерние данные по запросу. Контрол TreeList/TreeView использует информацию, предоставленную селектором, для создания дочерних узлов и отображения кнопок распахивания узлов.
Дочерний селектор полезен в следующих сценариях:
- Когда родительский и дочерний бизнес-объекты принадлежат к разным типам
- Во всех остальных случаях, когда вы не можете указать путь к дочерним элементам со свойством
ChildrenFieldName
.
Используйте свойство TreeListControlBase.ChildrenSelector
, чтобы назначить селектор вашему контролу TreeList/TreeView. Селектор - это объект, который реализует интерфейс ITreeListChildrenSelector
:
public interface ITreeListChildrenSelector
{
// The method should return whether a business object has child data.
// The control uses this information to display or hide node expand buttons.
bool HasChildren(object item) => true;
// The method should return child objects for a business object.
IEnumerable? SelectChildren(object item);
}
Пример - Реализация простого выбора дочерних элементов¶
В следующем примере создается селектор (MyTreeListChildrenSelector), который предоставляет информацию о дочерних элементах для контрола TreeList.
xmlns:mxtl="https://schemas.eremexcontrols.net/avalonia/treelist"
...
<Grid RowDefinitions="Auto, Auto, Auto, Auto" ColumnDefinitions="400, *">
<Grid.Resources>
<local:MyTreeListChildrenSelector x:Key="mySelector" />
</Grid.Resources>
<mxtl:TreeListControl Grid.Column="0" Name="treeList2"
ChildrenSelector="{StaticResource mySelector}">
<mxtl:TreeListControl.Columns>
<mxtl:TreeListColumn Name="colName1" FieldName="Name" />
<mxtl:TreeListColumn Name="colBirthdate1" FieldName="Birthdate"/>
</mxtl:TreeListControl.Columns>
</mxtl:TreeListControl>
</Grid>
using CommunityToolkit.Mvvm.ComponentModel;
Employee p1 = new Employee() { Name = "Mark Douglas", Birthdate = new DateTime(1990, 01, 5)};
Employee p2 = new Employee() { Name = "Mary Watson", Birthdate = new DateTime(1985, 12, 17)};
Employee p3 = new Employee() { Name = "Alex Wude", Birthdate = new DateTime(2000, 10, 7)};
Employee p4 = new Employee() { Name = "Sam Louis", Birthdate = new DateTime(1975, 8, 27)};
Employee p5 = new Employee() { Name = "Dan Miller", Birthdate = new DateTime(1981, 3, 6)};
p1.Subordinates.Add(p2);
p1.Subordinates.Add(p3);
p2.Subordinates.Add(p4);
p3.Subordinates.Add(p5);
ObservableCollection<Employee> employees = new ObservableCollection<Employee>() { p1 };
treeList2.ItemsSource = employees;
public class MyTreeListChildrenSelector : ITreeListChildrenSelector
{
public bool HasChildren(object? item) => (item as Employee).HasChildren;
public IEnumerable? SelectChildren(object? item) => (item as Employee).Subordinates;
}
public partial class Employee : ObservableObject
{
[ObservableProperty]
public string name="";
[ObservableProperty]
public DateTime? birthdate=null;
public ObservableCollection<Employee> Subordinates = new();
public bool HasChildren { get { return Subordinates.Count > 0; } }
}
Используйте дочерний селектор, когда родительский и дочерний бизнес-объекты различны¶
Следующие соображения применимы, когда родительские и дочерние бизнес-объекты относятся к разным типам.
-
TreeList отображает значения из полей, к которым привязаны столбцы TreeList (см.
TreeListColumn.FieldName
). Родительский и дочерний бизнес-объекты должны предоставлять доступ к этим полям. -
Контрол TreeView отображает значения из бизнес-объектов из поля, имя которого задано свойством
DataFieldName
. Убедитесь, что родительский и дочерний объекты данных содержат это поле.
Пример - Реализация дочернего селектора для различных бизнес-объектов¶
Предположим, что контрол TreeView привязан к коллекции объектов City. Каждый объект City владеет коллекцией объектов Street, которые, в свою очередь, владеют коллекциями объектов Building. TreeView должен отображать города на корневом уровне, улицы на втором уровне и номера зданий на третьем уровне.
Приведенный ниже код показывает определения бизнес-объектов City, Street и Building.
Обратите внимание, что все эти объекты имеют свойство Name. Имя этого свойства будет присвоено свойству TreeViewControl.DataFieldName
в XAML.
public class City
{
public City(string name) { Name = name; }
public string Name { get; set; }
public ObservableCollection<Street> Streets = new();
}
public class Street
{
public Street(string name, string[] buidingNumbers) {
Name = name;
foreach (string number in buidingNumbers)
{
Buildings.Add(new Building(number));
}
}
public string Name { get; set; }
public ObservableCollection<Building> Buildings = new();
}
public class Building
{
public Building(string name) { Name = name; }
public string Name { get; set; }
}
Контрол TreeView привязан к коллекции Cities, определенной в модели представления. Свойству TreeViewControl.DataFieldName
присвоено значение "Name". Это свойство определяет поле бизнес-объектов, значения которых должны отображаться в контроле TreeView.
public partial class MainWindowViewModel : ViewModelBase
{
[ObservableProperty]
ObservableCollection<City> cities = new();
//...
}
Поскольку бизнес-объекты в иерархическом источнике данных относятся к разным типам, необходимо реализовать селектор для извлечения дочерних элементов для узлов Tree View.
public class MyTreeListChildrenSelector : ITreeListChildrenSelector
{
public bool HasChildren(object item)
{
if (item is City city)
return city.Streets.Count > 0;
if (item is Street street)
return street.Buildings.Count > 0;
return false;
}
public IEnumerable? SelectChildren(object item)
{
if (item is City city)
return city.Streets;
if (item is Street street)
return street.Buildings;
return null;
}
}
Чтобы назначить селектор MyTreeListChildrenSelector контролу TreeView, используйте свойство TreeListControlBase.ChildrenSelector
.
xmlns:local="clr-namespace:EremexAvaloniaApplication10.Views"
<mx:MxWindow.Resources>
<local:MyTreeListChildrenSelector x:Key="mySelector" />
</mx:MxWindow.Resources>
<mxtl:TreeViewControl ...
ChildrenSelector="{StaticResource mySelector}"
/>
Полный код¶
Полный код этого примера приведен ниже.
<!-- MainWindow.axaml -->
<mx:MxWindow xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:EremexAvaloniaApplication10.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mx="https://schemas.eremexcontrols.net/avalonia"
xmlns:mxtl="https://schemas.eremexcontrols.net/avalonia/treelist"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="EremexAvaloniaApplication10.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Icon="/Assets/EMXControls.ico"
Title="EremexAvaloniaApplication10"
xmlns:local="clr-namespace:EremexAvaloniaApplication10.Views"
>
<Design.DataContext>
<vm:MainWindowViewModel/>
</Design.DataContext>
<mx:MxWindow.Resources>
<local:MyTreeListChildrenSelector x:Key="mySelector" />
</mx:MxWindow.Resources>
<mxtl:TreeViewControl Name="treeView"
ItemsSource="{Binding Cities}"
DataFieldName="Name"
ChildrenSelector="{StaticResource mySelector}"
/>
</mx:MxWindow>
//MainWindow.axaml.cs
using Avalonia.Controls;
using Eremex.AvaloniaUI.Controls.Common;
using Eremex.AvaloniaUI.Controls.TreeList;
using EremexAvaloniaApplication10.ViewModels;
using System.Collections;
namespace EremexAvaloniaApplication10.Views
{
public partial class MainWindow : MxWindow
{
public MainWindow()
{
InitializeComponent();
}
}
public class MyTreeListChildrenSelector : ITreeListChildrenSelector
{
public bool HasChildren(object item)
{
if (item is City city)
return city.Streets.Count > 0;
if (item is Street street)
return street.Buildings.Count > 0;
return false;
}
public IEnumerable? SelectChildren(object item)
{
if (item is City city)
return city.Streets;
if (item is Street street)
return street.Buildings;
return null;
}
}
}
//MainWindowViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using DynamicData;
using System.Collections.ObjectModel;
using System.Security.Cryptography;
namespace EremexAvaloniaApplication10.ViewModels
{
public partial class MainWindowViewModel : ViewModelBase
{
[ObservableProperty]
ObservableCollection<City> cities = new();
public MainWindowViewModel()
{
Street s1 = new Street("Khaosan Rd", new string[] { "234", "278", "299" });
Street s2 = new Street("Nonsi Rd", new string[] { "678", "639" });
Street s3 = new Street("Airport Rd", new string[] { "324", "373" } );
Street s4 = new Street("Pipeline Rd", new string[] { "111", "185" });
City c1 = new City("Bangkok");
City c2 = new City("Mumbai");
c1.Streets.Add(s1);
c1.Streets.Add(s2);
c2.Streets.Add(s3);
c2.Streets.Add(s4);
cities.AddRange(new[] { c1, c2 });
}
}
public class City
{
public City(string name) { Name = name; }
public string Name { get; set; }
public ObservableCollection<Street> Streets = new();
}
public class Street
{
public Street(string name, string[] buildingNumbers) {
Name = name;
foreach (string number in buildingNumbers)
{
Buildings.Add(new Building(number));
}
}
public string Name { get; set; }
public ObservableCollection<Building> Buildings = new();
}
public class Building
{
public Building(string name) { Name = name; }
public string Name { get; set; }
}
}
Смотрите также¶
- Привязка данных
- Привязка к самореферентному источнику данных
- Столбцы
- Как создать контрол TreeList и привязать его к иерархическому источнику данных
* Эта страница была создана автоматически с помощью сервиса машинного перевода Яндекс Переводчик.