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. usingSystem;
  2. usingSystem.Collections.Generic;
  3. usingSystem.Linq;
  4. usingSystem.Text;
  5. usingDepartmentTreeView.DB;
  6. usingSystem.Collections;
  7. usingDepartmentTreeView.DB.SampleDataSetTableAdapters;
  8. namespaceDepartmentTreeView{
  9. publicclassDepartmentHelper{
  10. staticSampleDataSetds=newSampleDataSet();
  11. staticDepartmentTableAdapterda=newDepartmentTableAdapter();
  12. staticDepartmentHelper(){
  13. da.Fill(ds.Department);
  14. }
  15. publicstaticobjectGetDepartment(intdid){
  16. vardepart=ds.Department.Where(c=>c.DID==did).SingleOrDefault();
  17. returndepart;
  18. }
  19. publicstaticIEnumerableGetSubDepartments(intpid){
  20. varlist=ds.Department.Where(c=>c.PID==pid).ToList();
  21. returnlist;
  22. }
  23. }
  24. }

4)创建TreeView 的Model类DepartmentViewModel

Code:
  1. usingSystem;
  2. usingSystem.Collections.Generic;
  3. usingSystem.Linq;
  4. usingSystem.Text;
  5. usingSystem.ComponentModel;
  6. usingSystem.Collections.ObjectModel;
  7. namespaceDepartmentTreeView{
  8. publicclassDepartmentViewModel:INotifyPropertyChanged{
  9. //临时子节点用,当Expanded时移除此节点,添加子节点
  10. staticreadonlyDepartmentViewModel_temp=newDepartmentViewModel(null);
  11. //选中的子节点
  12. privatestaticObservableCollection_checkedItems=newObservableCollection();
  13. //根节点
  14. staticDepartmentViewModel_rootItem;
  15. #regionfields&properties
  16. privatebool?_isChecked;
  17. publicbool?IsChecked{
  18. get{return_isChecked;}
  19. set{
  20. SetCheckState(value,true,true);
  21. }
  22. }
  23. privatevoidSetCheckState(bool?value,boolupdateChildren,boolupdateParent){
  24. if(_isChecked!=value){
  25. _isChecked=value;
  26. //通知选中项的集合
  27. if(_isChecked==true){
  28. _checkedItems.Add(this);
  29. PropertyChanged(this,newPropertyChangedEventArgs("CheckedItems"));
  30. }elseif(_isChecked==false){
  31. _checkedItems.Remove(this);
  32. PropertyChanged(this,newPropertyChangedEventArgs("CheckedItems"));
  33. }
  34. PropertyChanged(this,newPropertyChangedEventArgs("IsChecked"));
  35. if(updateChildren){
  36. if(HasChildren()){
  37. Children.ForEach(c=>c.SetCheckState(value,true,false));
  38. }
  39. }
  40. if(updateParent&&_parent!=null){
  41. _parent.VerifyState();
  42. }
  43. }
  44. }
  45. privatevoidVerifyState(){
  46. bool?state=null;
  47. for(inti=0;i<this.Children.Count;++i){
  48. bool?currentState=this.Children[i].IsChecked;
  49. if(i==0){
  50. state=currentState;
  51. }elseif(state!=currentState){
  52. state=null;
  53. break;
  54. }
  55. }
  56. this.SetCheckState(state,false,true);
  57. }
  58. privatebool_isExpanded;
  59. publicboolIsExpanded{
  60. get{return_isExpanded;}
  61. set{
  62. if(value!=_isExpanded){
  63. _isExpanded=value;
  64. PropertyChanged(this,newPropertyChangedEventArgs("IsExpanded"));
  65. }
  66. if(!HasChildren()){
  67. Children.Remove(_temp);
  68. LoadChildren();
  69. }
  70. }
  71. }
  72. privateobject_current;
  73. publicobjectCurrent{
  74. get{return_current;}
  75. set{_current=value;}
  76. }
  77. publicstringDisplayText{
  78. get{return((DepartmentTreeView.DB.SampleDataSet.DepartmentRow)Current)["DName"].ToString();}
  79. }
  80. privateDepartmentViewModel_parent;
  81. publicDepartmentViewModelParent{
  82. get{return_parent;}
  83. set{_parent=value;}
  84. }
  85. privateList_children;
  86. publicListChildren{
  87. get{return_children;}
  88. privateset{_children=value;}
  89. }
  90. #endregion
  91. publicstaticListCreate(){
  92. varlist=DepartmentHelper.GetSubDepartments(0);
  93. DepartmentViewModelroot=newDepartmentViewModel(null);
  94. _rootItem=root;
  95. root.Children.Clear();
  96. foreach(variteminlist){
  97. root.Children.Add(newDepartmentViewModel(item));
  98. }
  99. returnroot.Children;
  100. }
  101. privateDepartmentViewModel(objectcurrentObject){
  102. Current=currentObject;
  103. _isChecked=false;
  104. Children=newList();
  105. Children.Add(_temp);
  106. }
  107. ///
  108. ///初始化,用于设置父节点
  109. ///
  110. privatevoidInit(){
  111. if(!HasChildren())return;
  112. foreach(DepartmentViewModelchildinChildren){
  113. child.Parent=this;
  114. child.Init();
  115. }
  116. PropertyChanged(this,newPropertyChangedEventArgs("Children"));
  117. }
  118. ///
  119. ///加载子节点
  120. ///
  121. privatevoidLoadChildren(){
  122. if(Current!=null){
  123. intpid=Convert.ToInt32(((DepartmentTreeView.DB.SampleDataSet.DepartmentRow)Current)["DID"]);
  124. varlist=DepartmentHelper.GetSubDepartments(pid);
  125. foreach(variteminlist){
  126. DepartmentViewModelmodel=newDepartmentViewModel(item){_isChecked=this.IsChecked};
  127. if(model.IsChecked==true){
  128. _checkedItems.Add(model);
  129. PropertyChanged(this,newPropertyChangedEventArgs("CheckedItems"));
  130. }
  131. Children.Add(model);
  132. }
  133. Init();
  134. }
  135. }
  136. ///
  137. ///判断是否有子节点(逻辑是:如果只有一个临时子节点,说明没有真正的子节点)
  138. ///
  139. ///
  140. privateboolHasChildren(){
  141. return!(Children.Count==1&&Children[0]==_temp);
  142. }
  143. publicObservableCollectionCheckedItems{
  144. get{
  145. return_checkedItems;
  146. }
  147. }
  148. publiceventPropertyChangedEventHandlerPropertyChanged;
  149. }
  150. }

