日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

初步体验数据驱动之美---TreeView

發(fā)布時(shí)間:2023/12/1 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 初步体验数据驱动之美---TreeView 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

  

  1.前言

  繼上一篇《WPF應(yīng)用基礎(chǔ)篇---TreeView》的發(fā)布之后,有部分朋問我關(guān)于里面一些基礎(chǔ)應(yīng)用的問題,可能是我寫得不夠詳細(xì),所以在這里,我想再次那文章中的案例來談?wù)劤醪襟w驗(yàn)數(shù)據(jù)驅(qū)動之美,擺脫舊WinForm編程習(xí)慣(靠觸發(fā)事件來實(shí)現(xiàn)界面的變化)。

 2.背景

  

 ? 我們看看以下案例圖片的功能如何實(shí)現(xiàn):

  ?

    圖1-1(WinForm兩態(tài)樹)      ???? 圖1-2(WPF三態(tài)樹)

  如果我們還處在習(xí)慣于WinForm開發(fā)的時(shí)候,我們首先關(guān)注的是,我們需要重寫Tree控件,在上一篇文章中有提到過,這里就不再重復(fù)。然后當(dāng)我們布局和設(shè)計(jì)好數(shù)據(jù)結(jié)構(gòu)后,我們關(guān)心的自然就是選中的時(shí)候要做什么,我們首先會考慮到為樹節(jié)點(diǎn)添加事件來處理相應(yīng)的邏輯處理。大致實(shí)現(xiàn)以下幾個(gè)步驟(簡單的分析)

  • 把sender或者e參數(shù)轉(zhuǎn)換為TreeNode
  • 從TreeNode中的Tag數(shù)據(jù)
  • 根據(jù)Tag的類型轉(zhuǎn)換為具體數(shù)據(jù)
  • 判斷TreeNode選中的狀態(tài),更改Tag實(shí)例的屬性的狀態(tài)如(IsSelected)
  • 根據(jù)需求比如:

          全部選中-->父節(jié)點(diǎn)CheckBox打鉤 同時(shí)修改父節(jié)點(diǎn)數(shù)據(jù),根據(jù)當(dāng)前修改所有子節(jié)點(diǎn)狀態(tài)

          全部未選中-->父節(jié)點(diǎn)CheckBox為空 同時(shí)修改父節(jié)點(diǎn)數(shù)據(jù),根據(jù)當(dāng)前修改所有子節(jié)點(diǎn)狀態(tài)

  WinForm具體代碼實(shí)現(xiàn)兩態(tài)樹:

View Code /// <summary>
???????
/// 設(shè)置父節(jié)點(diǎn)狀態(tài)
???????
/// </summary>
???????
/// <param name="node"></param>
??????? public void SetParentNodeStatus(TreeNode node)
??????? {
???????????
if (node.Parent != null)
??????????? {
???????????????
bool isChecked = true;
???????????????
foreach (TreeNode data in node.Parent.Nodes)
??????????????? {
???????????????????
if (!data.Checked)
??????????????????? {
??????????????????????? isChecked
= false;
???????????????????????
break;
??????????????????? }
??????????????? }

???????????????
if (isChecked)
??????????????? {
??????????????????? node.Parent.Checked
= true;
???????????????????
if(node.Parent.Parent!=null)
??????????????????? {
??????????????????????? SetParentNodeStatus(node.Parent);
??????????????????? }
??????????????? }
???????????????
else
??????????????? {
??????????????????? node.Parent.Checked
= false;
??????????????? }
??????????? }
??????? }

???????
/// <summary>
???????
/// 設(shè)置孩子節(jié)點(diǎn)狀態(tài)
???????
/// </summary>
???????
/// <param name="node"></param>
??????? public void SetChildNodeStatus(TreeNode node)
??????? {
???????????
if (node.Nodes!=null)
??????????? {
???????????????
foreach (TreeNode data in node.Nodes)
??????????????? {
??????????????????? data.Checked
= node.Checked;
???????????????????
if (data.Nodes!=null)
??????????????????? {
??????????????????????? SetChildNodeStatus(data);
??????????????????? }
??????????????? }
??????????? }
??????? }

