效果图如下:支持分组的单选框,复选框样式和MVVM下功能的实现。这是项目中一个快捷键功能的扩展。
1,准备工作:VS2015 (15对WPF的支持变得异常的好,调试模式下允许自动更改属性。),随VS发布的Blend,几个基础类:
1 public classRelayCommand : ICommand2 {3 #region Fields
4
5 readonly Action_executeAct;6 readonly Predicate_canExecutePre;7 private readonlyAction _execute;8
9 private readonly Func_canExecute;10 #endregion
11
12 #region Constructors
13
14 ///
15 ///Creates a new command that can always execute.16 ///
17 /// The execution logic.
18 public RelayCommand(Actionexecute)19 : this(execute, null)20 {21 }22
23 ///
24 ///Creates a new command.25 ///
26 /// The execution logic.
27 /// The execution status logic.
28 public RelayCommand(Action execute, PredicatecanExecute)29 {30 if (execute == null)31 {32 throw new ArgumentNullException("execute");33 }34
35 _executeAct =execute;36 _canExecutePre =canExecute;37 }38
39
40 ///
41 ///Initializes a new instance of the RelayCommand class that42 ///can always execute.43 ///
44 /// The execution logic.
45 /// If the execute argument is null.
46 publicRelayCommand(Action execute)47 : this(execute, null)48 {49 }50
51 ///
52 ///Initializes a new instance of the RelayCommand class.53 ///
54 /// The execution logic.
55 /// The execution status logic.
56 /// If the execute argument is null.
57 public RelayCommand(Action execute, FunccanExecute)58 {59 if (execute == null)60 {61 throw new ArgumentNullException("execute");62 }63
64 _execute =execute;65 _canExecute =canExecute;66 }67
68 #endregion
69
70 #region ICommand Members
71
72 public bool CanExecute(objectparameter)73 {74 if (parameter == null)75 {76 return _canExecute == null ? true: _canExecute();77 }78
79 return _canExecutePre == null ? true: _canExecutePre(parameter);80 }81
82 public eventEventHandler CanExecuteChanged83 {84 add { CommandManager.RequerySuggested +=value; }85 remove { CommandManager.RequerySuggested -=value; }86 }87
88 public void Execute(objectparameter)89 {90 if (!CanExecute(parameter))91 return;92 if (parameter == null)93 {94 if (_execute != null)95 _execute();96 return;97 }98 if (_executeAct != null)99 _executeAct(parameter);100
101 }102
103 #endregion
104 }
View Code
1 public classModelBase : INotifyPropertyChanged2 {3 ///
4 ///属性改变事件5 ///
6 public eventPropertyChangedEventHandler PropertyChanged;7
8 ///
9 ///触发属性改变10 ///
11 ///
12 protected virtual void RaisePropertyChangedEvent(stringpropertyName)13 {14 ///一般写法
15 //PropertyChangedEventHandler handler = this.PropertyChanged;16 //if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
17 ///简易写法
18 PropertyChanged?.Invoke(this, newPropertyChangedEventArgs(propertyName));19 }20 }
View Code
2,前台XAML代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
93
94
95
96
97
98
99
100
101
102
105
106
107
108
109
110
111
112
113
View Code
使用的主要样式如下:(一般用Blend完成,这里加入了2个进入和退场动画)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
109
110
111
112
113
114
115
116
117
123
124
125
126
127
View Code
样式和颜色都是根据自己喜好来的,可以随意修改的。关键点事要用Path画出选中的钩,然后通过数据触发器将选中和不选中的姿态通过动画来展示:
3,定义ViewModel和相关结构:
首先是定义选中的3个状态:多行多选,多行单选,以及单行单选。(其它类型请自行添加和实现。)
public enum CheckType
{
MutilChecked,
SingleChecked,
RowChecked
}
然后是 设计层次结构:通用来讲我这里粗略的设计成嵌套类型,即数据源为List类型,而setmodel中又包含ObservableCollection类型,而这个tgmodel呢,其实就是精确到的每一个按钮对应的模型了。下面给出具体定义:
1 public classsetmodel : ModelBase2 {3 private string_name;4 ///
5 ///显示名称6 ///
7 public stringName8 {9 get { return_name; }10 set
11 {12 _name =value;13 RaisePropertyChangedEvent("Name");14 }15 }16
17 private string _groupname = "singlecheck";18 ///
19 ///显示名称20 ///
21 public stringGroupName22 {23 get { return_groupname; }24 set
25 {26 _groupname =value;27 RaisePropertyChangedEvent("GroupName");28 }29 }30 public ObservableCollection TgLists { get; set; }31
32
33 }
View Code
1 public classtgmodel : ModelBase2 {3 publictgmodel()4 {5 CommandDefine = newCommandDefine();6 }7 private stringcontent;8 ///
9 ///Toggle按钮显示内容10 ///
11 public stringContent12 {13 get { returncontent; }14 set
15 {16 content =value;17 RaisePropertyChangedEvent("Content");18 }19 }20
21
22 private boolischecked;23 ///
24 ///是否选中25 ///
26 public boolIsChecked27 {28 get { returnischecked; }29 set
30 {31 ischecked =value;32 RaisePropertyChangedEvent("IsChecked");33 }34 }35 public ICommand CheckedCommand { get; set; }36 public CommandDefine CommandDefine { get; set; }37 }
View Code
然后这里写了一个队togglebutton 信息的辅助类:主要是封装了togglebutton对应的模型 TgModel,它所在组的 上一级模型 SetModel,所在行的名称 RowName。
public class TgInfo
{
public tgmodel TgModel { get; set; }
public setmodel SetModel { get; set; }
public string RowName { get; set; }
//public CheckType CheckType { get; set; }
//public List SourceLists { get; set; }
}
4,最后就是功能的实现了,这里代码有点乱,还有进一步修改的可能。
(1)初始化DataContext,先是ViewModel:
SettingListsSingle 是我上面单行拿来做选择项的集合,下面的三行数据数据源则是
SettingLists。
1 public classViewModel : ModelBase2 {3 ///
4 ///设置列表5 ///
6 public List SettingLists { get; set; }7 public List SettingListsSingle { get; set; }8
9 privateCheckType _checktype;10 ///
11 ///选项类型12 ///
13 publicCheckType CheckType14 {15 get { return_checktype; }16 set
17 {18 _checktype =value;19 RaisePropertyChangedEvent("CheckType");20 }21 }22
23 privateCheckType _testchecktype;24 ///
25 ///选项类型26 ///
27 publicCheckType TestCheckType28 {29 get { return_testchecktype; }30 set
31 {32 _testchecktype =value;33 RaisePropertyChangedEvent("TestCheckType");34 }35 }36
37 }
初始化ViewModel,后台代码:
1 DataContext = newViewModel()2 {3 TestCheckType =CheckType.SingleChecked,4 SettingListsSingle = new List() {5 newsetmodel() {6 GroupName="GN0",7 Name="选中类型",8 TgLists =new ObservableCollection()9 { new tgmodel() { Content="多选"},10 new tgmodel() { Content="行单选"},11 new tgmodel() { Content="全部单选" ,IsChecked=true},12 }}, },13 CheckType =CheckType.RowChecked,14 SettingLists = new List() {15 newsetmodel() {16 GroupName="GN1",17 Name="测试数字",18 TgLists =new ObservableCollection()19 { new tgmodel() { Content="Test1"},20 new tgmodel() { Content="Test2"},21 new tgmodel() { Content="Test3"},22 new tgmodel() { Content="Test4"},23 new tgmodel() { Content="Test5",IsChecked=true},24 }},25 newsetmodel() {26 GroupName ="GN2",27 Name="测试字母",28 TgLists =new ObservableCollection()29 { new tgmodel() { Content="TestA"},30 new tgmodel() { Content="TestB"},31 new tgmodel() { Content="TestC"},32 new tgmodel() { Content="TestD"},33 new tgmodel() { Content="TestE",IsChecked=true},34 }},35 newsetmodel() {36 GroupName="GN3",37 Name="测试假名",38 TgLists =new ObservableCollection()39 { new tgmodel() { Content="Testあ"},40 new tgmodel() { Content="Testい"},41 new tgmodel() { Content="Testう"},42 new tgmodel() { Content="Testえ"},43 new tgmodel() { Content="Testお",IsChecked=true},44 }},45 }46 };
最后是命令的初始化:
1 var Vm = (DataContext asViewModel);2
3 Vm.SettingLists.ForEach(setmodel =>setmodel.TgLists.ToList().ForEach(4 (tgmodel=> tgmodel.CheckedCommand = new RelayCommand((pars) =>
5 {6 TgInfo info = newTgInfo7 {8 RowName =pars.ToString(),9 SetModel =setmodel,10 TgModel =tgmodel11 };12 SetTgStatus(info, Vm,Vm.CheckType, Vm.SettingLists);13 }))));14
15
16
17 Vm.SettingListsSingle.ForEach(setmodel =>setmodel.TgLists.ToList().ForEach(18 (tgmodel => tgmodel.CheckedCommand = new RelayCommand((pars) =>
19 {20 TgInfo info = newTgInfo21 {22 RowName =pars.ToString(),23 SetModel =setmodel,24 TgModel =tgmodel25 };26 SetTgStatus(info, Vm, Vm.TestCheckType, Vm.SettingListsSingle);27 }))));
设置命令实现如下:这里只是我的实现。
1 ///
2 ///设置选中状态3 ///
4 ///
5 ///
6 private void SetTgStatus(params object[] parms)7 {8 var tginfo = parms[0] asTgInfo;9 var tgmodel =tginfo.TgModel;10 var rowname =tginfo.RowName;11 var setmodel =tginfo.SetModel;12 var Vm = parms[1] asViewModel;13 var checktype = (CheckType)parms[2];14 var settings = parms[3] as List;15 if (setmodel.Name=="选中类型")16 {17 var index =setmodel.TgLists.IndexOf(tgmodel);18 Vm.CheckType = this[index];19 }20 if (checktype ==CheckType.RowChecked)21 {22 settings.ForEach(sets =>
23 sets.TgLists.Where24 (t => rowname == sets.GroupName&& t!=tgmodel).ToList().25 ForEach(tg => tg.IsChecked = false));26 }27 else if (checktype ==CheckType.SingleChecked)28 {29 settings.ForEach(sets =>
30 sets.TgLists.Where(t=>t!=tgmodel).ToList().31 ForEach(tg => tg.IsChecked = false));32 }33 else
34 {35
36 }37 }
其中CheckType 用到了索引器,实现如下:
1 public CheckType this[intindex]2 {3 get
4 {5 return index == 0 ?CheckType.MutilChecked :6 (index == 1 ?CheckType.RowChecked :7 CheckType.SingleChecked);8 }9
10 set
11 {12 }13 }
5,运行,OK。有什么问题可以一起讨论!
现在开始坚持每天一贴!也算是项目经验的总结和分享吧。当然肯定有很多不足之处,希望大家多多指教!