zoukankan      html  css  js  c++  java
  • 利刃 MVVMLight 7:命令深入

     上面一篇我们大致了解了命令的基本使用方法和基础原理,但是实际在运用命令的时候会复杂的多,并且会遇到各种各样的情况。

    一、命令带参数的情况:

    如果视图控件所绑定的命令想要传输参数,需要配置 CommandParameter 属性 ,用来传输参数出去。

    而继承制Icommand接口的 RelayCommand又支持泛型的能力,这样就可以接受来自客户端请求的参数。

    public RelayCommand(Action<T> execute);构造函数传入的是委托类型的参数,Execute 和 CanExecute执行委托方法。

    所以,修改上篇的代码如下:

    View代码:

    复制代码
     1  <StackPanel Margin="10,20,0,50">
     2                     <TextBlock Text="传递单个参数" FontWeight="Bold" FontSize="12" Margin="0,5,0,5" ></TextBlock>
     3                     <DockPanel x:Name="ArgStr" >
     4                         <StackPanel DockPanel.Dock="Left" Width="240" Orientation="Horizontal" >
     5                             <TextBox x:Name="ArgStrFrom" Width="100" Margin="0,0,10,0"></TextBox>
     6                             <Button Content="传递参数" Width="100" HorizontalAlignment="Left" Command="{Binding PassArgStrCommand}" 
     7                                     CommandParameter="{Binding ElementName=ArgStrFrom,Path=Text}"  ></Button>
     8                         </StackPanel>
     9                         <StackPanel DockPanel.Dock="Right" Width="240" Orientation="Horizontal">
    10                             <TextBlock Text="{Binding ArgStrTo,StringFormat='接收到参数:{0}'}" ></TextBlock>
    11                         </StackPanel>
    12                     </DockPanel>
    13   </StackPanel>
    复制代码

     ViewModel代码:

    复制代码
     1 #region 传递单个参数
     2 
     3         private String argStrTo;
     4         //目标参数
     5         public String ArgStrTo
     6         {
     7             get { return argStrTo; }
     8             set { argStrTo = value; RaisePropertyChanged(() => ArgStrTo); }
     9         }
    10 
    11 #endregion
    12 
    13 #region 命令
    14 
    15         private RelayCommand<String> passArgStrCommand;
    16         /// <summary>
    17         /// 传递单个参数命令
    18         /// </summary>
    19         public RelayCommand<String> PassArgStrCommand
    20         {
    21             get
    22             {
    23                 if (passArgStrCommand == null)
    24                     passArgStrCommand = new RelayCommand<String>((p) => ExecutePassArgStr(p));
    25                 return passArgStrCommand;
    26 
    27             }
    28             set { passArgStrCommand = value; }
    29         }
    30         private void ExecutePassArgStr(String arg)
    31         {
    32             ArgStrTo = arg;
    33         }
    34 
    35 #endregion
    复制代码

      结果如下:

      

    二、多个参数的情况

    上面是单个参数传输的,如果需要传入多个参数,可能就需要以参数对象方式传入,如下:

    Model代码:

    复制代码
     1    public class UserParam
     2     {
     3         public String UserName { get; set; }
     4 
     5         public String UserPhone { get; set; }
     6 
     7         public String UserAdd { get; set; }
     8 
     9         public String UserSex { get; set; }
    10     }
    复制代码

      View代码:

    1 xmlns:model="clr-namespace:MVVMLightDemo.Model"
    复制代码
     1  <StackPanel Margin="10,0,0,50">
     2                     <TextBlock Text="传递对象参数" FontWeight="Bold" FontSize="12" Margin="0,5,0,5" ></TextBlock>
     3                     <DockPanel>
     4                         <StackPanel DockPanel.Dock="Left" Width="240">
     5                             <Button Command="{Binding PassArgObjCmd}"  Content="传递多个参数" Height="23" HorizontalAlignment="Left" Width="100">
     6                                 <Button.CommandParameter>
     7                                     <model:UserParam UserName="Brand" UserPhone="88888888" UserAdd="地址" UserSex="男" ></model:UserParam>
     8                                 </Button.CommandParameter>
     9                             </Button>
    10                         </StackPanel>
    11                         <StackPanel DockPanel.Dock="Right" Width="240" 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>
    16                         </StackPanel>
    17                     </DockPanel>
    18                 </StackPanel>
    复制代码

     ViewModel代码:

    复制代码
     1  #region 传递参数对象
     2 
     3         private UserParam objParam;
     4         public UserParam ObjParam
     5         {
     6             get { return objParam; }
     7             set { objParam = value; RaisePropertyChanged(() => ObjParam); }
     8         }
     9 
    10  #endregion
    11 
    12  #region 命令
    13         private RelayCommand<UserParam> passArgObjCmd;
    14         public RelayCommand<UserParam> PassArgObjCmd
    15         {
    16             get
    17             {
    18                 if (passArgObjCmd == null)
    19                     passArgObjCmd = new RelayCommand<UserParam>((p) => ExecutePassArgObj(p));
    20                 return passArgObjCmd;    
    21             }
    22             set { passArgObjCmd = value; }
    23         }
    24         private void ExecutePassArgObj(UserParam up)
    25         {
    26             ObjParam = up;
    27         }
    28  #endregion
    复制代码

      结果如下:

    三、动态绑定多个参数情况

    参数过来了,但是我们会发现这样的参数是我们硬编码在代码中的,比较死,一帮情况下是动态绑定参数传递,所以我们修改上面的代码如下:

    复制代码
    1  <StackPanel DockPanel.Dock="Left" Width="240">
    2                             <Button Command="{Binding PassArgObjCmd}"  Content="传递多个参数" Height="23" HorizontalAlignment="Left" Width="100">
    3                                 <Button.CommandParameter>
    4                                     <model:UserParam UserName="{Binding ElementName=ArgStrFrom,Path=Text}" UserPhone="88888888" UserAdd="地址" UserSex="男" ></model:UserParam>
    5                                 </Button.CommandParameter>
    6                             </Button>
    7   </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类 改成 支持具有依赖属性的对象,如下:

    复制代码
     1    /// <summary>
     2     /// 自定义类型
     3     /// </summary>
     4     public class UserParam : FrameworkElement //继承于FrameworkElement
     5     {
     6         /// <summary>
     7         /// .net属性封装
     8         /// </summary>
     9         public int Age
    10         {
    11             get //读访问器
    12             {
    13                 return (int)GetValue(AgeProperty);
    14             }
    15             set //写访问器
    16             {
    17                 SetValue(AgeProperty, value);
    18             }
    19         }
    20         
    21 
    22         /// <summary>
    23         /// 声明并创建依赖项属性
    24         /// </summary>
    25         public static readonly DependencyProperty AgeProperty =
    26             DependencyProperty.Register("Age", typeof(int), typeof(CustomClass), new PropertyMetadata(0, CustomPropertyChangedCallback), CustomValidateValueCallback);
    27         
    28 
    29         /// <summary>
    30         /// 属性值更改回调方法
    31         /// </summary>
    32         /// <param name="d"></param>
    33         /// <param name="e"></param>
    34         private static void CustomPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    35         {
    36 
    37         }
    38 
    39         /// <summary>
    40         /// 属性值验证回调方法
    41         /// </summary>
    42         /// <param name="value"></param>
    43         /// <returns></returns>
    44         private static bool CustomValidateValueCallback(object value)
    45         {
    46             return true;
    47         }
    48     }
    复制代码

       但是这种方式不建议。仅仅是为了传输参数而大费周章,写一堆额外的功能,而且通用性差,几乎每个实例都要写一个对象,也破坏了Wpf文档树的设计结构。

    更建议的方式如下,用多绑定的方式。将多绑定的各个值转换成你想要的对象或者实例模型,再传递给ViewModel。

    View代码:

    1 xmlns:common="clr-namespace:MVVMLightDemo.Common"
    1   <Grid.Resources>
    2             <common:UserInfoConvert x:Key="uic" />
    3   </Grid.Resources>
    复制代码
     1   <StackPanel Margin="10,0,0,50">
     2                     <TextBlock Text="动态参数传递" FontWeight="Bold" FontSize="12" Margin="0,5,0,5" ></TextBlock>
     3                     <StackPanel Orientation="Horizontal" >
     4                         <StackPanel Orientation="Vertical" Margin="0,0,10,0" >
     5                             <StackPanel Orientation="Horizontal" Margin="0,0,0,5" >
     6                                 <TextBlock Text="姓名" Width="80" ></TextBlock>
     7                                 <TextBox x:Name="txtUName" Width="200" />
     8                             </StackPanel>
     9                             <StackPanel Orientation="Horizontal" Margin="0,0,0,5" >
    10                                 <TextBlock Text="电话" Width="80" ></TextBlock>
    11                                 <TextBox x:Name="txtUPhone" Width="200" />
    12                             </StackPanel>
    13                             <StackPanel Orientation="Horizontal" Margin="0,0,0,5" >
    14                                 <TextBlock Text="地址" Width="80"></TextBlock>
    15                                 <TextBox x:Name="txtUAdd" Width="200"/>
    16                             </StackPanel>
    17                             <StackPanel Orientation="Horizontal" Margin="0,0,0,5" >
    18                                 <TextBlock Text="性别" Width="80" ></TextBlock>
    19                                 <TextBox x:Name="txtUSex" Width="200" />
    20                             </StackPanel>                          
    21                         </StackPanel>
    22 
    23                         <StackPanel>
    24                             <Button Content="点击传递" Command="{Binding DynamicParamCmd}">
    25                                 <Button.CommandParameter>
    26                                     <MultiBinding Converter="{StaticResource uic}">
    27                                         <Binding ElementName="txtUName" Path="Text"/>
    28                                         <Binding ElementName="txtUSex" Path="Text"/>
    29                                         <Binding ElementName="txtUPhone" Path="Text"/>
    30                                         <Binding ElementName="txtUAdd" Path="Text"/>
    31                                     </MultiBinding>
    32                                 </Button.CommandParameter>
    33                             </Button>
    34                         </StackPanel>
    35 
    36                         <StackPanel Width="240" Orientation="Vertical" Margin="10,0,0,0" >
    37                             <TextBlock Text="{Binding ArgsTo.UserName,StringFormat='姓名:{0}'}" ></TextBlock>
    38                             <TextBlock Text="{Binding ArgsTo.UserPhone,StringFormat='电话:{0}'}" ></TextBlock>
    39                             <TextBlock Text="{Binding ArgsTo.UserAdd,StringFormat='地址:{0}'}" ></TextBlock>
    40                             <TextBlock Text="{Binding ArgsTo.UserSex,StringFormat='性别:{0}'}" ></TextBlock>
    41                         </StackPanel>                        
    42                     </StackPanel>
    43   </StackPanel>
    复制代码

     转换器 UserInfoConvert 代码:

    复制代码
     1   public class UserInfoConvert : IMultiValueConverter
     2     {
     3         /// <summary>
     4         /// 对象转换
     5         /// </summary>
     6         /// <param name="values">所绑定的源的值</param>
     7         /// <param name="targetType">目标的类型</param>
     8         /// <param name="parameter">绑定时所传递的参数</param>
     9         /// <param name="culture"><系统语言等信息</param>
    10         /// <returns></returns>
    11         public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    12         {
    13             if (!values.Cast<string>().Any(text => string.IsNullOrEmpty(text)) && values.Count() == 4)
    14             {
    15                 UserParam up = new UserParam() { UserName = values[0].ToString(), UserSex = values[1].ToString(), UserPhone = values[2].ToString(), UserAdd = values[3].ToString() };
    16                 return up;
    17             }
    18 
    19             return null;
    20         }
    21 
    22         public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    23         {
    24             throw new NotImplementedException();
    25         }
    26     }
    复制代码

      ViewModel代码:

    复制代码
     1    #region 动态参数传递
     2 
     3         private UserParam argsTo;
     4         /// <summary>
     5         /// 动态参数传递
     6         /// </summary>
     7         public UserParam ArgsTo
     8         {
     9             get { return argsTo; }
    10             set { argsTo = value; RaisePropertyChanged(() => ArgsTo); }
    11         }
    12 
    13    #endregion
    14  //=================================================================================================================
    15         private RelayCommand<UserParam> dynamicParamCmd;
    16         /// <summary>
    17         /// 动态参数传递
    18         /// </summary>
    19         public RelayCommand<UserParam> DynamicParamCmd
    20         {
    21             get
    22             {
    23                 if (dynamicParamCmd == null)
    24                     dynamicParamCmd = new RelayCommand<UserParam>(p => ExecuteDynPar(p));
    25                 return dynamicParamCmd;
    26             }
    27             set
    28             {
    29                
    30                 dynamicParamCmd = value;
    31             }
    32         }
    33 
    34         private void ExecuteDynPar(UserParam up)
    35         {
    36             ArgsTo = up;
    37         }
    复制代码

      效果如下:

    到这边,命令参数绑定相关的应该就比较清楚了,这种方式也比较好操作。

    个人观点:从MVVM的模式来说,其实命令中的参数传递未必是必要的。MVVM 的目标就是消除View和ViewModel开发人员之间过于频繁的数据交互。

    去维护一段额外的参数代码,还不如把所有的交互参数细化成在当前DataContext下的全局属性。View开发人员和ViewModel开发人员共同维护好这份命令清单和属性清单即可。

    而微软的很多控件也提供了类似  SelectedItem 和 SelectedValue之类的功能属性来辅助开发。

    四、传递原事件参数

    如果在一些特殊环境里,我们需要传递原事件的参数,那也很简单,只要设置 PassEventArgsToCommand="True" 即可,

    在ViewModel中对应接收参数即可。

    复制代码
     1  private RelayCommand<DragEventArgs> dropCommand;
     2         /// <summary>
     3         /// 传递原事件参数
     4         /// </summary>
     5         public RelayCommand<DragEventArgs> DropCommand
     6         {
     7             get
     8             {
     9                 if (dropCommand == null)
    10                     dropCommand = new RelayCommand<DragEventArgs>(e => ExecuteDrop(e));
    11                 return dropCommand;
    12             }
    13             set { dropCommand = value; }
    14         }    
    15 
    16         private void ExecuteDrop(DragEventArgs e)
    17         {
    18             FileAdd = ((System.Array)e.Data.GetData(System.Windows.DataFormats.FileDrop)).GetValue(0).ToString(); 
    19         }
    复制代码

     结果如下(将文件拖拽至红色区域内,会获取到拖拽来源,并解析参数显示出来):

    五、EventToCommand

         在WPF中,并不是所有控件都有Command,例如TextBox,那么当文本改变,我们需要处理一些逻辑,这些逻辑在ViewModel中,没有Command如何绑定呢?这

    个时候我们就用到EventToCommand,事件转命令,可以将一些事件例如TextChanged,Checked等转换成命令的方式。接下来我们就以下拉控件为例子,来看看具体的实例:

    View代码:(这边声明了i特性和mvvm特性,一个是为了拥有触发器和行为附加属性的能力,当事件触发时,会去调用相应的命令,EventName代表触发的事件名称;一个是为了使用MVVMLight中 EventToCommand功能。)

    这边就是当ComboBox执行SelectionChanged事件的时候,会相应去执行 SelectCommand 命令。

    1     xmlns:mvvm="http://www.galasoft.ch/mvvmlight"
    2     xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    复制代码
     1   <StackPanel Margin="10,0,0,50">
     2                     <TextBlock Text="事件转命令执行" FontWeight="Bold" FontSize="12" Margin="0,5,0,5" ></TextBlock>
     3                     <DockPanel x:Name="EventToCommand" >
     4                         <StackPanel DockPanel.Dock="Left" Width="240" Orientation="Horizontal" >
     5                             <ComboBox Width="130" ItemsSource="{Binding ResType.List}" DisplayMemberPath="Text" SelectedValuePath="Key" 
     6                           SelectedIndex="{Binding ResType.SelectIndex}" >
     7                                 <i:Interaction.Triggers>
     8                                     <i:EventTrigger EventName="SelectionChanged">
     9                                         <mvvm:EventToCommand Command="{Binding SelectCommand}"/>
    10                                     </i:EventTrigger>
    11                                 </i:Interaction.Triggers>
    12                             </ComboBox>
    13                         </StackPanel>
    14                         <StackPanel DockPanel.Dock="Right" Width="240" Orientation="Horizontal">
    15                             <TextBlock Text="{Binding SelectInfo,StringFormat='选中值:{0}'}" ></TextBlock>
    16                         </StackPanel>
    17                     </DockPanel>
    18  </StackPanel>
    复制代码

     ViewModel代码:

    复制代码
     1    private RelayCommand selectCommand;
     2         /// <summary>
     3         /// 事件转命令执行
     4         /// </summary>
     5         public RelayCommand SelectCommand
     6         {
     7             get
     8             {
     9                 if (selectCommand == null)
    10                     selectCommand = new RelayCommand(() => ExecuteSelect());
    11                 return selectCommand;
    12             }
    13             set { selectCommand = value; }
    14         }
    15         private void ExecuteSelect()
    16         {
    17             if (ResType != null && ResType.SelectIndex > 0)
    18             {
    19                 SelectInfo = ResType.List[ResType.SelectIndex].Text;
    20             }
    21         }
    复制代码

      结果如下:

    示例代码下载

  • 相关阅读:
    ORA00257 archiver error. 错误的处理方法
    Eclipse快捷键大全
    struts2 globalresults
    oracle创建表空间
    struts2 action中result参数详解
    struts2小程序登录验证
    清理系统垃圾文件 请命名为:*.bat
    网上免费阅读的计算机编程书籍列表
    eclipse+myeclipse+mysql+tomcat配置数据源
    o(∩_∩)o...哈哈 somethingaboutJAVA
  • 原文地址:https://www.cnblogs.com/123wang/p/6788854.html
Copyright © 2011-2022 走看看