WPF TreeView递归异步绑定

WPF的TreeView不像WinForms的TreeView那样,通过TreeNode实现树型节点。WPF的TreeView实现起来,我感觉有点像VM(View-Model)模式,就像WinForms中的DataGrid,控件负责显示,数据在DataTable中。那么我们在实现TreeView时,也需要用这样的思路,数据在Model中,控件是View负责显示。

我需要实现的最终目标是:
1.节点是异步加载的(解决效率问题)
2.节点是无限级别的(递归的,这样可复用与目录结构、组织架构等数据类型)
3.节点是可以多选的(用CheckBox选择)
4.XAML方式绑定(减少UI上Code量)
5.方便得获取选中项信息

先看效果图:


1)数据库设计与准备
为了演示方便,采用了ACCESS数据库,添加一个Department表
表字段:

数据内容:

2)创建WPF项目


3)数据访问类
为了图方便,我采用了Linq to DataSet

 

Code:
  1. using System;   
  2. using System.Collections.Generic;   
  3. using System.Linq;   
  4. using System.Text;   
  5. using DepartmentTreeView.DB;   
  6. using System.Collections;   
  7. using DepartmentTreeView.DB.SampleDataSetTableAdapters;   
  8.   
  9. namespace DepartmentTreeView {   
  10.     public class DepartmentHelper {   
  11.   
  12.         static SampleDataSet ds = new SampleDataSet();   
  13.         static DepartmentTableAdapter da = new DepartmentTableAdapter();   
  14.   
  15.         static DepartmentHelper() {   
  16.             da.Fill(ds.Department);   
  17.         }   
  18.   
  19.         public static object GetDepartment(int did) {   
  20.             var depart = ds.Department.Where(c => c.DID == did).SingleOrDefault();   
  21.             return depart;   
  22.         }   
  23.   
  24.         public static IEnumerable GetSubDepartments(int pid) {   
  25.             var list = ds.Department.Where(c => c.PID == pid).ToList();   
  26.             return list;   
  27.         }   
  28.     }   
  29. }   

4)创建TreeView 的Model类DepartmentViewModel

