MVVM中的命令绑定及命令参数

简介:

为了让本例支持更复杂的应用场景,我们这次要针对一个列表进行操作。

1:建立领域模型

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
public  class  StudentTeam: NotificationObject
     {
         string  teamName;
         public  string  TeamName
         {
             get
             {
                 return  teamName;
             }
             set
             {
                 teamName = value;
                 this .RaisePropertyChanged(() => this .TeamName);
             }
         }
 
         //获取模拟数据
         internal  List<Student> MockGetStudents()
         {
             List<Student> students = new  List<Student>();
             students.Add( new  Student() { FirstName = "f1" , LastName = "l1"  });
             students.Add( new  Student() { FirstName = "f2" , LastName = "l2"  });
             students.Add( new  Student() { FirstName = "f3" , LastName = "l3"  });
             students.Add( new  Student() { FirstName = "f4" , LastName = "l4"  });
             return  students;
         }
 
     }

领域模型完成的工作很简单,就是为了获取到一个详细的列表。同样,我们不关心数据来自与何方,这里只是进行了模拟。

2:建立ViewModel

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
public  class  StudentListViewModel : NotificationObject
{
     public  StudentListViewModel()
     {
         students = ( new  StudentTeam()).MockGetStudents();
     }
 
     List<Student> students;
 
     public  List<Student> Students
     {
         get
         {
             return  students;
         }
         set
         {
             students = value;
             this .RaisePropertyChanged(() => this .Students);
         }
     }
 
     string  someState = string .Empty;
 
     public  string  SomeState
     {
         get  { return  someState; }
         set  { someState = value; this .RaisePropertyChanged(() => this .SomeState); }
     }
 
 
     public  void  Select()
     {
         SomeState = "a" ;
     }
 
     public  void  UnSelect()
     {
         SomeState = "b" ;
     }
}

vm中方法Select用于绑定某条记录被选择时触发,UnSelect则用于取消选择时触发。

Students用于列表绑定,SomeState用于显示选定状态。

3:建立View

image

注意图中红线部分。

有一个细节要注意到,每一条记录都有一个CheckBox,由于采用了数据模版,默认CheckBox的绑定源对应的是ListBox的绑定源,而实际上,我们需要CheckBox对应的绑定源是VM对象,因为VM对象中才有Select和UnSelect方法,所以CheckBox的绑定源我们对应到了Grid上面。

现在,可以运行了。界面如下:

image

4:问题来了,如何让绑定方法带参数

由于Select和UnSelect都无法带参数,所以在VM中我们根本不知道是哪条记录被选中了。这个问题在这里特别突出,默认的命令绑定中Button的Command就是可以带参数的,但是这里的CallMethodAction就不允许带参数。

有人觉得在VM中新增一个属性用来绑定当前记录就可以的,但是个人认为这是一种非常丑陋的实现。

查看Prism和MVVM Light的源码,都创建了一个类似的类型,在Prism中是DelegateCommand,在MVVM Light中是RelayCommand,它们最终都继承自ICommand接口。使用这类类型,可以支持绑定带参数方法。

不过,即便我们不使用这两个框架,有几类方法仍然能支持使用参数。如:

1:在前台使用System.Windows.Interactivity命名空间下的InvokeCommandAction,在后台则使用Microsoft.Expression.Interactivity.Core下的ActionCommand也可以解决这个问题。当前SL子集中(4.0及以下)我没有直接实现ICommand接口的类型。

2:或者,干脆我们自己实现一个Command类型。

首先,我们来看第一种方法。

5:使用InvokeCommandAction和ActionCommand解决方法带参数

前台代码需修改的部分:

image

VM部分代码为:

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
public  class  StudentListViewModel : NotificationObject
{
     public  StudentListViewModel()
     {
         students = ( new  StudentTeam()).MockGetStudents();
         Selected = new  ActionCommand( this .Select);
         UnSelected = new  ActionCommand( this .UnSelect);
     }
 
     List<Student> students;
 
     public  List<Student> Students
     {
         get
         {
             return  students;
         }
         set
         {
             students = value;
             this .RaisePropertyChanged(() => this .Students);
         }
     }
 
     string  someState = string .Empty;
 
     public  string  SomeState
     {
         get  { return  someState; }
         set  { someState = value; this .RaisePropertyChanged(() => this .SomeState); }
     }
 
     public  ICommand Selected { get ; private  set ; }
 
     void  Select( object  obj)
     {
         SomeState = (obj as  Student).FirstName;
     }
 
     public  ICommand UnSelected { get ; private  set ; }
 
     void  UnSelect( object  obj)
     {
         SomeState = (obj as  Student).LastName;
     }
}

运行代码,你会发现一切已经如我们所愿。这里不妨停下来提供一下源代码:http://files.cnblogs.com/luminji/SilverlightApplication3.rar

6:实现自己的ICommand类型