???????
/// <summary>
???????
/// 樹節(jié)點(diǎn)被選中后 觸發(fā)的事件
???????
/// </summary>
???????
/// <param name="sender"></param>
???????
/// <param name="e"></param>
??????? private void treeView1_AfterCheck(object sender, TreeViewEventArgs e)
??????? {
??????????
//isClick是全局變量
????????????
//是為了解決無限遞歸而是用的一個(gè)標(biāo)志
??????????? if (!isClick)??????????????
????????????? {
???????????????
return;
??????????? }

??????????? isClick
= false;
??????????? TreeNode node
= e.Node;???????????
???????????
if (node.Parent != null)
??????????? {
??????????????? SetParentNodeStatus(e.Node);
??????????? }
???????????
if (node.Nodes != null)
??????????? {
??????????????? SetChildNodeStatus(node);
??????????? }
??????????? isClick
= true;
??????? }

   而當(dāng)我們開始慢慢采用WPF之后,我們的編程習(xí)慣會發(fā)生了很大的變化,我們開始有點(diǎn)對觸發(fā)事件來改變邏輯和界面變化(事件驅(qū)動)的做法感到反感。解決上面的問題,我們只需要靠一個(gè)接口的幫助,就能實(shí)現(xiàn)兩態(tài)樹的功能。

  • 實(shí)現(xiàn)INotifyPropertyChanged解口
  • 當(dāng)數(shù)據(jù)改變時(shí)修改父節(jié)點(diǎn)和相應(yīng)子節(jié)點(diǎn)的狀態(tài),然后把數(shù)據(jù)綁定到界面上去。?

  WPF具體代碼實(shí)現(xiàn)兩態(tài)樹:

  

View Code //是否被選中
????????private?bool??isSelected;
????????
public?bool??IsSelected?
????????{
????????????
get?{?return?isSelected;?}
????????????
set
????????????{
????????????????
if?(isSelected?!=?value)
????????????????{
????????????????????isSelected?
=?value;???
????????????????????ChangeChildNodes(
this);
????????????????????ChangedParentNodes(
this);
????????????????????NotifyPropertyChanged(
"IsSelected");
????????????????}
????????????}
????????}

///?<summary>
????????
///?向下遍歷,更改孩子節(jié)點(diǎn)狀態(tài)
????????
///?注意:這里的父節(jié)點(diǎn)不是屬性而是字段
????????
///?采用字段的原因是因?yàn)椴幌胱尭腹?jié)點(diǎn)觸發(fā)訪問器而觸發(fā)Setter
????????
///?</summary>
????????
///?<param?name="CurrentNode"></param>
????????public?void?ChangeChildNodes(Device?CurrentNode)
????????{
????????????
if?(CurrentNode.ChildNodes?!=?null)
????????????{
????????????????
foreach?(var?data?in?CurrentNode.ChildNodes)
????????????????{
????????????????????data.isSelected?
=?CurrentNode.IsSelected;
????????????????????data.NotifyPropertyChanged(
"IsSelected");
????????????????????
if?(data.ChildNodes?!=?null)
????????????????????{
????????????????????????data.ChangeChildNodes(data);
????????????????????}
????????????????}
????????????}
????????}

????????
///?<summary>
????????
///?向上遍歷,更改父節(jié)點(diǎn)狀態(tài)
????????
///?注意:這里的父節(jié)點(diǎn)不是屬性而是字段
????????
///?采用字段的原因是因?yàn)椴幌胱尭腹?jié)點(diǎn)觸發(fā)訪問器而觸發(fā)Setter
????????
///?</summary>
????????
///?<param?name="CurrentNode"></param>
????????public?void?ChangedParentNode(Device?CurrentNode)
????????{
????????????
if?(CurrentNode.ParentNode?!=?null)
????????????{
????????????????
bool?isCheck?=?true;
????????????????
foreach?(var?data?in?CurrentNode.ParentNode.ChildNodes)
????????????????{
????????????????????
if?(data.IsSelected?!=?true)
????????????????????{
????????????????????????isCheck?
=?false;
????????????????????????
break;
????????????????????}
????????????????}
????????????????CurrentNode.parentNode.isSelected?
=?isCheck;
????????????????CurrentNode.parentNode.NotifyPropertyChanged(
"IsSelected");
????????????}
????????}

  從兩段代碼可以看出,WinForm實(shí)現(xiàn)代碼是事件驅(qū)動,首先觸發(fā)一個(gè)事件,然后進(jìn)行一些邏輯判斷,而且還需要借助全部變量IsClick來防止代碼無限遞歸。而WPF的實(shí)現(xiàn)則是靠數(shù)據(jù)驅(qū)動,數(shù)據(jù)變化了,然后才調(diào)用方法來更改數(shù)據(jù)的相應(yīng)狀態(tài)。最后才通知界面刷新數(shù)據(jù)。其實(shí)可以看出現(xiàn)在的需求很簡單就是,根據(jù)節(jié)點(diǎn)選中狀態(tài)操作樹,但是如果我的需求變化了,例如圖1-2的需求一樣,如果我需要打鉤的時(shí)候,操作按鈕的狀態(tài),比如打鉤就連接,不打鉤則斷開。WinForm的話又要在代碼中做一些邏輯判斷,這很容易實(shí)現(xiàn),但是如果我斷開按鈕按下的時(shí)候,只能點(diǎn)擊連接,這時(shí)候WinForm的事件就要做很多邏輯處理,如果需求要求的功能多的話,事件的后臺代碼將越來越復(fù)雜,最后導(dǎo)致邏輯混亂。而WPF實(shí)現(xiàn)的話,則是根據(jù)數(shù)據(jù)變化而且在界面上顯示,當(dāng)我點(diǎn)擊的時(shí)候,修改下數(shù)據(jù)的狀態(tài)則可以。后臺無需要做太多的處理,這樣代碼結(jié)構(gòu)和邏輯會變得相對清晰。

 3.三態(tài)樹具體實(shí)現(xiàn)

  這里將為大家介紹下三態(tài)樹在WPF中的實(shí)現(xiàn),也是對上一篇文章的補(bǔ)充。本案例是在基于MVVM的基礎(chǔ)上實(shí)現(xiàn)的。要實(shí)現(xiàn)圖1-2(三態(tài)樹)只需要做以下兩個(gè)步驟。

  • 定義好數(shù)據(jù)結(jié)構(gòu),并在數(shù)據(jù)上通過實(shí)現(xiàn)INotifyPropertyChanged接口,來屬性變化后通知View刷新數(shù)據(jù)。
  • 把想對應(yīng)的屬性Binding到View的控件上。

  

  數(shù)據(jù)結(jié)構(gòu)實(shí)體代碼:

  