5)创建WPF窗体

Code:
  1. <Windowx: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. <ObjectDataProviderx:Key="depProvider"ObjectType="{x:Typelocal:DepartmentViewModel}"MethodName="Create"/>
  8. <Stylex:Key="TreeViewItemStyle"TargetType="{x:TypeTreeViewItem}">
  9. <SetterProperty="IsExpanded"Value="{BindingPath=IsExpanded,Mode=TwoWay}"/>
  10. Style>
  11. <HierarchicalDataTemplatex:Key="CheckBoxItemTemplate"ItemsSource="{BindingChildren}">
  12. <StackPanelOrientation="Horizontal"Margin="0,2,0,0">
  13. <CheckBoxFocusable="False"IsChecked="{BindingIsChecked,Mode=TwoWay}"VerticalAlignment="Center"/>
  14. <ContentPresenterContent="{BindingDisplayText,Mode=OneWay}"Margin="2,0"/>
  15. StackPanel>
  16. HierarchicalDataTemplate>
  17. Window.Resources>
  18. <GridMargin="5">
  19. <Grid.RowDefinitions>
  20. <RowDefinition/>
  21. <RowDefinitionHeight="Auto"/>
  22. <RowDefinitionHeight="Auto"/>
  23. Grid.RowDefinitions>
  24. <TreeViewName="tvDepartment"Grid.Row="0"ItemContainerStyle="{StaticResourceTreeViewItemStyle}"ItemsSource="{BindingSource={StaticResourcedepProvider}}"ItemTemplate="{StaticResourceCheckBoxItemTemplate}"/>
  25. <TextBlockName="tbText"Margin="5"Grid.Row="1">当前选中项:<TextBlockText="{BindingSource={StaticResourcedepProvider},Path=CheckedItems.Count,Mode=OneWay,IsAsync=True}"/>TextBlock>
  26. <ButtonMargin="5"Grid.Row="2"Content="GetCheckedItems"Click="Button_Click"/>
  27. Grid>
  28. Window>

6)窗口的后置代码

Code:
  1. usingSystem.Collections.Generic;
  2. usingSystem.Text;
  3. usingSystem.Windows;
  4. usingSystem.Windows.Data;
  5. usingSystem.Windows.Documents;
  6. usingSystem.ComponentModel;
  7. namespaceDepartmentTreeView{
  8. ///
  9. ///MainWindow.xaml的交互逻辑
  10. ///
  11. publicpartialclassMainWindow:Window{
  12. publicMainWindow(){
  13. InitializeComponent();
  14. }
  15. privatevoidButton_Click(objectsender,RoutedEventArgse){
  16. ObjectDataProviderprovider=FindResource("depProvider")asObjectDataProvider;
  17. ListfirstLevelItems=provider.DataasList;
  18. ICollectionViewview=CollectionViewSource.GetDefaultView(firstLevelItems);
  19. DepartmentViewModelrootItem=view.CurrentItemasDepartmentViewModel;
  20. ListcheckedItems=CollectionViewSource.GetDefaultView(provider)asList;
  21. StringBuilderbuilder=newStringBuilder();
  22. foreach(DepartmentViewModelcheckIteminrootItem.CheckedItems){
  23. builder.AppendLine(checkItem.DisplayText);
  24. }
  25. MessageBox.Show("Checkeditems:/n"+builder.ToString());
  26. }
  27. }
  28. }

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

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


你可能感兴趣的:(WPF TreeView递归异步绑定)