Реализация вью-модели дерева с элементами различного типа для TreeView в MVVM

Рейтинг: 1Ответов: 1Опубликовано: 12.04.2015

Считаю очень трудным реализовать Tree в MVVM, но худо бедно полазив по форумам смог реализовать часть:

MainWindowVM.cs

class MainWindowVM
{
    public List<TreeVM> Trees { get; set; }

    public MainWindowVM()
    {            
        Trees = new List<TreeVM>()
        {
            new TreeVM("МОУО/ОУО")
        };
    }
}

TreeVM.cs

class TreeVM
{
    public List<GovernmentVM> Governments { get; set; }

    public string TreeName { get; set; }

    public TreeVM(string treeName)
    {
        TreeName = treeName;
        switch (TreeName)
        {
            case "МОУО/ОУО":
                {
                    using (cokoEntities dataContext = new cokoEntities())
                    {
                        Governments = new List<GovernmentVM>();
                        var _govers = from o in dataContext.governments select o;
                        foreach (var gover in _govers)
                        {
                            Governments.Add(new GovernmentVM(gover));
                        }
                    }
                    break;
                }                

        }

    }
}

GovernmentVM.cs

class GovernmentVM
{
    public government Gover { get; set; }

    public GovernmentVM(government gover)
    {
        Gover = gover;
    }

}

Иерархию установил таким образом:

<TreeView ItemsSource="{Binding Trees}">
    <TreeView.Resources>
        <HierarchicalDataTemplate ItemsSource="{Binding Governments}" DataType="{x:Type this:TreeVM}">
            <Label Content="{Binding TreeName}"/>
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Schools}" DataType="{x:Type this:GovernmentVM}">
            <Label Content="{Binding Gover.GoverName}"/>
        </HierarchicalDataTemplate>
    </TreeView.Resources>
</TreeView>

В итоге получил пока все как хотел:

Т.е. как наверное поняли - в последнем слой дерева помещается объект government из базы данных.

Но теперь мне необходимо добавить в корень TreeView еще один объект, объект area другого типа из этой же базы данных.

Если начать с файла MainWindowVM, то будет следующее дополнение:

MainWindowVM.cs

class MainWindowVM
{
    public List<TreeVM> Trees { get; set; }

    public MainWindowVM()
    {            
        Trees = new List<TreeVM>()
        {
            new TreeVM("МОУО/ОУО"),
            new TreeVM("Районы") //новая ветка
        };
    }
}

Как дополнить проект?

Ответы

▲ 3Принят

Несколько замечаний:

  1. Сначала следует построить осмысленную вью-модель, а потом уже делать свойства под конкретные контролы, чтобы упростить байндинги. Если у вас есть список районов — не надо хранить его где-то в дереве, просто положите его в корневую вью-модель. Иначе вы потом с ума сойдёте от кода для доступа к нужным элементам.

  2. Свойства для контролов не обязаны быть строго типизированными. Если вы скажете, что в списке объекты типа object, то контрол списка разберётся, какие элементы как отобразить.

  3. Label для простого вывода текста избыточен, достаточно TextBlock.

  4. Коллекции следует делать ObservableCollection<>, а не List<>. Даже если дерево один раз читается и больше не обновляется, будет меньше ограничений, в какой момент наполнять коллекцию, чтобы всё верно отобразилось.

А теперь собственно решение. Учитывая, что у вас несколько корней деревьев, в каждом корне объекты одного своего типа, то я бы создал один общий тип TreeRoot, для которого можно задать надпись, и в который можно положить элементы любого типа. Списки бы оставил на верхнем уровне. Для дерева создал бы список из корней, и в каждый корень передал бы нужные списки.

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

MainWindow.xaml.cs

using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace WpfApp
{
    public class Shell
    {
        public ObservableCollection<Government> Governments { get; private set; }
        public ObservableCollection<Area> Areas { get; private set; }
        public ObservableCollection<TreeRoot> TreeRoots { get; private set; }

        public Shell ()
        {
            Governments = new ObservableCollection<Government> {
                new Government { GovernmentName = "Gov 1" },
                new Government { GovernmentName = "Gov 2" },
                new Government { GovernmentName = "Gov 3" },
            };
            Areas = new ObservableCollection<Area> {
                new Area { AreaName = "Area 1" },
                new Area { AreaName = "Area 2" },
                new Area { AreaName = "Area 3" },
            };
            TreeRoots = new ObservableCollection<TreeRoot> {
                new TreeRoot("Governments", Governments),
                new TreeRoot("Areas", Areas),
            };
        }
    }

    public class TreeRoot
    {
        public string RootName { get; set; }
        public IEnumerable<object> Items { get; private set; }

        public TreeRoot (string rootName, IEnumerable<object> items)
        {
            RootName = rootName;
            Items = items;
        }
    }

    public class Government
    {
        public string GovernmentName { get; set; }
    }

    public class Area
    {
        public string AreaName { get; set; }
    }

    public partial class MainWindow
    {
        public MainWindow ()
        {
            DataContext = new Shell();
            InitializeComponent();
        }
    }
}

MainWindow.xaml

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
        xmlns:local="clr-namespace:WpfApp"
        d:DataContext="{d:DesignInstance local:Shell}">
    <Control.Resources>
        <HierarchicalDataTemplate DataType="{x:Type local:TreeRoot}" ItemsSource="{Binding Items}">
            <TextBlock Text="{Binding RootName}"/>
        </HierarchicalDataTemplate>
        <DataTemplate DataType="{x:Type local:Government}">
            <TextBlock Text="{Binding GovernmentName}"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:Area}">
            <TextBlock Text="{Binding AreaName}"/>
        </DataTemplate>
    </Control.Resources>
    <TreeView ItemsSource="{Binding TreeRoots}"/>
</Window>

P.S. Я также добавил блендовские свойства, чтобы редактор видел свойства. Их можно выкинуть.