View Code ///?<summary>
????
///?設(shè)備基類
????
///?</summary>
????public?class?Device:INotifyPropertyChanged
????{
????????
//是否被選中
????????private?bool??isSelected;
????????
public?bool??IsSelected?
????????{
????????????
get?{?return?isSelected;?}
????????????
set
????????????{
????????????????
if?(isSelected?!=?value)
????????????????{
????????????????????isSelected?
=?value;???
????????????????????ChangeChildNodes(
this);
????????????????????ChangedParentNodes(
this);
????????????????????NotifyPropertyChanged(
"IsSelected");
????????????????}
????????????}
????????}
????????
????????
private?DeviceStatus?status;
????????
public?DeviceStatus?Status
????????{
????????????
get?{?return?status;?}
????????????
set
????????????{
????????????????
if?(status?!=?value)
????????????????{
????????????????????status?
=?value;
????????????????????NotifyPropertyChanged(
"Status");
????????????????}
????????????}
????????}

????????
public?string?Name?{?get;?set;?}
????????
public?string?ImageUrl{get;set;}

????????
private?List<Device>?childNodes;
????????
public?List<Device>?ChildNodes
????????{
????????????
get?{?return?childNodes;?}
????????????
set
????????????{
????????????????
if?(childNodes?!=?value)
????????????????{
????????????????????childNodes?
=?value;
????????????????????NotifyPropertyChanged(
"ChildNodes");
????????????????}
????????????}
????????}

????????
private?Device?parentNode;
????????
public?Device?ParentNode
????????{
????????????
get?{?return?parentNode;?}
????????????
set
????????????{
????????????????
if?(parentNode?!=?value)
????????????????{
????????????????????parentNode?
=?value;
????????????????????NotifyPropertyChanged(
"ParentNode");
????????????????}
????????????}
????????}

????????
///?<summary>
????????
///?向下遍歷,更改孩子節(jié)點(diǎn)狀態(tài)
????????
///?注意:這里的父節(jié)點(diǎn)不是屬性而是字段
????????
///?采用字段的原因是因?yàn)椴幌胱尭腹?jié)點(diǎn)觸發(fā)訪問器而觸發(fā)Setter
????????
///?</summary>
????????
///?<param?name="CurrentNode"></param>
????????public?void?ChangeChildNodes(Device?CurrentNode)
????????{
????????????
if?(CurrentNode.ChildNodes?!=?null)
????????????{
????????????????
foreach?(var?data?in?CurrentNode.ChildNodes)
????????????????{
????????????????????data.isSelected?
=?CurrentNode.IsSelected;
????????????????????data.NotifyPropertyChanged(
"IsSelected");
????????????????????
if?(data.ChildNodes?!=?null)
????????????????????{
????????????????????????data.ChangeChildNodes(data);
????????????????????}
????????????????}
????????????}
????????}

????????
///?<summary>
????????
///?向上遍歷,更改父節(jié)點(diǎn)狀態(tài)
????????
///?注意:這里的父節(jié)點(diǎn)不是屬性而是字段
????????
///?采用字段的原因是因?yàn)椴幌胱尭腹?jié)點(diǎn)觸發(fā)訪問器而觸發(fā)Setter
????????
///?</summary>
????????
///?<param?name="CurrentNode"></param>
????????public?void?ChangedParentNodes(Device?CurrentNode)
????????{
????????????
if?(CurrentNode.ParentNode?!=?null)
????????????{
????????????????
bool??parentNodeState?=?true;
????????????????
int?selectedCount?=?0;??//被選中的個(gè)數(shù)
????????????????int?noSelectedCount?=?0;????//不被選中的個(gè)數(shù)

????????????????
foreach?(var?data?in?CurrentNode.ParentNode.ChildNodes)
????????????????{
????????????????????
if?(data.IsSelected?==?true)
????????????????????{
????????????????????????selectedCount
++;
????????????????????}
????????????????????
else?if?(data.IsSelected?==?false)
????????????????????{
????????????????????????noSelectedCount
++;
????????????????????}
????????????????}

????????????????
//如果全部被選中,則修改父節(jié)點(diǎn)為選中
????????????????if?(selectedCount?==?
????????????????????CurrentNode.ParentNode.ChildNodes.Count)
????????????????{
????????????????????parentNodeState?
=?true;
????????????????}
????????????????
//如果全部不被選中,則修改父節(jié)點(diǎn)為不被選中
????????????????else?if?(noSelectedCount?==?
????????????????????CurrentNode.ParentNode.ChildNodes.Count)
????????????????{
????????????????????parentNodeState?
=?false;
????????????????}
????????????????
//否則標(biāo)記父節(jié)點(diǎn)(例如用實(shí)體矩形填滿)
????????????????else
????????????????{
????????????????????parentNodeState?
=?null;
????????????????}

????????????????CurrentNode.parentNode.isSelected?
=?parentNodeState;
????????????????CurrentNode.parentNode.NotifyPropertyChanged(
"IsSelected");

????????????????
if?(CurrentNode.ParentNode.ParentNode?!=?null)
????????????????{
????????????????????ChangedParentNodes(CurrentNode.parentNode);
????????????????}
????????????}
????????}

????????
public?void?NotifyPropertyChanged(string?name)
????????{
????????????
if(PropertyChanged!=null)
????????????PropertyChanged(
this,new?PropertyChangedEventArgs(name));
????????}
????????
public?event?PropertyChangedEventHandler?PropertyChanged;
????}

  

  View具體實(shí)現(xiàn)代碼:

  

