WPF 自定义TreeView控件 绑定子列表

来源:互联网 时间:1970-01-01

<pre name="code" class="csharp">

一、先看一下运行后的TreeView



备注:以上所有节点全部是后台添加的数据结构节点、不是控件。


二、TreeView样式  、我是自己封装成了控件、所以样式是针对数据结构定义的

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Local="clr-namespace:DialogEx.Controls" xmlns:Cvt="clr-namespace:DialogEx.Converters" > <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="../../Resource/Style/ScrollViewerEx.xaml"/> </ResourceDictionary.MergedDictionaries> <LinearGradientBrush x:Key="CheckBackground" StartPoint="0.5,0" EndPoint="0.5,1"> <GradientStop Color="#098ED4" Offset="0.100"/> <GradientStop Color="#0D83C7" Offset="0.500"/> <GradientStop Color="#176CAA" Offset="0.900"/> </LinearGradientBrush> <SolidColorBrush x:Key="MouseOverColor">#E5EEC4</SolidColorBrush> <SolidColorBrush x:Key="LostFocusColor">#E5EEC4</SolidColorBrush> <Style TargetType="ToggleButton" x:Key="ExpandButtonStyle"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ToggleButton"> <Border BorderThickness="0" Background="Transparent"> <Path Name="ExpandedPath" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5" Stroke="#A8A8A8" StrokeThickness="2"></Path> </Border> <ControlTemplate.Triggers> <Trigger Property="IsChecked" Value="False"> <Setter TargetName="ExpandedPath" Property="Data" Value="M0,5 L10,5 M5,0 L5,10"></Setter> </Trigger> <Trigger Property="IsChecked" Value="True"> <Setter TargetName="ExpandedPath" Property="Data" Value="M0,5 L10,5"></Setter> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style TargetType="TreeViewItem" x:Key="TreeViewItemStyle"> <!--是否展开--> <Setter Property="IsSelected" Value="{Binding IsSelected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></Setter> <Setter Property="IsExpanded" Value="{Binding IsExpanded,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></Setter> <!--样式--> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="TreeViewItem"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="30"></RowDefinition> <RowDefinition Height="auto"></RowDefinition> </Grid.RowDefinitions> <!--不能直接赋值Background 否则没效果--> <Border Name="Part_Border" BorderBrush="#E9E9E9" BorderThickness="1" CornerRadius="1" Panel.ZIndex="100" Margin="0,0.8,0,0.8"> <Border.Resources> <Style TargetType="Border"> <Setter Property="Background" Value="{Binding LevelColor}"></Setter> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="{DynamicResource MouseOverColor}"></Setter> </Trigger> </Style.Triggers> </Style> </Border.Resources> <StackPanel Orientation="Horizontal"> <ToggleButton Visibility="Hidden" Name="ExpandedPath" Style="{DynamicResource ExpandButtonStyle}" Height="23" Width="23" IsChecked="{Binding IsExpanded,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></ToggleButton> <TextBlock Margin="5,0,0,0" Name="DisPlayName" VerticalAlignment="Center" Text="{Binding DisplayName}"></TextBlock> </StackPanel> </Border> <StackPanel Name="Part_Host" IsItemsHost="True" Grid.Row="1" Margin="10,0.6,0,0.6"> </StackPanel> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsExpanded" Value="False"> <Setter TargetName="Part_Host" Property="Visibility" Value="Collapsed"></Setter> </Trigger> <Trigger Property="HasItems" Value="True"> <Setter TargetName="ExpandedPath" Property="Visibility" Value="Visible"></Setter> </Trigger> <!--这个触发器会触发父控件、及本子控件的效果--> <!--<Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="Part_Border" Property="Background" Value="{DynamicResource MouseOverColor}"></Setter> </Trigger>--> <Trigger Property="IsSelected" Value="True"> <Setter TargetName="Part_Border" Property="Background" Value="{DynamicResource CheckBackground}"></Setter> <Setter TargetName="DisPlayName" Property="Foreground" Value="White"></Setter> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style TargetType="TreeView" x:Key="TreeViewStyle"> <Setter Property="ItemContainerStyle" Value="{DynamicResource TreeViewItemStyle}"></Setter> <Setter Property="ItemTemplate"> <Setter.Value> <HierarchicalDataTemplate ItemsSource="{Binding Children}" ItemContainerStyle="{StaticResource TreeViewItemStyle}"> </HierarchicalDataTemplate> </Setter.Value> </Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type TreeView}"> <Grid> <Border x:Name="Border" Background="{TemplateBinding Background}" BorderBrush="{x:Null}" BorderThickness="0" CornerRadius="1,1,1,1"> <ScrollViewer Margin="0,2,0,2" Style="{StaticResource ScrollViewerStyle}" Focusable="False" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" CanContentScroll="False" Foreground="{DynamicResource {x:Static SystemColors.ActiveCaptionTextBrushKey}}"> <ItemsPresenter Margin="2" /> </ScrollViewer> </Border> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style></ResourceDictionary>