所谓实现自己的ICommand,其实就是参考两大框架的代码,取而用之,在这里使用的MVVM Light的RelayCommand,并稍稍改之(泛型,其实你会注意到这里的泛型实现没有任何意义,但是我懒得再去修改了)。

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
public  class  RelayCommand<T> : ICommand where  T : class
  {
      private  readonly  Action<T> _execute;
 
      private  readonly  Func< bool > _canExecute;
 
      public  RelayCommand(Action<T> execute)
          : this (execute, null )
      {
      }
 
      public  RelayCommand(Action<T> execute, Func< bool > canExecute)
      {
          if  (execute == null )
          {
              throw  new  ArgumentNullException( "execute" );
          }
 
          _execute = execute;
          _canExecute = canExecute;
      }
 
      public  event  EventHandler CanExecuteChanged;
 
      public  void  RaiseCanExecuteChanged()
      {
          var  handler = CanExecuteChanged;
          if  (handler != null )
          {
              handler( this , EventArgs.Empty);
          }
      }
 
      public  bool  CanExecute( object  parameter)
      {
          return  _canExecute == null  ? true  : _canExecute();
      }
 
      public  void  Execute( object  parameter)
      {
          _execute(parameter as  T);
      }
  }

相应的,因为使用自己的ICommand类型,所以,VM的代码也稍稍进行了修改:

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
public  class  StudentListViewModel : NotificationObject
{
     public  StudentListViewModel()
     {
         students = ( new  StudentTeam()).MockGetStudents();
         Selected = new  RelayCommand<Student>( this .Select);
         UnSelected = new  RelayCommand<Student>( this .UnSelect);
     }
 
     List<Student> students;
 
     public  List<Student> Students
     {
         get
         {
             return  students;
         }
         set
         {
             students = value;
             this .RaisePropertyChanged(() => this .Students);
         }
     }
 
     string  someState = string .Empty;
 
     public  string  SomeState
     {
         get  { return  someState; }
         set  { someState = value; this .RaisePropertyChanged(() => this .SomeState); }
     }
 
     public  ICommand Selected { get ; private  set ; }
 
     void  Select(Student obj)
     {
         SomeState = obj.FirstName;
     }
 
     public  ICommand UnSelected { get ; private  set ; }
 
     void  UnSelect(Student obj)
     {
         SomeState = obj.LastName;
     }

这部分的代码可以直接重构上部分提供的源码,故不再提供下载了。

7:题外话

既然已经完全实现了自己创建类型的MVVM简单框架,想要把这部分功能使用Prism或Light来实现,就是轻而易举的事情了,只要将相应的类型用两个框架中对应的类型就可以了。

本文转自最课程陆敏技博客园博客,原文链接:http://www.cnblogs.com/luminji/archive/2011/05/30/2062977.html,如需转载请自行联系原作者
相关文章
|
5月前
|
数据格式
【vue2事件传参1】自定义参数:在elementui的change事件中,自定义参数的传递方法
【vue2事件传参1】自定义参数:在elementui的change事件中,自定义参数的传递方法
468 1
【vue2事件传参1】自定义参数:在elementui的change事件中,自定义参数的传递方法
|
5月前
|
JavaScript
如何使用`watch`选项来监听特定属性的变化?
如何使用`watch`选项来监听特定属性的变化?
35 0
|
C# 图形学
Unity通过组件名称字符串添加脚本
通过Type.GetType(string typeName)来得到字符串对应的Type。 Type.GetType(typeName)能获取到自定义类的类型,但是获取Unity的组件不行。 例如Type.GetType(“Rigidbody”)值为null,其实是少了程序集。 string qualifiedName = typeof(Rigidbody).AssemblyQualifiedName; ty 获取Unity的组件程序集全名,再通过Type.GetType()得到的就不为null了。
481 0
Unity通过组件名称字符串添加脚本
|
前端开发 C#
WPF 之 数据与命令绑定 (MVVM方式)
WPF 之 数据与命令绑定 (MVVM方式)
192 0
WPF 之 数据与命令绑定 (MVVM方式)
|
JavaScript
element-ui中下拉command传递多参数事件封装
element-ui中下拉command传递多参数事件封装
519 0
element-ui中下拉command传递多参数事件封装
TP5.1自定义命名空间使用命令行添加文件(修改源码)
TP5.1自定义命名空间使用命令行添加文件(修改源码)
123 0
TP5.1自定义命名空间使用命令行添加文件(修改源码)
|
JavaScript
Cypress系列(23)- 可操作类型的命令 之 trigger()
Cypress系列(23)- 可操作类型的命令 之 trigger()
324 0
Cypress系列(23)- 可操作类型的命令 之 trigger()
|
监控 JavaScript
|
前端开发
vue-router切换不同参数共用路由来显示页面的方法
遇到需要切换页面显示,重新请求接口但是前端路由不变时的一种处理方法
650 0