潜移默化学会WPF(难点控件treeview)--改造TreeView(CheckBox多选择版本),递归绑定数据

目前自己对treeview的感慨很多

今天先讲 面对这种 表结构的数据 的其中一种绑定方法,后面多几列其他属性都没关系,例如多个字段,

1  A  0

2  B  0

3  C  0

4  D  1

5  E  2

6  F  4

7  G 1

...

就是递归型的表结构

然后通过treeview 展示( treeview 的name 叫 tv , collection 是 ObservableCollection<T> 的 一个实例

1.首先你必须需要  要建立一个 跟treeview 结构很相似的一个集合,这里建议用  ObservableCollection<T>   这个集合很特殊,你要记得,例如 tv.ItemsSource = this.collection;  当你这样绑定时,修改collection的属性时就是修改treeview绑定的某些属性

 

2.在做绑定时一定要 搞清楚treeview的item的结构,你想呈现什么样的,每个 treeveiwItem就是一个对象 ,这个对象可以用一个类去替代,或者什么去替代

 

3. 不啰嗦了,说正题,新建一个实体类

例如

   

   /// <summary>
   ////// </summary>
   public class ReportCategoryEntity
   {
       /// <summary>
       /// ID
       /// </summary>
       public int Id { get; set; }
       /// <summary>
       /// 名称
       /// </summary>
       public string Title { get; set; }
       /// <summary>
       ///父节点
       /// </summary>
       public int ParentID { get; set; }


   }

 

这个应该看得懂吧,加入那个递归表 叫 FoodCatagory(菜品目录) 表,现在只要递归显示出来就行了

把他所有行取出来 select * from FoodCatagory,如果其中不止基本的三列,有其他列,例如通过这些列的值  作为控制 treeview显示的样式的条件用 这也是很不错的点子,真的,比如说再加一个图片的地址,如果有个图片地址字典表,这里就存那个表中的字段了,如果其他字段你需要,那你可以继续写在 ReportCategoryEntity 这个类中

 

读出来的所有行封装到  List<ReportCategoryEntity>    lstReportCategoryEntity  ,这个应该会把,如果不会,说明你的oop基础没学好了

 

好了

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Windows.Media;

namespace 命名空间
{
    public class TreeCategory : INotifyPropertyChanged
    {
        private ObservableCollection<TreeCategory> children = new ObservableCollection<TreeCategory>();

        public TreeCategory() { }

        public TreeCategory(List<ReportCategoryEntity> totalCategory, int parentID)  //0  根目录
        {
            //Parent = this;
            foreach (ReportCategoryEntity item in totalCategory)
            {
                if (item.ParentID == parentID)
                {
                    TreeCategory tc = new TreeCategory(totalCategory, item.Id);
                    tc.Title = item.Title;
                    children.Add(tc);
                }
            }
        }

        public ObservableCollection<TreeCategory> Children
        {
            get { return this.children; }
        }

        /// <summary>
        /// 显示的名称
        /// </summary>
        private string title;
        public string Title
        {
            get
            {
                return title;
            }
            set
            {
                title = value;
                this.NotifyPropertyChanged("Title");
            }
        }
        /// <summary>
        /// 是否选中
        /// </summary>
        private bool isSelected = false;
        public bool IsSelected
        {
            set
            {
                this.isSelected = value;
                this.NotifyPropertyChanged("IsSelected");
            }
            get { return this.isSelected; }
        }

        /// <summary>
        /// 标题字体颜色
        /// </summary>
        private Brush foregroundBrush = new SolidColorBrush(Colors.Black);
        public Brush ForegroundBrush
        {
            set
            {
                this.foregroundBrush = value;
                this.NotifyPropertyChanged("ForegroundBrush");
            }
            get { return this.foregroundBrush; }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }


    }
}

 

 

接下来再建立一个最重要的 类,用来控制treeview显示的,看似像实体类,但又不像,下面是我建立的,本类下有个本类的集合的属性,还是ObservableCollection集合的,加了 INotifyPropertyChanged 只是为了达到只要修改这个集合的某些属性的值,就可以修改treeview的状态了。

例如我这里添加了 标题字体颜色  ForegroundBrush  这个属性,就是为了达到treeview的每个节点的显示颜色,你在递归的时候就可以根据 ReportCategoryEntity 的某些属性作为条件,然后动态给ForegroundBrush  赋值,要记住,每一个 TreeCategory 类就已经对应了一个 treeviewItem,该类的每一个字段都可以作为 treeview 显示出来的条件,至于前面treeview的样式模板该怎么写,很快你就清楚了

 

现在说一下 xaml 中前台的写法

 <TreeView Name="tv" Width="170" Height="800" MaxHeight="520" BorderThickness="0">
                    <TreeView.ItemContainerStyle>
                        <Style TargetType="TreeViewItem">
                            <Setter Property="TreeViewItem.IsExpanded" Value="True"/>
                            <!--<Setter Property="TreeViewItem.IsSelected" Value="{Binding IsSelected}"/>-->
                            <!--<EventSetter Event="MouseMove" Handler="tree_MouseMove" />-->
                            <!--<EventSetter Event="KeyDown" Handler="treeViewItem_KeyDown" />-->
                            <Setter Property="TreeViewItem.Margin" Value="0,1,0,0"/>
                        </Style>
                    </TreeView.ItemContainerStyle>
                    <TreeView.ItemTemplate>
                        <HierarchicalDataTemplate DataType="{x:Type c:TreeCategory}"  ItemsSource="{Binding Children}">
                            <StackPanel  Margin="-2,0,0,0" Orientation="Horizontal" x:Name="staTree">
                                <CheckBox Content="{Binding Title}" FontSize="14" FontFamily="微软雅黑" Tag="{Binding Children}" IsChecked="{Binding IsSelected}" Foreground="{Binding ForegroundBrush}" Unchecked="ck_Unchecked" Checked="ck_Checked"></CheckBox>
                            </StackPanel>
                            <HierarchicalDataTemplate.Triggers>
                                <DataTrigger Binding="{Binding IsSelected}" Value="true">
                                    <Setter TargetName="staTree" Property="Background" Value="White"/>
                                </DataTrigger>
                            </HierarchicalDataTemplate.Triggers>
                        </HierarchicalDataTemplate>
                    </TreeView.ItemTemplate>

                </TreeView>

重点看  <TreeView.ItemTemplate>  这个节点下面的 ,<TreeView.ItemContainerStyle>用来控制总体显示样式的

 

现在你应该有点清楚了

 

1. 首先你要知道  treeviewItem 对应一个  TreeCategory ,treeview控件每个节点下都有可能有子节点,像一个集合,所以我在TreeCategory 类中又包括了他自己的一个集合,已达到构造出和treeview很像的一个结构,感觉到了吗

2. ReportCategoryEntity  这个实体是  把从数据库读出来的每条数据的一个net样子,一条表中的数据对应一个 ReportCategoryEntity  实体对象,达到封装效果,不一定只有这三个字段,拓展他以达到treeview显示更丰富的效果,而且treeview的模板很好写,可以改造出很多种

3.就是 TreeCategory 类  ,如果以前没有接触xaml语言的可能会有疑问  

public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

为什么会有这堆代码,而且每个属性后面,又调了这个 NotifyPropertyChanged 方法,我不知道原理,但我知道怎么用了,wpf的控件很奇特,不像winform控件或者 asp.net控件那么死,重写个控件很难,这就是为什么wpf 做的桌面软件五花八门,而且很炫,只要你的到控件,不脱离实际,控件都可以变成你想要的控件。又说了好多废话。

最重要的还是我的那个 递归

 public TreeCategory(List<ReportCategoryEntity> totalCategory, int parentID)  //0  根目录
        {
            //Parent = this;
            foreach (ReportCategoryEntity item in totalCategory)
            {
                if (item.ParentID == parentID)
                {
                    TreeCategory tc = new TreeCategory(totalCategory, item.Id);
//加一些你想要的字段,然后在这里赋值吧 tc.Title
= item.Title; children.Add(tc); } } }

 

4.为了达到treeview显示的不同效果,加了数据触发器,这样后台一行代码也不要写,有必要的话,你可以再加一个值转换器吧 ,加一些你想要的数据触发器吧,丰富显示效果,例如根据某个字段作为判断条件,如果是什么,把image 控件的source地址换了,就可以达到不同的节点,显示不同的图片了  ,具体应用有很多

<HierarchicalDataTemplate.Triggers>
                                <DataTrigger Binding="{Binding IsSelected}" Value="true">
                                    <Setter TargetName="staTree" Property="Background" Value="White"/>
                                </DataTrigger>
                    //加一些你想要的数据触发器吧,丰富显示效果,例如根据某个字段作为判断条件,如果是什么,把image 控件的source地址换了,就可以达到不同的节点,显示不同的图片了        
</HierarchicalDataTemplate.Triggers>

 

 

后台绑定

    private ObservableCollection<TreeCategory> collection = new ObservableCollection<TreeCategory>();

     this.collection = new TreeCategory(ctagr, 0).Children;
                treeCategories.ItemsSource = this.collection;
ctagr 是个List<ReportCategoryEntity> 集合

 

先冒个泡

这是我做的一个菜品目录checkbox多选版本的 ,作为导航栏的,例如选中这个目录,列出这个目录下的所有菜,或者计算出这个菜品目录的菜的销售情况等等,扩展一下,部门与员工是挺经典的一个例子

 

至于选中根节点,子节点全部选中,那你可以写在checkbox的事件里,应该会吧,这我就不写了,建议递归吧,应为你不知道他下面有多少个子节点

 

这个我加了checkbox选择的模式,全选,全不选,反选,随选

这个我就再说一点吧,然后来体现ObservableCollection 集合的好处

你不可能修改treeviewItem的属性吧,那个有点傻了

直接修改ObservableCollection 中的 TreeCategory 的IsSelected属性就行了,treeview中自动体现

这里我就只说个全选,全不选 的吧

下面是我写的一个算法

为了防止你粘贴复制,不思考我就上个图吧

反选应该就不用我说了吧,留着思考吧

而且每个 节点的 checkbox都是可以改的,而且你还可以组合控件,例如把很多控件放在stackpanel 中,作为一个treeviewItem

例如每个节点鼠标移上去的效果你都可以很轻松的改的,就看你的技术了,鼠标移上去,我就不说了

再看个图吧

如果你熟悉treeview的话,迅雷看看的这个目录效果很轻松的可以做出来,那个TreeviewItem加载动画也可以做

,这是个 目录下有具体内容的treeview结构,这个如果也用这种MVVM思想的话也可以很轻松的做出来,我做过,多态做的,呵呵,下次有时间的话就再写篇 treeview下面有内容时,该怎么写。

 

我自己感觉迅雷看看有点像 Expander 加 treeview  控件呢?下次做一个迅雷看看左边的目录 制作教程吧,好了,就暂时啰嗦到这里吧

 

 

 

 

 

 

 

 

 

 

 

posted @ 2012-05-17 16:43  AYUI框架  阅读(5957)  评论(4编辑  收藏  举报