WPF 带CheckBox、图标的TreeView
在WPF实际项目开发的时候,经常会用到带CheckBox的TreeView,虽然微软在WPF的TreeView中没有提供该功能,但是微软在WPF中提供强大的ItemTemplate模板功能和自定义样式,那我们可以自己写一个这样的控件供自己使用。
我自己写的这个比较简单。
首先写一个供TreeView使用的数据模型,并且实现INotifyPropertyChanged接口,用于向客户端(通常是执行绑定的客户端)发出某一属性值已更改的通知,当属性改变时,相应的UI表现也改变。主要字段Id,Name,Icon,ToolTip,IsChecked,IsExpanded,Parent,Children
//*************************************************** // // 文件名(FileName) : TreeModel.cs // // 作者(Author) : zsm // // 创建时间(CreateAt): 2013-03-18 14:23:58 // // 描述(Description) : 供TreeView实用的数据模型 // //*************************************************** using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel;namespace Com.FMS.Model {public class TreeModel : INotifyPropertyChanged{#region 私有变量/// <summary>/// Id值/// </summary>private string _id;/// <summary>/// 显示的名称/// </summary>private string _name;/// <summary>/// 图标路径/// </summary>private string _icon;/// <summary>/// 选中状态/// </summary>private bool _isChecked;/// <summary>/// 折叠状态/// </summary>private bool _isExpanded;/// <summary>/// 子项/// </summary>private IList<TreeModel> _children;/// <summary>/// 父项/// </summary>private TreeModel _parent;#endregion /// <summary>/// 构造/// </summary>public TreeModel(){Children = new List<TreeModel>();_isChecked = false;IsExpanded = false;_icon = "/Images/16_16/folder_go.png";}/// <summary>/// 键值/// </summary>public string Id{get { return _id; }set { _id = value; }}/// <summary>/// 显示的字符/// </summary>public string Name{get { return _name; }set { _name = value; }}/// <summary>/// 图标/// </summary>public string Icon{get { return _icon; }set { _icon = value; }}/// <summary>/// 指针悬停时的显示说明/// </summary>public string ToolTip {get {return String.Format("{0}-{1}", Id, Name);}}/// <summary>/// 是否选中/// </summary>public bool IsChecked{get{return _isChecked;}set{if (value != _isChecked){_isChecked = value;NotifyPropertyChanged("IsChecked");if (_isChecked){//如果选中则父项也应该选中if (Parent != null){Parent.IsChecked = true;}}else{//如果取消选中子项也应该取消选中foreach (TreeModel child in Children){child.IsChecked = false;}}}}}/// <summary>/// 是否展开/// </summary>public bool IsExpanded{get { return _isExpanded; }set{if (value != _isExpanded){//折叠状态改变_isExpanded = value;NotifyPropertyChanged("IsExpanded");}}}/// <summary>/// 父项/// </summary>public TreeModel Parent{get { return _parent; }set { _parent = value; }}/// <summary>/// 子项/// </summary>public IList<TreeModel> Children{get { return _children; }set { _children = value; }}/// <summary>/// 设置所有子项的选中状态/// </summary>/// <param name="isChecked"></param>public void SetChildrenChecked(bool isChecked){foreach (TreeModel child in Children){child.IsChecked = IsChecked;child.SetChildrenChecked(IsChecked);}}/// <summary>/// 设置所有子项展开状态/// </summary>/// <param name="isExpanded"></param>public void SetChildrenExpanded(bool isExpanded) {foreach (TreeModel child in Children){child.IsExpanded = isExpanded;child.SetChildrenExpanded(isExpanded);}}/// <summary>/// 属性改变事件/// </summary>public event PropertyChangedEventHandler PropertyChanged;private void NotifyPropertyChanged(String info){if (PropertyChanged != null){PropertyChanged(this, new PropertyChangedEventArgs(info));}}} }
创建一个用户控件,主要含有一个TreeView控件,ContextMenu右键菜单项,UI代码如下(其中的路径请根据实际修改):
<UserControl x:Class="Com.FMS.View.UserControls.ZsmTreeView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Com.FMS.Model"mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"><Grid><DockPanel><Border DockPanel.Dock="Bottom"><StackPanel Orientation="Horizontal" ToolTip="右键有更多功能哦!"><Image Height="16" Width="16" Source="Images/16_16/emoticon_smile.png"></Image><Label Content="右键有更多功能哦!" Foreground="Gray"></Label></StackPanel></Border><Border><TreeView Name="tvZsmTree"><TreeView.ContextMenu><ContextMenu><MenuItem Name="menuExpandAll" Header="全部展开" Click="menuExpandAll_Click"><MenuItem.Icon><Image Source="/Com.FMS;component/Images/16_16/folder_open_arrow.png" /></MenuItem.Icon></MenuItem><MenuItem Name="menuUnExpandAll" Header="全部折叠" Click="menuUnExpandAll_Click"><MenuItem.Icon><Image Source="/Com.FMS;component/Images/16_16/folder_close_arrow.png" /></MenuItem.Icon></MenuItem><MenuItem Name="menuSelectAll" Header="全部选中" Click="menuSelectAll_Click"><MenuItem.Icon><Image Source="/Com.FMS;component/Images/16_16/tick.png" /></MenuItem.Icon></MenuItem><MenuItem Name="menuUnSelectAll" Header="全部取消" Click="menuUnSelectAll_Click"><MenuItem.Icon><Image Source="/Com.FMS;component/Images/16_16/delete.png" /></MenuItem.Icon></MenuItem></ContextMenu></TreeView.ContextMenu><TreeView.ItemContainerStyle><Style TargetType="TreeViewItem"><Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"></Setter><EventSetter Event="TreeViewItem.PreviewMouseRightButtonDown" Handler="TreeViewItem_PreviewMouseRightButtonDown"/></Style></TreeView.ItemContainerStyle><TreeView.ItemTemplate><HierarchicalDataTemplate DataType="{x:Type local:TreeModel}" ItemsSource="{Binding Children}"><StackPanel Margin="-2,0,0,0" Orientation="Horizontal" x:Name="staTree"><CheckBox ToolTip="{Binding ToolTip}" FontSize="14" FontFamily="微软雅黑" Tag="{Binding Children}" IsChecked="{Binding IsChecked, Mode=TwoWay}"><StackPanel Orientation="Horizontal"><Image VerticalAlignment="Center" Source="{Binding Icon}" ></Image><TextBlock Text="{Binding Name}"></TextBlock></StackPanel><CheckBox.ContextMenu><ContextMenu><MenuItem Name="menuSelectAllChild" Header="全部选中子项" Click="menuSelectAllChild_Click"><MenuItem.Icon><Image Source="/Com.FMS;component/Images/16_16/tick.png" /></MenuItem.Icon></MenuItem></ContextMenu></CheckBox.ContextMenu></CheckBox></StackPanel><HierarchicalDataTemplate.Triggers><DataTrigger Binding="{Binding IsChecked}" Value="true"><Setter TargetName="staTree" Property="Background" Value="White"/></DataTrigger></HierarchicalDataTemplate.Triggers></HierarchicalDataTemplate></TreeView.ItemTemplate></TreeView></Border></DockPanel></Grid> </UserControl>
交互逻辑的代码中,现在主要有控件数据ItemsSourceData属性,设置对应Id的项为选中状态SetCheckedById、忽略层次关系的情况下获取选中项CheckedItemsIgnoreRelation等方法,以及右键的选中所有子项菜单、全部选中、全部取消选中、全部折叠、全部展开等事件,交互逻辑代码为:
//*************************************************** // // 文件名(FileName) : ZsmTreeView.xaml.cs // // 作者(Author) : zsm // // 创建时间(CreateAt): 2013-03-15 16:52:40 // // 描述(Description) : 带CheckBox的TreeView控件的交互逻辑代码 // //*************************************************** using System; using System.Collections.Generic; using System.Linq; using System.Text; 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 Com.FMS.View.UserControls {/// <summary>/// ZsmTreeView.xaml 的交互逻辑/// </summary>public partial class ZsmTreeView : UserControl{#region 私有变量属性/// <summary>/// 控件数据/// </summary>private IList<Model.TreeModel> _itemsSourceData;#endregion/// <summary>/// 构造/// </summary>public ZsmTreeView(){InitializeComponent();}/// <summary>/// 控件数据/// </summary>public IList<Model.TreeModel> ItemsSourceData {get { return _itemsSourceData; }set{_itemsSourceData = value;tvZsmTree.ItemsSource = _itemsSourceData;}}/// <summary>/// 设置对应Id的项为选中状态/// </summary>/// <param name="id"></param>/// <returns></returns>public int SetCheckedById(string id, IList<Model.TreeModel> treeList) {foreach (var tree in treeList){if (tree.Id.Equals(id)){tree.IsChecked = true;return 1;}if (SetCheckedById(id, tree.Children) == 1){return 1;}}return 0;}/// <summary>/// 设置对应Id的项为选中状态/// </summary>/// <param name="id"></param>/// <returns></returns>public int SetCheckedById(string id){foreach (var tree in ItemsSourceData){if (tree.Id.Equals(id)){tree.IsChecked = true;return 1;}if (SetCheckedById(id, tree.Children) == 1){return 1;}}return 0;}/// <summary>/// 获取选中项/// </summary>/// <returns></returns>public IList<Model.TreeModel> CheckedItemsIgnoreRelation(){return GetCheckedItemsIgnoreRelation(_itemsSourceData);}/// <summary>/// 私有方法,忽略层次关系的情况下,获取选中项/// </summary>/// <param name="list"></param>/// <returns></returns>private IList<Model.TreeModel> GetCheckedItemsIgnoreRelation(IList<Model.TreeModel> list){IList<Model.TreeModel> treeList = new List<Model.TreeModel>();foreach (var tree in list){if (tree.IsChecked){treeList.Add(tree);}foreach (var child in GetCheckedItemsIgnoreRelation(tree.Children)){treeList.Add(child);}}return treeList;}/// <summary>/// 选中所有子项菜单事件/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void menuSelectAllChild_Click(object sender, RoutedEventArgs e){if (tvZsmTree.SelectedItem != null){Model.TreeModel tree = (Model.TreeModel)tvZsmTree.SelectedItem;tree.IsChecked = true;tree.SetChildrenChecked(true);}}/// <summary>/// 全部展开菜单事件/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void menuExpandAll_Click(object sender, RoutedEventArgs e){foreach (Model.TreeModel tree in tvZsmTree.ItemsSource){tree.IsExpanded = true;tree.SetChildrenExpanded(true);}}/// <summary>/// 全部折叠菜单事件/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void menuUnExpandAll_Click(object sender, RoutedEventArgs e){foreach (Model.TreeModel tree in tvZsmTree.ItemsSource){tree.IsExpanded = false;tree.SetChildrenExpanded(false);}}/// <summary>/// 全部选中事件/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void menuSelectAll_Click(object sender, RoutedEventArgs e){foreach (Model.TreeModel tree in tvZsmTree.ItemsSource){tree.IsChecked = true;tree.SetChildrenChecked(true);}}/// <summary>/// 全部取消选中/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void menuUnSelectAll_Click(object sender, RoutedEventArgs e){foreach (Model.TreeModel tree in tvZsmTree.ItemsSource){tree.IsChecked = false;tree.SetChildrenChecked(false);}}/// <summary>/// 鼠标右键事件/// </summary>/// <param name="sender"></param>/// <param name="e"></param>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;}} }
在使用控件的时候,要在xaml中引入命名控件(根据实际引入)
xmlns:my="clr-namespace:Com.FMS.View.UserControls"
<!--使用控件-->
<my:ZsmTreeView x:Name="ztvModule" />
ztvModule.ItemsSourceData = treeList;//treeList为IList<TreeModel>类型
本文转自 http://www.cnblogs.com/zsmhhfy/archive/2013/03/18/2965755.html