在充分尊重WPF MVVM的情况下,在每个节点中创建具有不同子对象的TreeView

本文关键字:创建 TreeView 对象 节点 WPF 情况下 MVVM | 更新日期: 2023-09-27 18:11:23

我想在完全尊重Wpf MVVM:的情况下创建如下树视图

根//0级

B1-儿童1//1级

B1-1-儿童1-1//2级

B1-2-儿童1-2

B1-3-。。。

B2-Child2/Level1

B2-1儿童2-1//2级

B2-2儿童2-2

B2-3。。。

正如你所看到的,我有三级树视图。

0级:根(始终修复(

级别1:两个孩子(总是修复(

级别2:动态子级,它们是从两个不同的类创建的

所以我的问题是如何在级别2中为级别1中的每个节点创建不同的子级。

我使用了下面的代码,但在一级中,我的孩子总是和父母一样。

我已经看了这个网站上以及网上提出的许多解决方案。。。但就是不知道怎么做…

我的尝试:

  public class MyViewModel 
{
    //private ReadOnlyCollection<AttributesMapDocViewModel> _attributeMapDoc;
    public object _document;
    #region Methodes


    private List<Level0ViewModel> _myDoc;
    public List<Level0ViewModel> MyDoc
    {
        get { return _myDoc; }
        set
        {
            _myDoc = value;
        }
    }
    #endregion
    #region Constructeur
    public MyViewModel()
    {

        MyDoc = new List<Level0ViewModel>()
        {
            new Level0ViewModel("Root",_document), //_document conatins data from xml file (code not shown)
        };
    }

    #endregion
}
public class Level0ViewModel : ViewModelBase
{
    private List<Level1ViewModel> _childLevel1;
    public Level0ViewModel(string name, object myObj)
    {
        ChildLeve0Name = name;
        ChildLevel1 = new List<Level1ViewModel>()
        {
            new Level1ViewModel("Child1",myObj),
            new Level1ViewModel("Child2",myObj)
        };
    }
    public List<Level1ViewModel> ChildLevel1
    {
        get
        {
            return _childLevel1;
        }
        set
        {
            SetProperty(ref _childLevel1, value, () => ChildLevel1);
        }
    }
    public string ChildLeve0Name { get; set; }
}
public class Level1ViewModel : ViewModelBase
{
    private ObservableCollection<Level2SecondTypeViewModel> _childLevel2SecondType;
    public ObservableCollection<Level2SecondTypeViewModel> ChildLevel2SecondType
    {
        get { return _childLevel2SecondType; }
        set
        {
            if (_childLevel2SecondType != value)
            {
                SetProperty(ref _childLevel2SecondType, value, () => ChildLevel2SecondType);
            }
        }
    }

    private ObservableCollection<Level2FirstTypeViewModel> _childLevel2FirstType;
    public ObservableCollection<Level2FirstTypeViewModel> ChildLevel2FirstType
    {
        get { return _childLevel2FirstType; }
        set
        {
            if (_childLevel2FirstType != value)
            {
                SetProperty(ref _childLevel2FirstType, value, () => ChildLevel2FirstType);
            }
        }
    }
    public Level1ViewModel(string name, object mapAtt)
    {
        ChildLevel1Name = name;
        ChildLevel2FirstType = new ObservableCollection<Level2FirstTypeViewModel>();
        foreach (FirstType myFirstType in mapAtt.FirstTypes)
        {
            ChildLevel2FirstType.Add(new Level2FirstTypeViewModel(myFirstType));
        }
        ChildLevel2SecondType = new ObservableCollection<Level2SecondTypeViewModel>();
        foreach (SecondType mySecondType in mapAtt.SecondTypes)
        {
            ChildLevel2SecondType.Add(new Level2SecondTypeViewModel(mySecondType));
        }
    }
    public string ChildLevel1Name
    {
        get;
        set;
    }
}
public class Level2FirstTypeViewModel : ViewModelBase
{
    public Level2FirstTypeViewModel(FirstType fType)
    {
        FirstTypeName = fType.name;
    }
    public string FirstTypeName
    {
        get;
        set;
    }
}

public class Level2SecondTypeViewModel
{
    public Level2SecondTypeViewModel(SecondType sType)
    {
        SecondTypeName = sType.name;
    }
    public string SecondTypeName
    {
        get;
        set;
    }
}


 <TreeView ItemsSource="{Binding MyDoc}" >
  <TreeView.Resources>
            <HierarchicalDataTemplate ItemsSource="{Binding MapAttDef}" DataType="{x:Type local:Level0ViewModel}">
                      <Label Content="{Binding ChildLeve0Name}"/>
            </HierarchicalDataTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding ChildLevel2SecondType}" DataType="{x:Type local:Level1ViewModel}">
                      <Label Content="{Binding ChildLevel1Name}"/>
            </HierarchicalDataTemplate>
            <HierarchicalDataTemplate  DataType="{x:Type local:Level2SecondTypeViewModel}">
                       <Label Content="{Binding FirstTypeName}"/>
            </HierarchicalDataTemplate>
            <HierarchicalDataTemplate  DataType="{x:Type local:Level2FirstTypeViewModel}">
                        <Label Content="{Binding SecondTypeName}"/>
            </HierarchicalDataTemplate>


   </TreeView.Resources>

我的尝试给了我这样的东西(这根本不是我想要的!!(:

根//0级

B1-儿童1//1级

B1-1-儿童1-1//2级

B1-2-儿童1-2

B1-3-。。。

B2-Child2/Level1

B2-1儿童1-1/2级

B2-2儿童1-2

B2-3。。。

在充分尊重WPF MVVM的情况下,在每个节点中创建具有不同子对象的TreeView

放弃这些属性:Level1ViewModel.ChildLevel2FirstTypeLevel1ViewModel.ChildLevel2SecondType

你需要为孩子们单独收集。此集合的项类型取决于继承层次结构,并且应该是最近的公共祖先(最坏的情况是object(,也就是说,如下所示:

public Level1ViewModel
{
    public ObservableCollection<object> ChildLevel2
    {
        // ...
    }
    // ...
}

带有这些更改的标记将如下所示:

    <!-- Level 1 -->
    <HierarchicalDataTemplate DataType="{x:Type local:Level1ViewModel}" ItemsSource="{Binding ChildLevel2}">
              <Label Content="{Binding ChildLevel1Name}"/>
    </HierarchicalDataTemplate>
    <!-- Level 1 -->
    <DataTemplate  DataType="{x:Type local:Level2FirstTypeViewModel}">
                <Label Content="{Binding SecondTypeName}"/>
    </DataTemplate>
    <DataTemplate  DataType="{x:Type local:Level2SecondTypeViewModel}">
               <Label Content="{Binding FirstTypeName}"/>
    </DataTemplate>

请注意,HierarchicalDataTemplate只能用于分层数据源。对于非分层的(如Level2FirstTypeViewModel和CCD_ 6(使用常规CCD_。

public class BaseTreeViewItem<TSource, TParent> : ViewModelBase
{
    // add static field to hold selected Item
    public static BaseTreeViewItem<TSource, TParent> SelectedChild { get; set; }
    private bool _isSelected;
    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            if (_isSelected == value) return;
            if (SelectedChild != null)
            {
                SelectedChild.IsSelected = false;
                SelectedChild = null;
            }
            _isSelected = value;
            if (_isSelected)
                SelectedChild = this;
            // NotifyPropertyChanged
        }
    }
    public TParent Parent
    {
        get;
        private set;
    }
    public BaseTreeViewItem(string name, TSource myObj, TParent parent)
    {
        ChildName = name;
        DataSource = myObj;
        Parent = parent;
    }

    public string ChildName { get; set; }
    public TSource DataSource { get; set; }
    public override string ToString()
    {
        return ChildName;
    }
}
// Node
public class MyTreeViewItem<TChild, TSource, TParent> : BaseTreeViewItem<TSource, TParent>, IDeleteItem
    where TParent : IDeleteItem
    where TChild : class
{

    public ObservableCollection<TChild> Children { get; set; }
    public MyTreeViewItem(string name, TSource myObj, TParent parent)
        :base(name,myObj, parent)
    {
        Children = new ObservableCollection<TChild>();
    }
    protected virtual void InitChild()
    {
    }
    public void DeleteItem(object myTreeViewItem)
    {
        Children.Remove(myTreeViewItem as TChild);
    }
    public static void DeleteSelectedItem()
    {
        if (SelectedChild != null && SelectedChild.Parent != null)
        {
            SelectedChild.Parent.DeleteItem(SelectedChild);
        }
    }

}
public class Level0ViewModel : MyTreeViewItem<Level1ViewModel, XmlDocument, IDeleteItem>
{
    protected override sealed void InitChild()
    {
        base.InitChild();
        Children.Add(new Level1ViewModel("Child1", new Level1Src(), this));
        Children.Add(new Level1ViewModel("Child2", new Level1Src(), this));
    }
    public Level0ViewModel(string name, XmlDocument myObj) :
        base(name, myObj,null)
    {
        InitChild();
    }
}
public class Level1ViewModel : MyTreeViewItem<Level2TypeViewModel, Level1Src, Level0ViewModel>
{
    public Level1ViewModel(string name, Level1Src myObj, Level0ViewModel parent)
        : base(name, myObj, parent)
    {
        InitChild();
    }
    protected override sealed void InitChild()
    {
        base.InitChild();
        foreach (FirstType myFirstType in DataSource.FirstTypes)
        {
            Children.Add(new Level2TypeViewModel(myFirstType, this));
        }
        foreach (SecondType mySecondType in DataSource.SecondTypes)
        {
            Children.Add(new Level2TypeViewModel(mySecondType, this));
        }
    }
    // Use linq if tou want child by type
    public IEnumerable<Level2TypeViewModel> ChildType1 
    {
        get
        {
            return Children.Where(item => item.DataSource is FirstType);
        }
    }
    public IEnumerable<Level2TypeViewModel> ChildType2
    {
        get
        {
            return Children.Where(item => item.DataSource is SecondType);
        }
    }
}
public class LevelType
{
    public string Name;
}
public class FirstType : LevelType
{
}
public class SecondType : LevelType
{
}
public class Level2TypeViewModel : BaseTreeViewItem<LevelType, Level1ViewModel>
{
    public Level2TypeViewModel(LevelType sType, Level1ViewModel parent)
        : base(sType.Name, sType, parent)
    {
    }
}

我的数据源是

public Level0ViewModel TreeModel
    {
        get
        {
            return new Level0ViewModel("Root", new XmlDocument());
        }
    }

对于TreeView数据模板,请使用

<TreeView DataContext="{Binding ElementName=UI, Path=TreeModel}">
        <TreeView.Items>
            <TreeViewItem Header="{Binding ChildName}" ItemsSource="{Binding Path=Children}" >
                <TreeViewItem.ItemTemplate>
                    <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                        <Label Content="{Binding ChildName}"></Label>
                    </HierarchicalDataTemplate>
                </TreeViewItem.ItemTemplate>
            </TreeViewItem>
        </TreeView.Items>
    </TreeView>

第一个treeviewItem用于显示根节点。如果你不想使用它您可以将数据源更改为

public MyTreeViewItem<Level0ViewModel,object> TreeModel
        {
            get
            {
                MyTreeViewItem<Level0ViewModel,object> src = new MyTreeViewItem<Level0ViewModel, object>("Root", null);
                src.Children.Add(new Level0ViewModel("toto", new XmlDocument()));
                return src;
            }
        }

和数据模板到

<TreeView DataContext="{Binding ElementName=UI, Path=TreeModel}" ItemsSource="{Binding Children}">
            <TreeViewItem.ItemTemplate>
                        <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                            <Label Content="{Binding ChildName}"></Label>
                        </HierarchicalDataTemplate>
                    </TreeViewItem.ItemTemplate>
 </TreeView>

样本方式

public class TreeViewItemViewModel : ViewModelBase
{
    // add static field to hold selected Item
    public static TreeViewItemViewModel SelectedChild { get; set; }
    public TreeViewItemViewModel Parent{ get; private set;}
    public string ChildName { get; set; }
    public object DataSource { get; set; }
    private readonly ObservableCollection<TreeViewItemViewModel> _children;
    public ObservableCollection<TreeViewItemViewModel> Children
    {
        get
        {
            return _children;
        }
    }

    private bool _isSelected;
    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            if (_isSelected == value) return;
            if (SelectedChild != null)
            {
                SelectedChild.IsSelected = false;
                SelectedChild = null;
            }
            _isSelected = value;
            if (_isSelected)
                SelectedChild = this;
            // NotifyPropertyChanged
        }
    }

    public TreeViewItemViewModel(string name, object myObj, TreeViewItemViewModel parent)
    {
        ChildName = name;
        DataSource = myObj;
        Parent = parent;
        _children = new ObservableCollection<TreeViewItemViewModel>();
    }
    public override string ToString()
    {
        return ChildName;
    }

    protected virtual void InitChild()
    {
    }
    public void DeleteItem(TreeViewItemViewModel myTreeViewItem)
    {
        Children.Remove(myTreeViewItem);
    }
    public static void DeleteSelectedItem()
    {
        if (SelectedChild != null && SelectedChild.Parent != null)
        {
            SelectedChild.Parent.DeleteItem(SelectedChild);
        }
    }
}

如果你的视图模型中有一个选定的项目,你可以使用这种方法

公共类TreeViewItemViewModel:ViewModelBase{

    // add static field to hold selected Item
    public static TreeViewItemViewModel SelectedChild { get; set; }
    public TreeViewItemViewModel Parent{ get; private set;}
    public string ChildName { get; set; }
    public object DataSource { get; set; }
    private readonly ObservableCollection<TreeViewItemViewModel> _children;
    public ObservableCollection<TreeViewItemViewModel> Children
    {
        get
        {
            return _children;
        }
    }

    private bool _isSelected;
    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            if (_isSelected == value) return;
            if (SelectedChild != null)
            {
                SelectedChild.IsSelected = false;
                SelectedChild = null;
            }
            _isSelected = value;
            if (_isSelected)
                SelectedChild = this;
            // NotifyPropertyChanged
        }
    }

    public TreeViewItemViewModel(string name, object myObj, TreeViewItemViewModel parent)
    {
        ChildName = name;
        DataSource = myObj;
        Parent = parent;
        _children = new ObservableCollection<TreeViewItemViewModel>();
    }
    public override string ToString()
    {
        return ChildName;
    }

    protected virtual void InitChild()
    {
    }
    public void DeleteItem(TreeViewItemViewModel myTreeViewItem)
    {
        Children.Remove(myTreeViewItem);
    }
    public static void DeleteSelectedItem()
    {
        if (SelectedChild != null && SelectedChild.Parent != null)
        {
            SelectedChild.Parent.DeleteItem(SelectedChild);
        }
    }
}