Code:
  1. using System;   
  2. using System.Collections.Generic;   
  3. using System.Linq;   
  4. using System.Text;   
  5. using System.ComponentModel;   
  6. using System.Collections.ObjectModel;   
  7.   
  8. namespace DepartmentTreeView {   
  9.     public class DepartmentViewModel:INotifyPropertyChanged {   
  10.         //临时子节点用,当Expanded时移除此节点,添加子节点   
  11.         static readonly DepartmentViewModel _temp = new DepartmentViewModel(null);   
  12.         //选中的子节点   
  13.         private static ObservableCollection<DepartmentViewModel> _checkedItems = new ObservableCollection<DepartmentViewModel>();   
  14.         //根节点   
  15.         static DepartmentViewModel _rootItem;  
  16.  
  17.         #region fields&properties   
  18.         private bool? _isChecked;   
  19.         public bool? IsChecked {   
  20.             get { return _isChecked; }   
  21.             set {   
  22.                 SetCheckState(value, truetrue);   
  23.                    
  24.             }   
  25.         }   
  26.         private void SetCheckState(bool? value, bool updateChildren, bool updateParent) {   
  27.             if (_isChecked != value) {   
  28.                 _isChecked = value;   
  29.   
  30.                 //通知选中项的集合   
  31.                 if (_isChecked == true) {   
  32.                     _checkedItems.Add(this);   
  33.                     PropertyChanged(thisnew PropertyChangedEventArgs("CheckedItems"));   
  34.                 } else if (_isChecked == false) {   
  35.                     _checkedItems.Remove(this);   
  36.                     PropertyChanged(thisnew PropertyChangedEventArgs("CheckedItems"));   
  37.                 }   
  38.   
  39.                 PropertyChanged(thisnew PropertyChangedEventArgs("IsChecked"));   
  40.   
  41.                 if (updateChildren) {   
  42.                     if (HasChildren()) {   
  43.                         Children.ForEach(c => c.SetCheckState(value, truefalse));   
  44.                     }   
  45.                 }   
  46.                 if (updateParent && _parent != null) {   
  47.                     _parent.VerifyState();   
  48.                 }   
  49.             }   
  50.         }   
  51.         private void VerifyState() {   
  52.             bool? state = null;   
  53.             for (int i = 0; i < this.Children.Count; ++i) {   
  54.                 bool? currentState = this.Children[i].IsChecked;   
  55.                 if (i == 0) {   
  56.                     state = currentState;   
  57.                 } else if (state != currentState) {   
  58.                     state = null;   
  59.                     break;   
  60.                 }   
  61.             }   
  62.             this.SetCheckState(state, falsetrue);   
  63.         }   
  64.   
  65.         private bool _isExpanded;   
  66.         public bool IsExpanded {   
  67.             get { return _isExpanded; }   
  68.             set {   
  69.                 if (value != _isExpanded) {   
  70.                     _isExpanded = value;   
  71.                     PropertyChanged(thisnew PropertyChangedEventArgs("IsExpanded"));   
  72.                 }   
  73.                 if (!HasChildren()) {   
  74.                     Children.Remove(_temp);   
  75.                     LoadChildren();   
  76.                 }   
  77.             }   
  78.         }   
  79.   
  80.         private object _current;   
  81.         public object Current {   
  82.             get { return _current; }   
  83.             set { _current = value; }   
  84.         }   
  85.         public string DisplayText {   
  86.             get { return ((DepartmentTreeView.DB.SampleDataSet.DepartmentRow)Current)["DName"].ToString(); }   
  87.         }   
  88.   
  89.         private DepartmentViewModel _parent;   
  90.         public DepartmentViewModel Parent {   
  91.             get { return _parent; }   
  92.             set { _parent = value; }   
  93.         }   
  94.   
  95.         private List<DepartmentViewModel> _children;   
  96.         public List<DepartmentViewModel> Children {   
  97.             get { return _children; }   
  98.             private set { _children = value; }   
  99.         }   
  100.         #endregion   
  101.   
  102.         public static List<DepartmentViewModel> Create() {   
  103.             var list = DepartmentHelper.GetSubDepartments(0);   
  104.             DepartmentViewModel root = new DepartmentViewModel(null);   
  105.             _rootItem = root;   
  106.             root.Children.Clear();   
  107.             foreach (var item in list) {   
  108.                 root.Children.Add(new DepartmentViewModel(item));   
  109.             }   
  110.             return root.Children;   
  111.         }   
  112.         private DepartmentViewModel(object currentObject) {   
  113.             Current = currentObject;   
  114.             _isChecked = false;   
  115.             Children = new List<DepartmentViewModel>();   
  116.             Children.Add(_temp);   
  117.         }   
  118.   
  119.         /// <summary>   
  120.         /// 初始化,用于设置父节点   
  121.         /// </summary>   
  122.         private void Init() {   
  123.             if (!HasChildren()) return;   
  124.             foreach (DepartmentViewModel child in Children) {   
  125.                 child.Parent = this;   
  126.                 child.Init();   
  127.             }   
  128.             PropertyChanged(thisnew PropertyChangedEventArgs("Children"));   
  129.         }   
  130.   
  131.         /// <summary>   
  132.         /// 加载子节点   
  133.         /// </summary>   
  134.         private void LoadChildren() {   
  135.             if (Current != null) {   
  136.                 int pid = Convert.ToInt32(((DepartmentTreeView.DB.SampleDataSet.DepartmentRow)Current)["DID"]);   
  137.                 var list = DepartmentHelper.GetSubDepartments(pid);   
  138.                 foreach (var item in list) {   
  139.                     DepartmentViewModel model = new DepartmentViewModel(item) { _isChecked = this.IsChecked };   
  140.                     if (model.IsChecked == true) {   
  141.                         _checkedItems.Add(model);   
  142.                         PropertyChanged(thisnew PropertyChangedEventArgs("CheckedItems"));   
  143.                     }   
  144.                     Children.Add(model);   
  145.                 }   
  146.                 Init();   
  147.             }   
  148.         }   
  149.   
  150.         /// <summary>   
  151.         /// 判断是否有子节点(逻辑是:如果只有一个临时子节点,说明没有真正的子节点)   
  152.         /// </summary>   
  153.         /// <returns></returns>   
  154.         private bool HasChildren() {   
  155.             return !(Children.Count == 1 && Children[0] == _temp);   
  156.         }   
  157.   
  158.         public ObservableCollection<DepartmentViewModel> CheckedItems {   
  159.             get {   
  160.                 return _checkedItems;   
  161.             }   
  162.         }   
  163.   
  164.         public event PropertyChangedEventHandler PropertyChanged;   
  165.     }   
  166. }   

