­

WPF之複選MVVM TreeView(TreeView+CheckBox)

需求背景:

當我們用到權限菜單欄時權限菜單欄屬於遞歸效果,我們需要用到TreeView+CheckBox進行組合複選開發時,我們需要解決此類問題時怎麼辦,那麼就引出今天的小筆記內容

實現方式:

下載MVVM框架以及Newtonsoft,這時候可能大家會覺得奇怪,為什麼還會用到Json,大家直接看代碼,我每一步驟都很詳細,代碼可直接粘貼,讓大家爽一爽

1、創建模型 CheckModel

 1 /// <summary>
 2     /// 模型可進行序列化
 3     /// </summary>
 4     [Serializable]
 5     public class CheckModel : BindableBase
 6     {
 7         public CheckModel()
 8         {
 9 
10         }
11         private bool? _IsSelected=false;
12         public bool? IsSelected
13         {
14             get { return _IsSelected; }
15             set {
16                 if (SetProperty(ref _IsSelected, value))
17                 {
18                     SelectCheck(value, true, true);
19                 }
20             }
21         }
22         /// <summary>
23         /// 設置全選狀態
24         /// </summary>
25         /// <param name="value"></param>
26         /// <param name="checkedChildren"></param>
27         /// <param name="checkedParent"></param>
28         private void SelectCheck(bool? value, bool checkedChildren, bool checkedParent)
29         {
30             if (checkedChildren && value.HasValue && ChildList != null)
31                 foreach (var item in ChildList)
32                 {
33                     item.SelectCheck(value, true, false);
34                 }
35             if (checkedParent && this.Parent != null)//父類模型不為空就進行狀態修改
36                 this.Parent.CheckParentCheckState();
37             IsSelected = value;
38         }
39         /// <summary>
40         /// 設置父類選中狀態
41         /// </summary>
42         private void CheckParentCheckState()
43         {
44             bool? _currentState = this._IsSelected;//臨時存儲當前選中狀態
45             bool? _firstState = null;//剛開始預設為選中狀態為空
46 
47             for (int i = 0; i < this.ChildList.Count(); i++)
48             {
49                 bool? childrenState = this.ChildList[i].IsSelected;//抓起子集合選中狀態
50                 if (i == 0)//如果為第一次循環,將子集選中狀態賦給預設狀態
51                 {
52                     _firstState = childrenState;
53                 }
54                 else if (_firstState != childrenState)//如果不是第一次循環將預設狀態,也就是將父類選中站台標記為空(空代表子集又被選中)
55                 {
56                     _firstState = null;
57                 }
58             }
59             if (_firstState != null) _currentState = _firstState;//如果預設的狀態不為空,將預設狀態給臨時存儲當前選中狀態
60             SelectCheck(_firstState, false, true);//開始設置選中狀態
61         }
62 
63         private string _Title;
64         public string Title
65         {
66             get { return _Title; }
67             set { SetProperty(ref _Title, value); }
68         }
69         /// <summary>
70         /// 這一點很關鍵,解決Json序列化循環依賴問題
71         /// </summary>
72         [JsonIgnore]
73         private CheckModel _Parent;
74         [JsonIgnore]
75         public CheckModel Parent
76         {
77             get { return _Parent; }
78             set { SetProperty(ref _Parent, value); }
79         }
80         private ObservableCollection<CheckModel> _ChildList=new ObservableCollection<CheckModel>();
81         public ObservableCollection<CheckModel> ChildList
82         {
83             get { return _ChildList; }
84             set { SetProperty(ref _ChildList, value); }
85         }
86     }

2、創建ViewModel

 1 public class MainWindowViewModel : BindableBase
 2     {
 3         public MainWindowViewModel()
 4         {
 5             CheckModel model = new CheckModel();
 6             model.Title = "目錄";
 7             model.ChildList = new ObservableCollection<CheckModel>();
 8             ListData.Add(model);
 9                 CheckModel chmodel = new CheckModel();
10                 chmodel.Title = "目錄1";
11                 chmodel.Parent = model; 
12                     CheckModel chmodel_1 = new CheckModel();
13                     chmodel_1.Title = "目錄1-1";
14                     chmodel_1.Parent = chmodel;
15                     chmodel.ChildList.Add(chmodel_1);
16                 model.ChildList.Add(chmodel);
17                 CheckModel chmodel2 = new CheckModel();
18                 chmodel2.Title = "目錄2";
19                 chmodel2.Parent = model;
20                 model.ChildList.Add(chmodel2);
21         }
22         private ObservableCollection<CheckModel> _ListData = new ObservableCollection<CheckModel>()
23         {
24 
25         };
26         public ObservableCollection<CheckModel> ListData
27         {
28             get { return _ListData; }
29             set { SetProperty(ref _ListData, value); }
30         }
31         private ObservableCollection<CheckModel> _CheckListData = new ObservableCollection<CheckModel>();
32         public ObservableCollection<CheckModel> CheckListData
33         {
34             get { return _CheckListData; }
35             set { SetProperty(ref _CheckListData, value); }
36         }
37         private DelegateCommand _CheckedCommand;
38         public DelegateCommand CheckedCommand =>
39             _CheckedCommand ?? (_CheckedCommand = new DelegateCommand(ExecuteCheckedCommand));
40 
41         void ExecuteCheckedCommand()
42         {
43             CheckListData.Clear();
44             #region 這一點很重要,用過Json解決集合地址引用問題,如果直接進行原有集合抓取選中狀態,那麼你進行遞歸篩查時涉及到數據地址引用問題,造成源數據被破環問題,利用Json序列化字符串就可以完美就解決地址引用問題
45             string datas = Newtonsoft.Json.JsonConvert.SerializeObject(ListData.ToList());
46             ObservableCollection<CheckModel> list = Newtonsoft.Json.JsonConvert.DeserializeObject<ObservableCollection<CheckModel>>(datas);
47             CheckListData = new ObservableCollection<CheckModel>(GetCheckedItems(list));
48             #endregion
49             //#region 還原上面描述場景
50             //CheckListData = new ObservableCollection<CheckModel>(GetCheckedItems(ListData));
51             //#endregion
52         }
53         /// <summary>
54         /// 獲取選中項
55         /// </summary>
56         /// <param name="tree">需要遞歸的集合</param>
57         private ObservableCollection<CheckModel> GetCheckedItems(ObservableCollection<CheckModel> tree)
58         {
59             //用於存儲抓取的選中臨時數據
60             ObservableCollection<CheckModel> list = new ObservableCollection<CheckModel>();
61             if (tree.ToList().Count > 0)
62             {
63                 foreach (var item in tree)
64                 {
65                     //檢測選中狀態
66                     if (item.IsSelected != false) {
67                         //防呆,檢測集合空值。若為空值 直接進行添加當前對象
68                         if (item.ChildList!=null)
69                         {
70                             //很重要->>>遞歸抓取下一層集合數據賦予當前模型集合
71                             item.ChildList = GetCheckedItems(item.ChildList);
72                             //填充當前抓取的對象
73                             list.Add(item);
74                         }
75                         else {
76                             /// 填充當前抓取的對象
77                             list.Add(item);
78                         }
79                     }
80                 }
81             }
82             return list;
83         }
84     }

備註:

  原創文章禁止轉載 2022-02-18

Tags: