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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

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

  

  1.前言

  繼上一篇《WPF應用基礎篇---TreeView》的發布之后,有部分朋問我關于里面一些基礎應用的問題,可能是我寫得不夠詳細,所以在這里,我想再次那文章中的案例來談談初步體驗數據驅動之美,擺脫舊WinForm編程習慣(靠觸發事件來實現界面的變化)。

 2.背景

  

 ? 我們看看以下案例圖片的功能如何實現:

  ?

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

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

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

          全部選中-->父節點CheckBox打鉤 同時修改父節點數據,根據當前修改所有子節點狀態

          全部未選中-->父節點CheckBox為空 同時修改父節點數據,根據當前修改所有子節點狀態

  WinForm具體代碼實現兩態樹:

View Code /// <summary>
???????
/// 設置父節點狀態
???????
/// </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>
???????
/// 設置孩子節點狀態
???????
/// </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>
???????
/// 樹節點被選中后 觸發的事件
???????
/// </summary>
???????
/// <param name="sender"></param>
???????
/// <param name="e"></param>
??????? private void treeView1_AfterCheck(object sender, TreeViewEventArgs e)
??????? {
??????????
//isClick是全局變量
????????????
//是為了解決無限遞歸而是用的一個標志
??????????? if (!isClick)??????????????
????????????? {
???????????????
return;
??????????? }

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

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

  • 實現INotifyPropertyChanged解口
  • 當數據改變時修改父節點和相應子節點的狀態,然后把數據綁定到界面上去。?

  WPF具體代碼實現兩態樹:

  

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

///?<summary>
????????
///?向下遍歷,更改孩子節點狀態
????????
///?注意:這里的父節點不是屬性而是字段
????????
///?采用字段的原因是因為不想讓父節點觸發訪問器而觸發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>
????????
///?向上遍歷,更改父節點狀態
????????
///?注意:這里的父節點不是屬性而是字段
????????
///?采用字段的原因是因為不想讓父節點觸發訪問器而觸發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實現代碼是事件驅動,首先觸發一個事件,然后進行一些邏輯判斷,而且還需要借助全部變量IsClick來防止代碼無限遞歸。而WPF的實現則是靠數據驅動,數據變化了,然后才調用方法來更改數據的相應狀態。最后才通知界面刷新數據。其實可以看出現在的需求很簡單就是,根據節點選中狀態操作樹,但是如果我的需求變化了,例如圖1-2的需求一樣,如果我需要打鉤的時候,操作按鈕的狀態,比如打鉤就連接,不打鉤則斷開。WinForm的話又要在代碼中做一些邏輯判斷,這很容易實現,但是如果我斷開按鈕按下的時候,只能點擊連接,這時候WinForm的事件就要做很多邏輯處理,如果需求要求的功能多的話,事件的后臺代碼將越來越復雜,最后導致邏輯混亂。而WPF實現的話,則是根據數據變化而且在界面上顯示,當我點擊的時候,修改下數據的狀態則可以。后臺無需要做太多的處理,這樣代碼結構和邏輯會變得相對清晰。

 3.三態樹具體實現

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

  • 定義好數據結構,并在數據上通過實現INotifyPropertyChanged接口,來屬性變化后通知View刷新數據。
  • 把想對應的屬性Binding到View的控件上。

  

  數據結構實體代碼:

  

View Code ///?<summary>
????
///?設備基類
????
///?</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>
????????
///?向下遍歷,更改孩子節點狀態
????????
///?注意:這里的父節點不是屬性而是字段
????????
///?采用字段的原因是因為不想讓父節點觸發訪問器而觸發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>
????????
///?向上遍歷,更改父節點狀態
????????
///?注意:這里的父節點不是屬性而是字段
????????
///?采用字段的原因是因為不想讓父節點觸發訪問器而觸發Setter
????????
///?</summary>
????????
///?<param?name="CurrentNode"></param>
????????public?void?ChangedParentNodes(Device?CurrentNode)
????????{
????????????
if?(CurrentNode.ParentNode?!=?null)
????????????{
????????????????
bool??parentNodeState?=?true;
????????????????
int?selectedCount?=?0;??//被選中的個數
????????????????int?noSelectedCount?=?0;????//不被選中的個數

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

????????????????
//如果全部被選中,則修改父節點為選中
????????????????if?(selectedCount?==?
????????????????????CurrentNode.ParentNode.ChildNodes.Count)
????????????????{
????????????????????parentNodeState?
=?true;
????????????????}
????????????????
//如果全部不被選中,則修改父節點為不被選中
????????????????else?if?(noSelectedCount?==?
????????????????????CurrentNode.ParentNode.ChildNodes.Count)
????????????????{
????????????????????parentNodeState?
=?false;
????????????????}
????????????????
//否則標記父節點(例如用實體矩形填滿)
????????????????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具體實現代碼:

  

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

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

  4.總結     

  WPF的主要思想是用數據驅動來代替事件驅動。當數據發生變化的時候才做出一些相應的處理。這樣的好處就是:

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

  5.附加源碼:點擊下載     ?

轉載于:https://www.cnblogs.com/smlAnt/archive/2011/08/09/2130334.html

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。