目前自己對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 控件呢?下次做一個迅雷看看左邊的目錄 制作教程吧,好了,就暫時啰嗦到這里吧