5)创建WPF窗体

Code:
  1. <Window x:Class="DepartmentTreeView.MainWindow"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         xmlns:local="clr-namespace:DepartmentTreeView"  
  5.         Title="MainWindow" Height="329" Width="212" FontFamily="Arial">  
  6.     <Window.Resources>  
  7.         <ObjectDataProvider x:Key="depProvider" ObjectType="{x:Type local:DepartmentViewModel}" MethodName="Create" />  
  8.         <Style x:Key="TreeViewItemStyle" TargetType="{x:Type TreeViewItem}">  
  9.             <Setter Property="IsExpanded" Value="{Binding Path=IsExpanded,Mode=TwoWay}" />  
  10.         </Style>  
  11.         <HierarchicalDataTemplate x:Key="CheckBoxItemTemplate" ItemsSource="{Binding Children}">  
  12.             <StackPanel Orientation="Horizontal" Margin="0,2,0,0">  
  13.                 <CheckBox Focusable="False" IsChecked="{Binding IsChecked,Mode=TwoWay}" VerticalAlignment="Center" />  
  14.                 <ContentPresenter Content="{Binding DisplayText,Mode=OneWay}" Margin="2,0" />  
  15.             </StackPanel>  
  16.         </HierarchicalDataTemplate>  
  17.     </Window.Resources>  
  18.     <Grid Margin="5">  
  19.         <Grid.RowDefinitions>  
  20.             <RowDefinition />  
  21.             <RowDefinition Height="Auto" />  
  22.             <RowDefinition Height="Auto" />  
  23.         </Grid.RowDefinitions>  
  24.         <TreeView Name="tvDepartment" Grid.Row="0" ItemContainerStyle="{StaticResource TreeViewItemStyle}" ItemsSource="{Binding Source={StaticResource depProvider}}" ItemTemplate="{StaticResource CheckBoxItemTemplate}" />  
  25.         <TextBlock Name="tbText" Margin="5" Grid.Row="1">当前选中项:<TextBlock Text="{Binding Source={StaticResource depProvider},Path=CheckedItems.Count,Mode=OneWay,IsAsync=True}" /></TextBlock>  
  26.         <Button Margin="5" Grid.Row="2" Content="Get CheckedItems" Click="Button_Click" />  
  27.     </Grid>  
  28. </Window>  

6)窗口的后置代码

Code:
  1. using System.Collections.Generic;   
  2. using System.Text;   
  3. using System.Windows;   
  4. using System.Windows.Data;   
  5. using System.Windows.Documents;   
  6. using System.ComponentModel;   
  7.   
  8. namespace DepartmentTreeView {   
  9.     /// <summary>   
  10.     /// MainWindow.xaml 的交互逻辑   
  11.     /// </summary>   
  12.     public partial class MainWindow : Window {   
  13.         public MainWindow() {   
  14.             InitializeComponent();   
  15.         }   
  16.   
  17.         private void Button_Click(object sender, RoutedEventArgs e) {   
  18.             ObjectDataProvider provider = FindResource("depProvider"as ObjectDataProvider;   
  19.             List<DepartmentViewModel> firstLevelItems = provider.Data as List<DepartmentViewModel>;   
  20.   
  21.             ICollectionView view = CollectionViewSource.GetDefaultView(firstLevelItems);   
  22.             DepartmentViewModel rootItem = view.CurrentItem as DepartmentViewModel;   
  23.   
  24.             List<DepartmentViewModel> checkedItems = CollectionViewSource.GetDefaultView(provider) as List<DepartmentViewModel>;   
  25.   
  26.             StringBuilder builder = new StringBuilder();   
  27.             foreach (DepartmentViewModel checkItem in rootItem.CheckedItems) {   
  28.                 builder.AppendLine(checkItem.DisplayText);   
  29.             }   
  30.             MessageBox.Show("Checked items:/n" + builder.ToString());   
  31.         }   
  32.     }   
  33. }   

好了,到此大功告成!!!

源代码:http://download.csdn.net/source/3253097


 

你可能感兴趣的:(数据库,datagrid,null,LINQ,WPF,WinForms)