利刃 MVVMLight 7:命令深入
上面一篇我们大致了解了命令的基本使用方法和基础原理,但是实际在运用命令的时候会复杂的多,并且会遇到各种各样的情况。
一、命令带参数的情况:
如果视图控件所绑定的命令想要传输参数,需要配置 CommandParameter 属性 ,用来传输参数出去。
而继承制Icommand接口的 RelayCommand又支持泛型的能力,这样就可以接受来自客户端请求的参数。
public RelayCommand(Action<T> execute);构造函数传入的是委托类型的参数,Execute 和 CanExecute执行委托方法。
所以,修改上篇的代码如下:
View代码:
<StackPanel Margin="10,20,0,50">
<TextBlock Text="传递单个参数" FontWeight="Bold" FontSize="" Margin="0,5,0,5" ></TextBlock>
<DockPanel x:Name="ArgStr" >
<StackPanel DockPanel.Dock="Left" Width="" Orientation="Horizontal" >
<TextBox x:Name="ArgStrFrom" Width="" Margin="0,0,10,0"></TextBox>
<Button Content="传递参数" Width="" HorizontalAlignment="Left" Command="{Binding PassArgStrCommand}"
CommandParameter="{Binding ElementName=ArgStrFrom,Path=Text}" ></Button>
</StackPanel>
<StackPanel DockPanel.Dock="Right" Width="" Orientation="Horizontal">
<TextBlock Text="{Binding ArgStrTo,StringFormat='接收到参数:\{0\}'}" ></TextBlock>
</StackPanel>
</DockPanel>
</StackPanel>
ViewModel代码:
1 #region 传递单个参数 private String argStrTo;
//目标参数
public String ArgStrTo
{
get { return argStrTo; }
set { argStrTo = value; RaisePropertyChanged(() => ArgStrTo); }
} #endregion #region 命令 private RelayCommand<String> passArgStrCommand;
/// <summary>
/// 传递单个参数命令
/// </summary>
public RelayCommand<String> PassArgStrCommand
{
get
{
if (passArgStrCommand == null)
passArgStrCommand = new RelayCommand<String>((p) => ExecutePassArgStr(p));
return passArgStrCommand; }
set { passArgStrCommand = value; }
}
private void ExecutePassArgStr(String arg)
{
ArgStrTo = arg;
} #endregion
结果如下:
二、多个参数的情况
上面是单个参数传输的,如果需要传入多个参数,可能就需要以参数对象方式传入,如下:
Model代码:
public class UserParam
{
public String UserName { get; set; } public String UserPhone { get; set; } public String UserAdd { get; set; } public String UserSex { get; set; }
}
View代码:
xmlns:model="clr-namespace:MVVMLightDemo.Model"
<StackPanel Margin="10,0,0,50">
<TextBlock Text="传递对象参数" FontWeight="Bold" FontSize="" Margin="0,5,0,5" ></TextBlock>
<DockPanel>
<StackPanel DockPanel.Dock="Left" Width="">
<Button Command="{Binding PassArgObjCmd}" Content="传递多个参数" Height="" HorizontalAlignment="Left" Width="">
<Button.CommandParameter>
<model:UserParam UserName="Brand" UserPhone="88888888" UserAdd="地址" UserSex="男" ></model:UserParam>
</Button.CommandParameter>
</Button>
</StackPanel>
<StackPanel DockPanel.Dock="Right" Width="" Orientation="Vertical">
12 <TextBlock Text="{Binding ObjParam.UserName,StringFormat='姓名:\{0\}'}" ></TextBlock>
13 <TextBlock Text="{Binding ObjParam.UserPhone,StringFormat='电话:\{0\}'}" ></TextBlock>
14 <TextBlock Text="{Binding ObjParam.UserAdd,StringFormat='地址:\{0\}'}" ></TextBlock>
15 <TextBlock Text="{Binding ObjParam.UserSex,StringFormat='性别:\{0\}'}" ></TextBlock>
</StackPanel>
</DockPanel>
</StackPanel>
ViewModel代码:
#region 传递参数对象 private UserParam objParam;
public UserParam ObjParam
{
get { return objParam; }
set { objParam = value; RaisePropertyChanged(() => ObjParam); }
} #endregion #region 命令
private RelayCommand<UserParam> passArgObjCmd;
public RelayCommand<UserParam> PassArgObjCmd
{
get
{
if (passArgObjCmd == null)
passArgObjCmd = new RelayCommand<UserParam>((p) => ExecutePassArgObj(p));
return passArgObjCmd;
}
set { passArgObjCmd = value; }
}
private void ExecutePassArgObj(UserParam up)
{
ObjParam = up;
}
#endregion
结果如下:
三、动态绑定多个参数情况
参数过来了,但是我们会发现这样的参数是我们硬编码在代码中的,比较死,一帮情况下是动态绑定参数传递,所以我们修改上面的代码如下:
<StackPanel DockPanel.Dock="Left" Width="">
<Button Command="{Binding PassArgObjCmd}" Content="传递多个参数" Height="" HorizontalAlignment="Left" Width="">
<Button.CommandParameter>
<model:UserParam UserName="{Binding ElementName=ArgStrFrom,Path=Text}" UserPhone="" UserAdd="地址" UserSex="男" ></model:UserParam>
</Button.CommandParameter>
</Button>
</StackPanel>
这时候编译运行,他会提示:不能在“UserParam”类型的“UserName”属性上设置“Binding”。只能在 DependencyObject 的 DependencyProperty 上设置“Binding”。
原来,我们的绑定属性只能用在 DependencyObject 类型的控件对象上。像我们的 TextBox、Button、StackPanel等等控件都是
System.Windows.FrameworkElement => System.Windows.UIElement=> System.Windows.Media.Visual => System.Windows.DependencyObject 这样的一种继承方式。所以支持绑定特性。
Wpf的所有UI控件都是依赖对象。
一种方式就是将 UserParam类 改成 支持具有依赖属性的对象,如下:
/// <summary>
/// 自定义类型
/// </summary>
public class UserParam : FrameworkElement //继承于FrameworkElement
{
/// <summary>
/// .net属性封装
/// </summary>
public int Age
{
get //读访问器
{
return (int)GetValue(AgeProperty);
}
set //写访问器
{
SetValue(AgeProperty, value);
}
} /// <summary>
/// 声明并创建依赖项属性
/// </summary>
public static readonly DependencyProperty AgeProperty =
DependencyProperty.Register("Age", typeof(int), typeof(CustomClass), new PropertyMetadata(, CustomPropertyChangedCallback), CustomValidateValueCallback); /// <summary>
/// 属性值更改回调方法
/// </summary>
/// <param name="d"></param>
/// <param name="e"></param>
private static void CustomPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{ } /// <summary>
/// 属性值验证回调方法
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private static bool CustomValidateValueCallback(object value)
{
return true;
}
}
但是这种方式不建议。仅仅是为了传输参数而大费周章,写一堆额外的功能,而且通用性差,几乎每个实例都要写一个对象,也破坏了Wpf文档树的设计结构。
更建议的方式如下,用多绑定的方式。将多绑定的各个值转换成你想要的对象或者实例模型,再传递给ViewModel。
View代码:
xmlns:common="clr-namespace:MVVMLightDemo.Common"
<Grid.Resources>
<common:UserInfoConvert x:Key="uic" />
</Grid.Resources>
<StackPanel Margin="10,0,0,50">
<TextBlock Text="动态参数传递" FontWeight="Bold" FontSize="" Margin="0,5,0,5" ></TextBlock>
<StackPanel Orientation="Horizontal" >
<StackPanel Orientation="Vertical" Margin="0,0,10,0" >
<StackPanel Orientation="Horizontal" Margin="0,0,0,5" >
<TextBlock Text="姓名" Width="" ></TextBlock>
<TextBox x:Name="txtUName" Width="200" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,5" >
<TextBlock Text="电话" Width="" ></TextBlock>
<TextBox x:Name="txtUPhone" Width="200" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,5" >
<TextBlock Text="地址" Width=""></TextBlock>
<TextBox x:Name="txtUAdd" Width="200"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,5" >
<TextBlock Text="性别" Width="" ></TextBlock>
<TextBox x:Name="txtUSex" Width="200" />
</StackPanel>
</StackPanel> <StackPanel>
<Button Content="点击传递" Command="{Binding DynamicParamCmd}">
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource uic}">
<Binding ElementName="txtUName" Path="Text"/>
<Binding ElementName="txtUSex" Path="Text"/>
<Binding ElementName="txtUPhone" Path="Text"/>
<Binding ElementName="txtUAdd" Path="Text"/>
</MultiBinding>
</Button.CommandParameter>
</Button>
</StackPanel> <StackPanel Width="" Orientation="Vertical" Margin="10,0,0,0" >
<TextBlock Text="{Binding ArgsTo.UserName,StringFormat='姓名:\{0\}'}" ></TextBlock>
<TextBlock Text="{Binding ArgsTo.UserPhone,StringFormat='电话:\{0\}'}" ></TextBlock>
<TextBlock Text="{Binding ArgsTo.UserAdd,StringFormat='地址:\{0\}'}" ></TextBlock>
<TextBlock Text="{Binding ArgsTo.UserSex,StringFormat='性别:\{0\}'}" ></TextBlock>
</StackPanel>
</StackPanel>
</StackPanel>
转换器 UserInfoConvert 代码:
public class UserInfoConvert : IMultiValueConverter
{
/// <summary>
/// 对象转换
/// </summary>
/// <param name="values">所绑定的源的值</param>
/// <param name="targetType">目标的类型</param>
/// <param name="parameter">绑定时所传递的参数</param>
/// <param name="culture"><系统语言等信息</param>
/// <returns></returns>
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (!values.Cast<string>().Any(text => string.IsNullOrEmpty(text)) && values.Count() == )
{
UserParam up = new UserParam() { UserName = values[].ToString(), UserSex = values[].ToString(), UserPhone = values[].ToString(), UserAdd = values[].ToString() };
return up;
} return null;
} public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
ViewModel代码:
#region 动态参数传递 private UserParam argsTo;
/// <summary>
/// 动态参数传递
/// </summary>
public UserParam ArgsTo
{
get { return argsTo; }
set { argsTo = value; RaisePropertyChanged(() => ArgsTo); }
} #endregion
//=================================================================================================================
private RelayCommand<UserParam> dynamicParamCmd;
/// <summary>
/// 动态参数传递
/// </summary>
public RelayCommand<UserParam> DynamicParamCmd
{
get
{
if (dynamicParamCmd == null)
dynamicParamCmd = new RelayCommand<UserParam>(p => ExecuteDynPar(p));
return dynamicParamCmd;
}
set
{ dynamicParamCmd = value;
}
} private void ExecuteDynPar(UserParam up)
{
ArgsTo = up;
}
效果如下:
到这边,命令参数绑定相关的应该就比较清楚了,这种方式也比较好操作。
个人观点:从MVVM的模式来说,其实命令中的参数传递未必是必要的。MVVM 的目标就是消除View和ViewModel开发人员之间过于频繁的数据交互。
去维护一段额外的参数代码,还不如把所有的交互参数细化成在当前DataContext下的全局属性。View开发人员和ViewModel开发人员共同维护好这份命令清单和属性清单即可。
而微软的很多控件也提供了类似 SelectedItem 和 SelectedValue之类的功能属性来辅助开发。
四、传递原事件参数
如果在一些特殊环境里,我们需要传递原事件的参数,那也很简单,只要设置 PassEventArgsToCommand="True" 即可,
在ViewModel中对应接收参数即可。
private RelayCommand<DragEventArgs> dropCommand;
/// <summary>
/// 传递原事件参数
/// </summary>
public RelayCommand<DragEventArgs> DropCommand
{
get
{
if (dropCommand == null)
dropCommand = new RelayCommand<DragEventArgs>(e => ExecuteDrop(e));
return dropCommand;
}
set { dropCommand = value; }
} private void ExecuteDrop(DragEventArgs e)
{
FileAdd = ((System.Array)e.Data.GetData(System.Windows.DataFormats.FileDrop)).GetValue(0).ToString();
}
结果如下(将文件拖拽至红色区域内,会获取到拖拽来源,并解析参数显示出来):
五、EventToCommand
在WPF中,并不是所有控件都有Command,例如TextBox,那么当文本改变,我们需要处理一些逻辑,这些逻辑在ViewModel中,没有Command如何绑定呢?这
个时候我们就用到EventToCommand,事件转命令,可以将一些事件例如TextChanged,Checked等转换成命令的方式。接下来我们就以下拉控件为例子,来看看具体的实例:
View代码:(这边声明了i特性和mvvm特性,一个是为了拥有触发器和行为附加属性的能力,当事件触发时,会去调用相应的命令,EventName代表触发的事件名称;一个是为了使用MVVMLight中 EventToCommand功能。)
这边就是当ComboBox执行SelectionChanged事件的时候,会相应去执行 SelectCommand 命令。
xmlns:mvvm="http://www.galasoft.ch/mvvmlight"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<StackPanel Margin="10,0,0,50">
<TextBlock Text="事件转命令执行" FontWeight="Bold" FontSize="" Margin="0,5,0,5" ></TextBlock>
<DockPanel x:Name="EventToCommand" >
<StackPanel DockPanel.Dock="Left" Width="" Orientation="Horizontal" >
<ComboBox Width="" ItemsSource="{Binding ResType.List}" DisplayMemberPath="Text" SelectedValuePath="Key"
SelectedIndex="{Binding ResType.SelectIndex}" >
<i:Interaction.Triggers>
8 <i:EventTrigger EventName="SelectionChanged">
9 <mvvm:EventToCommand Command="{Binding SelectCommand}"/>
10 </i:EventTrigger>
11 </i:Interaction.Triggers>
</ComboBox>
</StackPanel>
<StackPanel DockPanel.Dock="Right" Width="" Orientation="Horizontal">
<TextBlock Text="{Binding SelectInfo,StringFormat='选中值:\{0\}'}" ></TextBlock>
</StackPanel>
</DockPanel>
</StackPanel>
ViewModel代码:
private RelayCommand selectCommand;
/// <summary>
/// 事件转命令执行
/// </summary>
public RelayCommand SelectCommand
{
get
{
if (selectCommand == null)
selectCommand = new RelayCommand(() => ExecuteSelect());
return selectCommand;
}
set { selectCommand = value; }
}
private void ExecuteSelect()
{
if (ResType != null && ResType.SelectIndex > )
{
SelectInfo = ResType.List[ResType.SelectIndex].Text;
}
}
结果如下:
转载请注明出处,谢谢
利刃 MVVMLight 7:命令深入的更多相关文章
- 利刃 MVVMLight 10:Messenger 深入
1.Messager交互结构和消息类型 衔接上篇,Messeger是信使的意思,顾名思义,他的目是用于View和ViewModel 以及 ViewModel和ViewModel 之间的消息通知和接收. ...
- 利刃 MVVMLight
已经很久没有写系列文章了,上一次是2012年写的HTLM5系列,想想我们应该是较早一批使用HTML5做项目的人. 相比我当时动不动100+的粉丝增长和两天3000+的阅读量,MVVM Light只能算 ...
- 利刃 MVVMLight 6:命令基础
在MVVM Light框架中,事件是WPF应用程序中UI与后台代码进行交互的最主要方式,与传统方式不同,mvvm中主要通过绑定到命令来进行事件的处理, 因此要了解mvvm中处理事件的方式,就必须先熟悉 ...
- 利刃 MVVMLight 1:MVVMLight介绍以及在项目中的使用
一.MVVM 和 MVVMLight介绍 MVVM是Model-View-ViewModel的简写.类似于目前比较流行的MVC.MVP设计模式,主要目的是为了分离视图(View)和模型(Model)的 ...
- 利刃 MVVMLight 2:Model、View、ViewModel结构以及全局视图模型注入器的说明
上一篇我们已经介绍了如何使用NuGet把MVVMLight应用到我们的WPF项目中.这篇我们来了解下一个基本的MVVMLight框架所必须的结构和运行模式. MVVMLight安装之后,我们 ...
- 利刃 MVVMLight 3:双向数据绑定
上篇我们已经了解了MVVM的框架结构和运行原理.这里我们来看一下伟大的双向数据绑定. 说到双向绑定,大家比较熟悉的应该就是AngularJS了,几乎所有的AngularJS 系列教程的开篇 ...
- 利刃 MVVMLight 5:绑定在表单验证上的应用
表单验证是MVVM体系中的重要一块.而绑定除了推动 Model-View-ViewModel (MVVM) 模式松散耦合 逻辑.数据 和 UI定义 的关系之外,还为业务数据验证方案提供强大而灵活的支持 ...
- 利刃 MVVMLight 9:Messenger
MVVM的目标之一就是为了解耦View和ViewModel.View负责视图展示,ViewModel负责业务逻辑处理,尽量保证 View.xaml.cs中的简洁,不包含复杂的业务逻辑代码. 但是在实际 ...
- 利刃 MVVMLight 8:DispatchHelper在多线程和调度中的使用
在应用程序中,线程可以被看做是应用程序的一个较小的执行单位.每个应用程序都至少拥有一个线程,我们称为主线程,这是在启动时调用应用程序的主方法时由操作系统分配启动的线程. 当调用和操作主线程的时候,该操 ...
随机推荐
- 1022: [SHOI2008]小约翰的游戏John
1022: [SHOI2008]小约翰的游戏John Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 1322 Solved: 829[Submit][ ...
- MSTP多实例的配置
MSTP多实例的配置 这次实验主要是为了加强对stp生成树协议中,RP(根端口),DP(指定端口),AP(阻塞端口)的判断方法:虽然很多时候不需要我们人工判断,因为当我们吧所有的配置好之后,然后开启生 ...
- poj 2892---Tunnel Warfare(线段树单点更新、区间合并)
题目链接 Description During the War of Resistance Against Japan, tunnel warfare was carried out extensiv ...
- web从入门开始(5)-----表单
1. 表单的概念 是用来获取客户端用户数据的(信息)的.如:注册表单,查询表单,登录表单等. 2. 表单的工作原理 1.浏览有表单的网页,填写一些必要的信息,然后单击某个按钮,进行提交. 2.这 ...
- 用ListView实现对数据库的内容显示
用ListView实现对数据库的内容显示 创建一个触发机制 ---------(作用)将数据读入ArrayList集合中 MyBase base = new MyBase(); SQLiteDatab ...
- 使用Java语言开发微信公众平台(四)——图文消息的发送与响应
在上一篇文章中,我们实现了被关注回复与关键词回复功能.在用户关注的时候自动推送功能菜单,并根据用户输入的关键词,回复特定信息.但是,我们只能回复文本消息给用户,如何才回复一条图文消息呢?本周,我们一起 ...
- AutoIt 脚本小试——刷网易云音乐歌单
AutoIt 确实是个很强大的脚本工具. 如果早知道有这个,当初是怎么都不会去学易语言的 (๑•̀ω•́๑) 这是个简单脚本 = ๛ก(ー̀ωー́ก) 用来增加歌单播放次数和个人的听歌量. 原理不过 ...
- Logistic Regression理论总结
简述: 1. LR 本质上是对正例负例的对数几率做线性回归,因为对数几率叫做logit,做的操作是线性回归,所以该模型叫做Logistic Regression. 2. LR 的输出可以看做是一种可能 ...
- iOS开发优秀博客和软件推荐
iOSBlogAndTools iOS开发优秀博客和软件推荐 本博客和工具列表由广大iOS开发者收集和推荐,如果大家有好的博客或者工具想要分享请点击:我要提交. 收到大家的提交后会及时收录与更新.Gi ...
- 查看apache,mysql,nginx,php的编译参数
查看nginx编译参数:/usr/local/nginx/sbin/nginx -V 查看apache编译参数:cat /usr/local/apache2/build/config.nice 查看m ...