三、封装自定义控件加载TreeView样式 


using DialogEx.Classes;using System;using System.Collections.Generic;using System.Collections.ObjectModel;using System.ComponentModel;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows;using System.Windows.Controls;using System.Windows.Media;namespace DialogEx.Controls{ public class TreeViewEx : TreeView { #region 公共方法 //构造 public TreeViewEx() { Loaded += (sender, args) => { this.Resources = ResourceDictionaries.ResourceCollection_TreeViewEx; this.Style = this.Resources["TreeViewStyle"] as Style; }; } #endregion public class TreeViewExNode : INotifyPropertyChanged { #region private private string _DisplayName = ""; private bool _IsExpanded = false; private int _Level = 1; private string _ID; private TreeViewExNode _Parent; #endregion public event PropertyChangedEventHandler PropertyChanged; #region 公共属性 public string ID { get { return _ID; } set { this._ID = value; RaisePropertyChanged("ID"); } } public TreeViewExNode Parent { get { return this._Parent; } set { if (this._Parent != null) { throw new Exception("同一个节点只能指定一次父节点"); } else { this._Parent = value; } } } /// <summary> /// 展示名称 /// </summary> public string DisplayName { get { return this._DisplayName; } set { this._DisplayName = value; RaisePropertyChanged("DisplayName"); } } /// <summary> /// 是否展开 /// </summary> public bool IsExpanded { get { return this._IsExpanded; } set { if (this._IsExpanded != value) { this._IsExpanded = value; RaisePropertyChanged("IsExpanded"); } } } private bool _IsSelected; public bool IsSelected { get { return _IsSelected; } set { _IsSelected = value; } } /// <summary> /// 层级 >= 1 /// </summary> public int Level { get { return this._Level; } set { this._Level = value; } } /// <summary> /// Level颜色 /// </summary> public LinearGradientBrush LevelColor { get { LinearGradientBrush levelColor = null; switch (Level) { case 1: { levelColor = ResourceDictionaries.ResourceCollection_Colors["TreeViewExLevel01Color"] as LinearGradientBrush; } break; case 2: { levelColor = ResourceDictionaries.ResourceCollection_Colors["TreeViewExLevel02Color"] as LinearGradientBrush; } break; case 3: { levelColor = ResourceDictionaries.ResourceCollection_Colors["TreeViewExLevel03Color"] as LinearGradientBrush; } break; } return levelColor; } } /// <summary> /// 子列表 /// </summary> public ObservableCollection<TreeViewExNode> Children { get; set; } #endregion #region 公共方法 //构造 public TreeViewExNode() { Children = new ObservableCollection<TreeViewExNode>(); } //通知 private void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } #endregion }; }}

备注:其中有一个类ResourceDictionaries是个静态的资源字典,目的是无论新建多少该TreeView 实例始终加载同一个资源

ResourceDictionaries.ResourceCollection_TreeViewEx是这么定义的


/// <summary> /// 颜色字典 /// </summary> public static ResourceDictionary ResourceCollection_Colors { get { if (_ResourceCollection_Colors == null) { _ResourceCollection_Colors = new ResourceDictionary(); _ResourceCollection_Colors.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri("DialogEx;Component/Resource/Colors/Colors.xaml", UriKind.RelativeOrAbsolute) }); } return _ResourceCollection_Colors; } } /// <summary> /// TreeViewEx资源-定义样式加载器 /// </summary> public static ResourceDictionary ResourceCollection_TreeViewEx { get { if (_ResourceCollection_TreeViewEx == null) { _ResourceCollection_TreeViewEx = new ResourceDictionary(); _ResourceCollection_TreeViewEx.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri("DialogEx;Component/Controls/TreeViewEx/TreeViewEx.xaml", UriKind.RelativeOrAbsolute) }); } return _ResourceCollection_TreeViewEx; } }

颜色资源:层级颜色是通过 Level来设置的请看节点数据结构的代码、目前颜色字典里仅仅设置了前3级颜色。

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <!--TreeViewEx层级1颜色--> <LinearGradientBrush x:Key="TreeViewExLevel01Color" StartPoint="0.5,0" EndPoint="0.5,1"> <GradientStop Color="#FDFDFD" Offset="0.100"/> <GradientStop Color="#F4F4F4" Offset="0.500"/> <GradientStop Color="#EEEEEE" Offset="0.900"/> </LinearGradientBrush> <!--TreeViewEx层级2颜色--> <LinearGradientBrush x:Key="TreeViewExLevel02Color" StartPoint="0.5,0" EndPoint="0.5,1"> <GradientStop Color="#D1EDFF" Offset="0.100"/> <GradientStop Color="#D1EDFF" Offset="0.500"/> <GradientStop Color="#D1EDFF" Offset="0.900"/> </LinearGradientBrush> <!--TreeViewEx层级3颜色--> <LinearGradientBrush x:Key="TreeViewExLevel03Color" StartPoint="0.5,0" EndPoint="0.5,1"> <GradientStop Color="#26A0B0" Offset="0.100"/> <GradientStop Color="#2E8E9A" Offset="0.500"/> <GradientStop Color="#348189" Offset="0.900"/> </LinearGradientBrush></ResourceDictionary>


四、调用TreeView - 直接在调用窗口Window.xaml加载本控件

1、前台XAML

<Window x:Class="WPFPro.Views.TreeViewExView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="TreeViewExView" Height="600" Width="900" xmlns:TreeViewExSpace="clr-namespace:ExpControls.Conrols;assembly=ExpControls" > <Grid> <TreeViewExSpace:TreeViewEx ItemsSource="{Binding ModelList}"> </TreeViewExSpace:TreeViewEx> </Grid></Window>
2、后台CS

using ExpControls.Conrols;using System;using System.Collections.Generic;using System.Collections.ObjectModel;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.Shapes;namespace WPFPro.Views{    /// <summary>    /// TreeViewExView.xaml 的交互逻辑    /// </summary>    public partial class TreeViewExView : Window    {        public TreeViewExView()        {            InitializeComponent();            this.ModelList = new ObservableCollection<TreeViewEx.TreeViewExNode>();            TreeViewEx.TreeViewExNode Node = new TreeViewEx.TreeViewExNode()            {                IsExpanded = true,                Level = 1,                DisplayName = "节点1"            };            Node.Children.Add(new TreeViewEx.TreeViewExNode() { IsExpanded = false, Level = 2, DisplayName = "节点101" });            Node.Children.Add(new TreeViewEx.TreeViewExNode() { IsExpanded = false, Level = 2, DisplayName = "节点102" });            TreeViewEx.TreeViewExNode Node103 = new TreeViewEx.TreeViewExNode() { IsExpanded = false, Level = 2, DisplayName = "节点103" };            Node103.Children.Add(new TreeViewEx.TreeViewExNode() { IsExpanded = false, Level = 3, DisplayName = "节点103-001" });            Node.Children.Add(Node103);            ModelList.Add(Node);            Node = new TreeViewEx.TreeViewExNode()            {                IsExpanded = true,                Level = 1,                DisplayName = "节点2"            };            Node.Children.Add(new TreeViewEx.TreeViewExNode() { IsExpanded = false, Level = 2, DisplayName = "节点201" });            Node.Children.Add(new TreeViewEx.TreeViewExNode() { IsExpanded = false, Level = 2, DisplayName = "节点202" });            Node.Children.Add(new TreeViewEx.TreeViewExNode() { IsExpanded = false, Level = 2, DisplayName = "节点203" });            ModelList.Add(Node);            Node = new TreeViewEx.TreeViewExNode()            {                IsExpanded = true,                Level = 1,                DisplayName = "节点3"            };            ModelList.Add(Node);            this.DataContext = this;        }        public ObservableCollection<TreeViewEx.TreeViewExNode> ModelList { get; set; }    }        }

ModelList采用ObservableCollection、是为了实现动态插入删除节点而设计的。


MyControls 代码分享仅控件代码调用请看本文章




相关阅读:
Top