用wpf设计树状单选和多选功能_可分组的选择框控件(MVVM下)(Toggle样式 仿造单选框RadioButton,复选框CheckBox功能)...

本文展示了如何在WPF中设计支持分组的单选和多选功能,利用MVVM模式实现。通过提供一个自定义的RelayCommand类,实现了命令的创建和执行,并给出了前台XAML代码、样式和动画,以及ViewModel和相关数据结构的定义。文章最后提及了功能的实现和可能出现的问题,鼓励读者交流讨论。
摘要由CSDN通过智能技术生成

效果图如下:支持分组的单选框,复选框样式和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。有什么问题可以一起讨论!

现在开始坚持每天一贴!也算是项目经验的总结和分享吧。当然肯定有很多不足之处,希望大家多多指教!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值