View Code <CheckBox?IsChecked="{Binding?IsSelected,Mode=TwoWay}"?Margin="2"?VerticalAlignment="Center"/>

??? 這里只需要把實(shí)體的IsSelected屬性Bingding到View上,Mode是雙向的就可以了,具體的邏輯有實(shí)體內(nèi)部做處理,這樣更能體現(xiàn)出View中代碼的干凈,而且更能讓View和ViewModel耦合性降到最低。實(shí)現(xiàn)三態(tài)樹的時(shí)候有一個(gè)小技巧,讓代碼避開了無限遞歸的問題,這里采用屬性如IsSelected,屬性有setter和gettter訪問器,當(dāng)我們向上、下遍歷的時(shí)候,改變的是數(shù)據(jù)中的字段isSelected,這樣就不會觸發(fā)了屬性的setter。這也是數(shù)據(jù)驅(qū)動的一個(gè)優(yōu)點(diǎn)之一。

  4.總結(jié)     

  WPF的主要思想是用數(shù)據(jù)驅(qū)動來代替事件驅(qū)動。當(dāng)數(shù)據(jù)發(fā)生變化的時(shí)候才做出一些相應(yīng)的處理。這樣的好處就是:

  • 使得代碼邏輯更加清晰。
  • 可以讓數(shù)據(jù)發(fā)生變化,通過屬性訪問器來控制相應(yīng)的邏輯變化(其實(shí)也是數(shù)據(jù)變化),最后通知View。這樣簡化了邏輯處理而且減少了邏輯混亂的局面。
  • 有利于降低View和ViewModel(或后臺具體實(shí)現(xiàn)代碼)之間的耦合度,也就是說有利于把強(qiáng)依賴關(guān)系轉(zhuǎn)為弱依賴甚至沒依賴關(guān)系?! ?/span>

  5.附加源碼:點(diǎn)擊下載     ?

轉(zhuǎn)載于:https://www.cnblogs.com/smlAnt/archive/2011/08/09/2130334.html

總結(jié)

以上是生活随笔為你收集整理的初步体验数据驱动之美---TreeView的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。