Мне кажется, что это довольно просто реализовать, если вы правильно проектируете модель представления.
Вы в основном проектируете элементы так же, как если бы отображали их в обычной сетке данных, т.е. каждый элементимеет свойство для каждого столбца.По всей вероятности, ваша базовая модель данных является иерархической, но коллекция, к которой привязана сетка, будет выровнена, то есть будет содержать элемент для каждого узла в иерархии независимо от отношений родитель / потомок.
Модель представления элемента имеет некоторые дополнительные свойства: Level
, Children
, IsExpanded
и IsVisible
.Level
- это число предков узла, Children
содержит дочерние узлы модели представления, IsExpanded
используется в пользовательском интерфейсе и IsVisible
- true, если узел видим.Он также реализует свойство с именем VisibleDescendants
:
public IEnumerable<NodeViewModel> VisibleDescendants
{
get
{
return Children
.Where(x => x.IsVisible)
.SelectMany(x => (new[] {x}).Concat(x.VisibleDescendants)));
}
}
Вы используете Level
, HasChildren
и IsExpanded
в стиле для элемента в первом столбце элемента управления: они контролируют левое полеи какой значок (если есть) отображается.
Вам также нужно реализовать свойства ExpandCommand
и CollapseCommand
.ExpandCommand
включается, если Children.Any()
- true, а IsExpanded
- false, а CollapseCommand
-, если Children.Any()
- true, а IsExpanded
- true.Эти команды, когда они выполняются, изменяют значение IsExpanded
.
И вот здесь это становится интересным.Простой способ реализовать это может сработать для вас: элементы предоставляются родительской моделью представления, свойство Items
которой не является коллекцией.Вместо этого это перечислитель, который перемещается по цепочке моделей дочерних представлений и выдает только видимые узлы:
public IEnumerable<NodeViewModel> Items
{
get
{
return _Items
.Where(x => x.IsVisible)
.SelectMany(x => (new[] {x}).Concat(x.VisibleDescendants));
}
}
Всякий раз, когда изменяется свойство IsVisible
любого потомка, модель родительского представления поднимает PropertyChanged
для Items
свойство, которое заставляет сетку данных заполняться.
Существует и менее простая реализация, где вы делаете свойство Items
классом, который реализует INotifyCollectionChanged
и вызывает соответствующие события CollectionChanged
когда узлы-потомки становятся видимыми / невидимыми, но вам нужно идти туда только в случае проблем с производительностью.