博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【WPF】树形结构TreeView的用法(MVVM)
阅读量:6833 次
发布时间:2019-06-26

本文共 12562 字,大约阅读时间需要 41 分钟。

TreeView控件的用法还是有蛮多坑点的,最好记录一下。

参考项目:


静态的树形结构

如果树形结构的所有子节点都已经确定且不会改动,可以直接在控制层用C#代码来生成这个TreeView。

var rootItem = new OutlineTreeData            {                outlineTypeName = "David Weatherbeam",                Children=                {                    new OutlineTreeData                    {                        outlineTypeName="Alberto Weatherbeam",                        Children=                        {                            new OutlineTreeData                            {                                outlineTypeName="Zena Hairmonger",                                Children=                                {                                    new OutlineTreeData                                    {                                        outlineTypeName="Sarah Applifunk",                                    }                                }                            },new OutlineTreeData                            {                                outlineTypeName="Jenny van Machoqueen",                                Children=                                {                                    new OutlineTreeData                                    {                                        outlineTypeName="Nick van Machoqueen",                                    },                                    new OutlineTreeData                                    {                                        outlineTypeName="Matilda Porcupinicus",                                    },                                    new OutlineTreeData                                    {                                        outlineTypeName="Bronco van Machoqueen",                                    }                                }                            }                        }                    },                    new OutlineTreeData                    {                        outlineTypeName="Komrade Winkleford",                        Children=                        {                            new OutlineTreeData                            {                                outlineTypeName="Maurice Winkleford",                                Children=                                {                                    new OutlineTreeData                                    {                                        outlineTypeName="Divinity W. Llamafoot",                                    }                                }                            },                            new OutlineTreeData                            {                                outlineTypeName="Komrade Winkleford, Jr.",                                Children=                                {                                    new OutlineTreeData                                    {                                        outlineTypeName="Saratoga Z. Crankentoe",                                    },                                    new OutlineTreeData                                    {                                        outlineTypeName="Excaliber Winkleford",                                    }                                }                            }                        }                    }                }            };

运行后能看到树形结构是下面的样子。


获取TreeViewItem控件

前台页面xaml:

尝试过在初始化时获取TreeViewItem,发现都是为Null。

  • TreeViewItem item= (TreeViewItem)(myWindow.treeView.ItemContainerGenerator.ContainerFromIndex(0)); // 无法获取,为Null!
  • VisualTreeHelper.GetChild(); // 无法获取,为Null!

谷歌一下,看到,下面这位跟我遇到的情况一样,用以上方法都无法获取TreeViewItem。

不过他给出的答案是通过点击来获取到被选中的TreeViewItem。


给TreeView默认选中一个TreeViewItem

上面的办法通过点击TreeViewItem来从事件中获得这个控件,但是如果我们想生成TreeView后不靠手动点击,立马自动选中一个默认的TreeViewItem呢?注意此时控件还未渲染,通过ItemContainerGenerator无法获取到控件。

此时只能考虑使用MVVM的绑定机制来获取控件!因为如果修改数据后,UI的更新是延迟的,无法立刻获取到前台控件。

绑定:

xaml中TreeView的ItemsSource绑定到ViewModel中的ItemTreeDataList列表,该列表中的元素类型是自定义ItemTreeData实体类。在样式中绑定好IsExpandedIsSelected属性TreeViewItem模板是绑定到Children属性,该属性在ItemTreeData实体类中。

ViewModel:

// Item的树形结构private ObservableCollection
itemTreeDataList;public ObservableCollection
ItemTreeDataList{ get { return itemTreeDataList; } set { SetProperty(ref itemTreeDataList, value); }}

ItemTreeData实体类

