TreeView边学边总结
这是我第一次写博客。先介绍下情况吧,我现在是大三,专业是计算机,在一家公司实习,因为大学里三年都是混过来的,基本同龄的都知道,所以专业没学好,很差。但是现在又要面临实习,毕业找工作,幸好有认识的在一家公司上班,可以让我进去实习,语言c#。而我大学大一学c++,大二java,大三Android,c#是选修的,我没有学过。其他语言又很烂,水平跟大一进来一样,三年基本没编程过,计算机专业的应该知道这类人是什么情况,实验什么的都是靠班里学习好的人分享。说实话我很幸运,能有这样一个实习机会。可以说我是从头开始学c#。跟很多初次接触编程人一样,很迷惑,很苦恼,也有点后悔。废话不说了,开始总结下这几天学到的TreeView。
刚入门c#,所以比较基础,老鸟不要嘲笑新手。
TreeView有2种方法,一种是直接在代码里添加Node,这种方法我没有具体了解过,只是看了下,大概知道是一种硬编码。我学习的是另外一种,数据绑定的方法。
我写贴上自己写的代码,然后对代码进行解释。
MainWindow.xaml文件
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525" xmlns:local="clr-namespace:WpfApplication1"> <Window.Resources> <ContextMenu x:Key="ss"> </ContextMenu> </Window.Resources> <Grid> <TreeView SelectedItemChanged="treeview_SelectedItemChanged_1" Name="treeview" HorizontalAlignment="Left" Height="154" Margin="24,32,0,0" VerticalAlignment="Top" Width="112"> <TreeView.ContextMenu> <ContextMenu> <MenuItem Name="menuExpand" Header="全部展开" Click="menuExpand_Click_1"/> <MenuItem Name="menuUnExpand" Header="全部折叠" Click="menuUnExpand_Click_1" /> <MenuItem Name="menuSelect" Header="全部选中" Click="menuSelect_Click_1"/> <MenuItem Name="menuCancel" Header="全部取消" Click="menuCancel_Click_1"/> <MenuItem Name="adeMenuNode" Header="增加节点" Click="addMenuNode_Click_1"/> <MenuItem Name="DeleteNode" Header="删除节点" Click="DeleteNode_Click_2"/> </ContextMenu> </TreeView.ContextMenu> <TreeView.ItemContainerStyle> <Style TargetType="TreeViewItem"> <Setter Property="IsExpanded" Value="{Binding isExpand, Mode=TwoWay}"/> <EventSetter Event="TreeViewItem.PreviewMouseRightButtonDown" Handler="TreeViewItem_PreviewMouseRightButtonDown"/> </Style> </TreeView.ItemContainerStyle> <TreeView.ItemTemplate> <HierarchicalDataTemplate DataType="{x:Type local:PropertyNodeItem}" ItemsSource="{Binding Path=Children}"> <StackPanel Name="stackPanel" Orientation="Horizontal"> <Image VerticalAlignment="Center" Source="{Binding ICON}"/> <CheckBox ToolTip="{Binding ToolTip}" IsChecked="{Binding isChecked, Mode=TwoWay}" Tag="{Binding}" Checked="CheckBox_Checked_1"/> <TextBlock VerticalAlignment="Center" Text="{Binding DisplayName}"/> <Image VerticalAlignment="Center" Source="{Binding EditIcon}"/> <StackPanel.ToolTip> <TextBlock VerticalAlignment="Center" Text="{Binding Name}"/> </StackPanel.ToolTip> </StackPanel> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> <Button Content="展开下一级" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="10,262,0,0" Click="Button_Click_1"/> <Button Content="全部展开" HorizontalAlignment="Left" Margin="131,262,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click_2" RenderTransformOrigin="0.5,0.5"/> <Button Content="全部折叠" HorizontalAlignment="Left" Margin="260,262,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click_3"/> <Label Content="加入节点名称" HorizontalAlignment="Left" Margin="24,211,0,0" VerticalAlignment="Top" Width="90"/> <TextBox HorizontalAlignment="Left" Height="23" Margin="108,214,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120" RenderTransformOrigin="0.146,0.533"/> </Grid> </Window>
<Window.Resources>
<ContextMenu x:Key="ss">
</ContextMenu>
</Window.Resources>
这部分不用理会,只是windows的资源引用,以后wpf样式,右键菜单选项什么的都可以放到这里。
<TreeView
<TreeView SelectedItemChanged="treeview_SelectedItemChanged_1" Name="treeview" HorizontalAlignment="Left" Height="154" Margin="24,32,0,0" VerticalAlignment="Top" Width="112"> <TreeView.ContextMenu> <ContextMenu> <MenuItem Name="menuExpand" Header="全部展开" Click="menuExpand_Click_1"/> <MenuItem Name="menuUnExpand" Header="全部折叠" Click="menuUnExpand_Click_1" /> <MenuItem Name="menuSelect" Header="全部选中" Click="menuSelect_Click_1"/> <MenuItem Name="menuCancel" Header="全部取消" Click="menuCancel_Click_1"/> <MenuItem Name="adeMenuNode" Header="增加节点" Click="addMenuNode_Click_1"/> <MenuItem Name="DeleteNode" Header="删除节点" Click="DeleteNode_Click_2"/> </ContextMenu> </TreeView.ContextMenu>
第一行设置了个SelectedItemChanged="treeview_SelectedItemChanged_1" 一个事件,后台代码没有写过,要写的可以自己增加,其他属性应该看的懂。
<TreeView.ContextMenu>l里面的很多菜单项是我后台代码对树进行的一些操作。
<TreeView.ItemContainerStyle> <Style TargetType="TreeViewItem"> <Setter Property="IsExpanded" Value="{Binding isExpand, Mode=TwoWay}"/> <EventSetter Event="TreeViewItem.PreviewMouseRightButtonDown" Handler="TreeViewItem_PreviewMouseRightButtonDown"/> </Style> </TreeView.ItemContainerStyle>
这段代码是为TreeView设置了个ItemContainerStyle,里面的IsExpand属性跟后台的isExpand进行了绑定,Mode是TwoWay方式,下面的事件是鼠标右击事件,不用管。
<TreeView.ItemTemplate> <HierarchicalDataTemplate DataType="{x:Type local:PropertyNodeItem}" ItemsSource="{Binding Path=Children}"> <StackPanel Name="stackPanel" Orientation="Horizontal"> <Image VerticalAlignment="Center" Source="{Binding ICON}"/> <CheckBox ToolTip="{Binding ToolTip}" IsChecked="{Binding isChecked, Mode=TwoWay}" Tag="{Binding}" Checked="CheckBox_Checked_1"/> <TextBlock VerticalAlignment="Center" Text="{Binding DisplayName}"/> <StackPanel.ToolTip> <TextBlock VerticalAlignment="Center" Text="{Binding Name}"/> </StackPanel.ToolTip> </StackPanel> </HierarchicalDataTemplate> </TreeView.ItemTemplate>
这是关键代码,DataType后面跟的是后台你定义的和TreeView绑定的类 类名字叫PropertyNodeItem Children是PropertyNodeItem的一个属性,但他是public List<PropertyNodeItem> Children { get; set; }
也就是你定义类型的一个List,当作数据源绑定给树。
在树里你可以给树设置Image,checkbox,textblock,然后跟你定义的PropertyNodeItem里的对应属性进行绑定,ToolTip是在鼠标移到textblock上会显示text绑定的name属性值。
下面是PropertyNodeItem的定义
public class PropertyNodeItem
{
//private bool _isExpand;
public string ICON { get; set; }
public string EditIcon { get; set; }
public string DisplayName { get; set; }
public string Name { get; set; }
public bool isChecked { get; set; }
public bool isExpand { get; set; }
public List<PropertyNodeItem> Children { get; set; }
public PropertyNodeItem()
{
Children = new List<PropertyNodeItem>();
}
}
我在贴出后台代码
MainWindow.xamls.cs文件
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WpfApplication1 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { int NodeOfSLayer; public List<PropertyNodeItem> ItemList = new List<PropertyNodeItem>(); int i = 1; public MainWindow() { InitializeComponent(); ShowTreeView(); } private void ShowTreeView() { PropertyNodeItem node1 = new PropertyNodeItem() { DisplayName="根节点", Name="yzf1", isExpand = false, }; PropertyNodeItem node1child1 = new PropertyNodeItem() { DisplayName="子节点1", Name="yzf2", isExpand=false, // ICON =" Chrysanthemum.jpg" }; PropertyNodeItem node1child1child1 = new PropertyNodeItem() { DisplayName = "子节点1_1", Name = "yzf7", isExpand = false, // ICON =" Chrysanthemum.jpg" }; PropertyNodeItem node1child2 = new PropertyNodeItem() { DisplayName = "子节点2", Name = "yzf3", isExpand = false, // ICON =" Chrysanthemum.jpg" }; node1child1.Children.Add(node1child1child1); node1.Children.Add(node1child1); node1.Children.Add(node1child2); NodeOfSLayer += 2; PropertyNodeItem node2 = new PropertyNodeItem() { DisplayName = "根节点", Name = "yzf4", isExpand = false, }; PropertyNodeItem node2child1 = new PropertyNodeItem() { DisplayName = "子节点1", Name = "yzf5", isExpand = false, // ICON =" Chrysanthemum.jpg" }; PropertyNodeItem node2child2 = new PropertyNodeItem() { DisplayName = "子节点2", Name = "yzf6", isExpand = false, // ICON =" Chrysanthemum.jpg" }; node2.Children.Add(node2child1); node2.Children.Add(node2child2); ItemList.Add(node1); ItemList.Add(node2); NodeOfSLayer += 1; this.treeview.ItemsSource = ItemList; } private void AddChildNode() { // TreeViewItem } private void AddParentNode() { } private void Button_Click_1(object sender, RoutedEventArgs e) { } private void Button_Click_2(object sender, RoutedEventArgs e) { } private void Button_Click_3(object sender, RoutedEventArgs e) { } private void menuExpand_Click_1(object sender, RoutedEventArgs e) { List<PropertyNodeItem> list =ItemList; if(e.Source!=null&&treeview.SelectedItem!=null) { SetNodeExpanded(list,true); //list.Add(tree); } ItemList = list; treeview.ItemsSource = null; treeview.ItemsSource = ItemList; } private void menuUnExpand_Click_1(object sender, RoutedEventArgs e) { List<PropertyNodeItem> list = ItemList; if (e.Source != null && treeview.SelectedItem != null) { SetNodeExpanded(list, false); //list.Add(tree); } ItemList = list; treeview.ItemsSource = null; treeview.ItemsSource = ItemList; } private void menuSelect_Click_1(object sender, RoutedEventArgs e) { List<PropertyNodeItem> list = ItemList; if(e.Source!=null) { if(list!=null&&list.Count>0) { SetNodeChecked(list,true); } } ItemList = list; treeview.ItemsSource = null; treeview.ItemsSource = ItemList; } private void menuCancel_Click_1(object sender, RoutedEventArgs e) { List<PropertyNodeItem> list = ItemList; if (e.Source != null) { if (list != null && list.Count > 0) { SetNodeChecked(list, false); } } ItemList = list; treeview.ItemsSource = null; treeview.ItemsSource = ItemList; } private void TreeViewItem_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) { TreeViewItem item = VisualUpwardSearch<TreeViewItem>(e.OriginalSource as DependencyObject) as TreeViewItem; if (item != null) { item.Focus(); e.Handled = true; } } static DependencyObject VisualUpwardSearch<T>(DependencyObject source) { while (source != null && source.GetType() != typeof(T)) source = VisualTreeHelper.GetParent(source); return source; } //private void menuExpandNode_Click_1(object sender, RoutedEventArgs e) //{ // PropertyNodeItem selected_item = treeview.SelectedItem as PropertyNodeItem; // List<PropertyNodeItem> list = new List<PropertyNodeItem>(); // // PropertyNodeItem modelTag = (PropertyNodeItem)(((CheckBox)e.Source).Tag); // list = ItemList; // if (selected_item != null) // { // // PropertyNodeItem item = GetTree(list, selected_item.DisplayName);完全用不到GetTree函数,他返回的就是selected_item // SetCrruntNodeExpanded(list, true); // } // else // { // MessageBox.Show("请选择一个节点"); // } // ItemList = list; // treeview.ItemsSource = null; // treeview.ItemsSource = ItemList; //} private void treeview_SelectedItemChanged_1(object sender, RoutedPropertyChangedEventArgs<object> e) { } private void addMenuNode_Click_1(object sender, RoutedEventArgs e) { PropertyNodeItem selected_item = treeview.SelectedItem as PropertyNodeItem; List<PropertyNodeItem> list = new List<PropertyNodeItem>(); list=ItemList; PropertyNodeItem node1 = new PropertyNodeItem() { DisplayName = "增加的节点", Name = "addNode", isExpand = false, }; if(selected_item!=null&&list!=null) { AddChildrenNode(list, node1,selected_item); } ItemList = list; treeview.ItemsSource = null; treeview.ItemsSource = ItemList; } private void AddChildrenNode(List<PropertyNodeItem> list,PropertyNodeItem node,PropertyNodeItem selected_item) { foreach (PropertyNodeItem item in list) { if (item == selected_item) { item.Children.Add(node); } if(item.Children!=null) { AddChildrenNode(item.Children, node, selected_item); } } } private PropertyNodeItem SearchNode( List< PropertyNodeItem> ParentList, PropertyNodeItem SelectItem) { if (ParentList != null && ParentList.Count > 0) { foreach (PropertyNodeItem parentModel in ParentList) { if (parentModel.Children != null && parentModel.Children.Count > 0) { SearchNode(parentModel.Children, SelectItem); } if (parentModel == SelectItem) { return parentModel; } } } return null; } private void SetNodeExpanded(List<PropertyNodeItem> itemlist,bool expand) { if (itemlist != null && itemlist.Count > 0) { foreach (PropertyNodeItem item in itemlist) { item.isExpand = expand; if(item.Children!=null&&item.Children.Count>0) { SetNodeExpanded(item.Children,expand); } } } } private void SetNodeChecked(List<PropertyNodeItem> itemlist, bool check) { if (itemlist != null && itemlist.Count > 0) { foreach (PropertyNodeItem item in itemlist) { item.isChecked = check; if (item.Children != null && item.Children.Count > 0) { SetNodeChecked(item.Children, check); } } } } //private void SetCrruntNodeExpanded(List<PropertyNodeItem> itemlist,bool expand) //{ // if(itemlist!=null&&itemlist.Count>0) // { // foreach(PropertyNodeItem item in itemlist) // { // if (i == 0) // { // i = 1; // return; // } // else // { // item.isExpand = expand; // if(item.Children!=null&&item.Children.Count>0) // { // i = i - 1; // SetCrruntNodeExpanded(item.Children,expand); // } // } // } // } //} private PropertyNodeItem GetTree(List<PropertyNodeItem> list,string displayname) { if (list != null && list.Count > 0) { for(int i=0;i<list.Count;i++) { if(list[i].DisplayName==displayname) { return list[i]; } if(list[i].Children!=null&&list[i].Children.Count>0) { PropertyNodeItem returnNodeItem=GetTree(list[i].Children,displayname); if(returnNodeItem!=null) { return returnNodeItem; } } } } return null; } private void DeleteNode_Click_2(object sender, RoutedEventArgs e) { //在下面的RemoveNode函数里 要注意的是方法里遍历treeview的时候要用for循环,而不能用foreach, //因为foreach是只读操作,不允许删除,更新、添加没有问题,所以在上面的AddChildrenNode函数里确可以用foreach。 PropertyNodeItem selected_item = treeview.SelectedItem as PropertyNodeItem; List<PropertyNodeItem> list = new List<PropertyNodeItem>(); list = ItemList; if (selected_item.DisplayName == "根节点") { MessageBox.Show("根节点不允许删除"); } else { if (selected_item != null) { RemoveNode(list, selected_item); } ItemList = list; treeview.ItemsSource = null; treeview.ItemsSource = ItemList; } } private void RemoveNode(List<PropertyNodeItem> list,PropertyNodeItem selectItem) { for (int i = 0; i < list.Count;i++) { if (list[i] == selectItem) { list.Remove(list[i]); return; } if (list[i].Children != null) { RemoveNode(list[i].Children, selectItem); } } } private void CheckBox_Checked_1(object sender, RoutedEventArgs e) { // PropertyNodeItem modelTag = (PropertyNodeItem)(((CheckBox)e.Source).Tag); } } public class PropertyNodeItem { private bool _isExpand; public string ICON { get; set; } public string EditIcon { get; set; } public string DisplayName { get; set; } public string Name { get; set; } public bool isChecked { get; set; } public bool isExpand { get; set; } public List<PropertyNodeItem> Children { get; set; } public PropertyNodeItem() { Children = new List<PropertyNodeItem>(); } } }
里面具体的一些菜单事件的函数先可以不用看。
private void ShowTreeView()和定义的类PropertyNodeItem就可以呈现一个简单的树了,Image属性我没有给他赋值,大家可以自己赋值。在项目里引用一些图片,然后我上面注释的代码
// ICON =" Chrysanthemum.jpg"
这句话就是用来设置图片的
运行后样子是这样的
这些菜单的事件下次再讲。菜单事件是可以运行起来的,有兴趣的可以自己跑起来看看。