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