public class ItemTreeData // 自定义Item的树形结构    {        public int itemId { get; set; }      // ID        public string itemName { get; set; } // 名称        public int itemStep { get; set; }    // 所属的层级        public int itemParent { get; set; }  // 父级的ID        private ObservableCollection
_children = new ObservableCollection
(); public ObservableCollection
Children { // 树形结构的下一级列表 get { return _children; } } public bool IsExpanded { get; set; } // 节点是否展开 public bool IsSelected { get; set; } // 节点是否选中 }

关于上面的层级/父级/下一级的概念,假设现在树形结构为以下结构,从上往下依此定义为根节点、零级节点、一级节点、二级节点。

/**  rootItem*      |----zeroTreeItem*                 |----firstTreeItem*                            |----secondTreeItem*/

控制层在生成TreeView时通过绑定的属性来设置默认选中的Item,预先将三个层级的Item分别装在不同的列表中使用。

现在尝试把二级节点中的第一个Item作为默认选中的Item。关键代码如下:

// 构造轮廓选择的树形结构        private void InitTreeView()        {            // 添加树形结构            ItemTreeData item = GetTreeData();            myViewModel.ItemTreeDataList.Clear();            myViewModel.ItemTreeDataList.Add(item);        }        // 构造树形结构        private ItemTreeData GetTreeData()        {            /*             *  rootItem             *      |----zeroTreeItem             *                 |----firstTreeItem             *                            |----secondTreeItem             */            // 根节点            ItemTreeData rootItem = new ItemTreeData();            rootItem.itemId = -1;            rootItem.itemName = " -- 请选择轮廓 -- ";            rootItem.itemStep = -1;            rootItem.itemParent = -1;            rootItem.IsExpanded = true; // 根节点默认展开            rootItem.IsSelected = false;            for (int i = 0; i < itemViewModel.ZeroStepList.Count; i++) // 零级分类            {                Items zeroStepItem = itemViewModel.ZeroStepList[i];                ItemTreeData zeroTreeItem = new ItemTreeData();                zeroTreeItem.itemId = zeroStepItem.itemId;                zeroTreeItem.itemName = zeroStepItem.itemName;                zeroTreeItem.itemStep = zeroStepItem.itemSteps;                zeroTreeItem.itemParent = zeroStepItem.itemParent;                if (i == 0)                {                    zeroTreeItem.IsExpanded = true; // 只有需要默认选中的第一个零级分类是展开的                }                zeroTreeItem.IsSelected = false;                rootItem.Children.Add(zeroTreeItem); // 零级节点无条件加入根节点                for (int j = 0; j < itemViewModel.FirstStepList.Count; j++) // 一级分类                {                    Items firstStepItem = itemViewModel.FirstStepList[j];                    if (firstStepItem.itemParent == zeroStepItem.itemId) //零级节点添加一级节点                    {                        ItemTreeData firstTreeItem = new ItemTreeData();                        firstTreeItem.itemId = firstStepItem.itemId;                        firstTreeItem.itemName = firstStepItem.itemName;                        firstTreeItem.itemStep = firstStepItem.itemSteps;                        firstTreeItem.itemParent = firstStepItem.itemParent;                        if (j == 0)                        {                            firstTreeItem.IsExpanded = true; // 只有需要默认选中的第一个一级分类是展开的                        }                        firstTreeItem.IsSelected = false;                        zeroTreeItem.Children.Add(firstTreeItem);                        for (int k = 0; k < itemViewModel.SecondStepList.Count; k++) // 二级分类                        {                            Items secondStepItem = itemViewModel.SecondStepList[k];                            if (secondStepItem.itemParent == firstStepItem.itemId) // 一级节点添加二级节点                            {                                ItemTreeData secondTreeItem = new ItemTreeData();                                secondTreeItem.itemId = secondStepItem.itemId;                                secondTreeItem.itemName = secondStepItem.itemName;                                secondTreeItem.itemStep = secondStepItem.itemSteps;                                secondTreeItem.itemParent = secondStepItem.itemParent;                                if (k == 0)                                {                                    // 默认选中第一个二级分类                                    secondTreeItem.IsExpanded = true; // 已经没有下一级了,这个属性无所谓                                    secondTreeItem.IsSelected = true;                                }                                firstTreeItem.Children.Add(secondTreeItem);                            }                        }                    }                }            }            return rootItem;        }

通过初始化时给被绑定的属性赋值,使得TreeView默认选中了二级节点中的第一个TreeViewItem,效果如下图:

注意点:

  • 只是修改目标节点的IsSelected = true还不够,还要把它的所有父节点(祖父节点)都设置IsExpanded = true才行!!!

显示选中TreeViewItem的完整分类路径

需求是如上图,要得到字符串"I户型_1.5-1_a2",即通过下划线连接得到“零级节点_一级节点_二级节点”,然后用一个TextBlock控件显示出来。注意这里不包含root根节点。

该功能可以写在树形结构的选项改变事件中。因为初始化TreeView时就选中了其中一个Item,所以初始化时也会调用到选项改变事件。

// 树形结构的选项改变事件        private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs e)        {            ItemTreeData selectedItem = (ItemTreeData)(myWindow.treeView.SelectedItem);            if (selectedItem != null)            {                // UI层找不到,只能在数据层找                List
nameList = new List
(); int step = selectedItem.itemStep; // 可能值为:-1,0,1,2 int parentId = selectedItem.itemParent; // 临时保存遍历中每次使用的Id for (int i = 0; i < step; i++) // 零级分类虽然有父节点root,但是数据层中没有相应的itemParent值(为-1) { ItemTreeData parent = this.GetTreeDataById(parentId); if (parent != null) { nameList.Add(parent.itemName); parentId = parent.itemParent; } } // 组拼字符串 = 零级名称 + 一级名称 + 二级名称 string text = ""; for (int i = nameList.Count; i > 0; i--) // 倒序遍历 { if (i == nameList.Count) { text = nameList[i - 1]; } else { text = text + "_" + nameList[i - 1]; } } if (string.IsNullOrEmpty(text)) { myWindow.itemSelectedTB.Text = selectedItem.itemName; } else { myWindow.itemSelectedTB.Text = text + "_" + selectedItem.itemName; } } } // 根据Id,获得树形结构中指定的Item private ItemTreeData GetTreeDataById(int targetId) { ItemTreeData data = null; // 是否为根节点 ItemTreeData root = myViewModel.ItemTreeDataList[0]; if (root.itemId == targetId) { data = root; return data; } // 搜索零级大类 foreach (ItemTreeData zeroStepData in root.Children) { if (zeroStepData.itemId == targetId) { data = zeroStepData; return data; } // 搜索一级分类 foreach (ItemTreeData firstStepData in zeroStepData.Children) { if (firstStepData.itemId == targetId) { data = firstStepData; return data; } // 搜索二级分类 foreach (ItemTreeData secondStepData in firstStepData.Children) { if (secondStepData.itemId == targetId) { data = secondStepData; return data; } } } } return data; }

注意点:

  • 同样是找不到TreeViewItem控件!UI层找不到,所以只能在数据层找它关联的ItemTreeData对象,从数据层中去获取itemName属性值。
  • 因为每一级ItemTreeData对象中记录了它的父级ID,所以往根节点方向遍历父节点、祖父节点时,先加入到List中的是上一级的节点名。而需求是“零级节点_一级节点_二级节点”的顺序,所以在使用List时需要倒序遍历!

最后显示的完整分类如下:

 

转载地址:http://uxtkl.baihongyu.com/

你可能感兴趣的文章
WCF 中 TCP 与 HTTP 性能简单比较
查看>>
04 企业的结构
查看>>
FlipViewDemo
查看>>
* 与 ** 在调用函数时的作用
查看>>
大数据团队必须设置的五种职位
查看>>
POJ 3345 Bribing FIPA 树形DP
查看>>
在COM组件中调用JavaScript函数
查看>>
archlinux使用sudo
查看>>
Hibernate 一对一映射(惟一外键)
查看>>
Spring + iBatis 的多库横向切分简易解决思路
查看>>
PS拾色器(前景色背景色)快捷键
查看>>
Composer帮你轻松管理PHP包 autoload
查看>>
poj 2914(stoer_wanger算法求全局最小割)
查看>>
搭建交叉编译环境
查看>>
linux下tar压缩/解压的使用(tar) 压缩/解压
查看>>
菜单each+hover
查看>>
乐观锁和悲观锁【转】
查看>>
抵制长假,呼唤年假!
查看>>
Linux的安装
查看>>
修復 “Failed to bring up eth0″ in Ubuntu virtualbox
查看>>