博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
MVVM中的命令绑定及命令参数
阅读量:5742 次
发布时间:2019-06-18

本文共 5450 字,大约阅读时间需要 18 分钟。

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

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

注意图中红线部分。

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

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

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解决方法带参数

前台代码需修改的部分:

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;
    
}
}

运行代码,你会发现一切已经如我们所愿。这里不妨停下来提供一下源代码:

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,如需转载请自行联系原作者
你可能感兴趣的文章
java数组只能交换0下标和n_编程练习-只用0交换排序数组
查看>>
centos7安装mysql视频教程_centos7安装mysql(完整)
查看>>
php图片赋值,php如何优雅地赋值
查看>>
如何解决OutOfMemoryError
查看>>
【探索HTML5第二弹01】HTML5的前世今生以及来世
查看>>
Failed to connect to remote VM. Connection refused. Connection refused: connect
查看>>
freeze
查看>>
JS时间转时间戳,时间戳转时间。时间显示模式。
查看>>
SAP HANA存储过程结果视图调用
查看>>
设计模式 ( 十八 ):State状态模式 -- 行为型
查看>>
OracleLinux安装说明
查看>>
nova分析(7)—— nova-scheduler
查看>>
python Gunicorn
查看>>
Entity Framework 实体框架的形成之旅--Code First模式中使用 Fluent API 配置(6)
查看>>
Spring.net 学习笔记之ASP.NET底层架构
查看>>
I.MX6 wpa_cli 使用
查看>>
OpenMediaVault 搭建git,ssh无法连接问题
查看>>
[WPF]使用WindowChrome自定义Window Style
查看>>
java多线程之:Java中的ReentrantLock和synchronized两种锁定机制的对比 (转载)
查看>>
mysql性能优化学习笔记-参数介绍及优化建议
查看>>