一、概述
有时候,单纯的命令绑定不一定能满足我们的开发需求,比如我们需要在命令绑定的时候传递一个参数,这个时候,我们就需要使用RelayCommand的泛型版本了。
RelayCommand的泛型版本的构造函数以下:
public RelayCommand(Action<T> execute, bool keepTargetAlive = false);
public RelayCommand(Action<T> execute, Func<T, bool> canExecute, bool keepTargetAlive = false);
构造函数传入的是委托类型的参数,Execute 和 CanExecute执行委托方法。
二、带一个参数的命令绑定
代码片段如下:
<StackPanel> <GroupBox Header="带string类型参数的命令" BorderBrush="#FF11519C" BorderThickness="1" FontSize="16" Foreground="#FFCDAA0C" Margin="2"> <StackPanel> <StackPanel Orientation="Horizontal"> <Label Content="UserList:" VerticalContentAlignment="Center" FontSize="20" ></Label> <Label Content="{Binding Path=UserList,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" FontSize="20" /> </StackPanel> <StackPanel Orientation="Horizontal"> <Label Content="UserName:" VerticalContentAlignment="Center" FontSize="20" ></Label> <TextBox Width="200" Name="tbUser"></TextBox> <Button Content="AddUser" Command="{Binding AddUserCommand}" CommandParameter="{Binding ElementName=tbUser,Path=Text}"></Button> <CheckBox Content="IsCanAdd" VerticalAlignment="Center" FontSize="16" IsChecked="{Binding IsCanAddUser, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></CheckBox> </StackPanel> </StackPanel> </GroupBox> </StackPanel>
private RelayCommand<string> addUserCommand; public RelayCommand<string> AddUserCommand { get { if (addUserCommand == null) { addUserCommand = new RelayCommand<string>(AddUser, (string p) => { return IsCanAddUser; }); } return addUserCommand; } set { addUserCommand = value; } } private void AddUser(string par) { UserList = UserList + " " + par; }
三、带多个参数的命令绑定
给命令传递多个参数,建议使用以下方式:
使用MultiBinding将多绑定的各个值转换成我们所需的对象或者实例模型,再传递给ViewModel中的命令。
代码片段如下:
<Window x:Class="MvvmLightDemo1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:MvvmLightDemo1" xmlns:cvt="clr-namespace:MvvmLightDemo1.Converter" xmlns:mvvm="http://www.galasoft.ch/mvvmlight" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" mc:Ignorable="d" Title="MVVMLIghtDemo1" Height="500" Width="700" > <Window.Resources> <cvt:UserInfoConverter x:Key="userInfoConverter"></cvt:UserInfoConverter> </Window.Resources> <Window.DataContext> <Binding Path="Main" Source="{StaticResource Locator}"></Binding> </Window.DataContext> <StackPanel> <StackPanel> <GroupBox Header="带string类型参数的命令" BorderBrush="#FF11519C" BorderThickness="1" FontSize="16" Foreground="#FFCDAA0C" Margin="2"> <StackPanel> <StackPanel Orientation="Horizontal"> <Label Content="UserList:" VerticalContentAlignment="Center" FontSize="20" ></Label> <Label Content="{Binding Path=UserList,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" FontSize="20" /> </StackPanel> <StackPanel Orientation="Horizontal"> <Label Content="UserName:" VerticalContentAlignment="Center" FontSize="20" ></Label> <TextBox Width="200" Name="tbUser"></TextBox> <Button Content="AddUser" Command="{Binding AddUserCommand}" CommandParameter="{Binding ElementName=tbUser,Path=Text}"></Button> <CheckBox Content="IsCanAdd" VerticalAlignment="Center" FontSize="16" IsChecked="{Binding IsCanAddUser, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></CheckBox> </StackPanel> </StackPanel> </GroupBox> </StackPanel> <StackPanel> <GroupBox Header="带对象类型参数的命令" BorderBrush="#FF11519C" BorderThickness="1" FontSize="16" Foreground="#FF127C0D" Margin="2"> <StackPanel> <StackPanel Orientation="Horizontal"> <Label Content="UserName:" FontSize="16" ></Label> <TextBox Width="200" Name="tbxUser" FontSize="16" /> <Label Content="Password:" FontSize="16" ></Label> <TextBox Width="200" Name="tbxPwd" FontSize="16" /> <Button Content="AddUser" Command="{Binding AddUserCommandWithObjPar}"> <Button.CommandParameter> <MultiBinding Converter="{StaticResource userInfoConverter}"> <Binding ElementName="tbxUser" Path="Text"/> <Binding ElementName="tbxPwd" Path="Text"/> </MultiBinding> </Button.CommandParameter> </Button> </StackPanel> <StackPanel Orientation="Horizontal"> <Label Content="Parameter:" FontSize="16" ></Label> <Label Content="{Binding ObjParameter}" FontSize="16" ></Label> </StackPanel> </StackPanel> </GroupBox> </StackPanel> <StackPanel> <GroupBox Header="事件转命令" BorderBrush="#FF11519C" BorderThickness="1" FontSize="16" Foreground="#FFCDAA0C" Margin="2"> <StackPanel> <StackPanel> <ListBox x:Name="lb" ItemsSource="{Binding ListBoxData}" BorderThickness="0" SelectedIndex="{Binding SelectIndex}" > <i:Interaction.Triggers> <i:EventTrigger EventName="SelectionChanged"> <mvvm:EventToCommand Command="{Binding SelectionChangedCommand}"/> </i:EventTrigger> </i:Interaction.Triggers> <ListBox.ItemsPanel> <ItemsPanelTemplate> <WrapPanel Width="{Binding ActualWidth,RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"/> </ItemsPanelTemplate> </ListBox.ItemsPanel> <ListBox.ItemTemplate> <DataTemplate> <Border BorderBrush="AntiqueWhite" BorderThickness="1"> <StackPanel Margin="2"> <Image Source="{Binding Img}" Width="96" Height="96"/> <TextBlock HorizontalAlignment="Center" Text="{Binding Info}"/> </StackPanel> </Border> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <Label Content="您选择的是:" FontSize="16" ></Label> <Label Content="{Binding Path=SelResult,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" FontSize="16" /> </StackPanel> </StackPanel> </GroupBox> </StackPanel> </StackPanel> </Window>
1 using GalaSoft.MvvmLight; 2 3 namespace MvvmLightDemo1.ViewModel 4 { 5 public class UserModel: ObservableObject 6 { 7 private string userName; 8 9 public string UserName 10 { 11 get { return userName; } 12 set 13 { 14 userName = value; 15 RaisePropertyChanged(); 16 } 17 } 18 19 private string passWord; 20 21 public string PassWord 22 { 23 get { return passWord; } 24 set 25 { 26 passWord = value; 27 RaisePropertyChanged(); 28 } 29 } 30 31 32 } 33 }
using MvvmLightDemo1.ViewModel; using System; using System.Linq; using System.Windows.Data; namespace MvvmLightDemo1.Converter { public class UserInfoConverter: IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (!values.Cast<string>().Any(text => string.IsNullOrEmpty(text)) && values.Count() == 2) { UserModel userModel = new UserModel() { UserName = values[0].ToString(), PassWord = values[1].ToString()}; return userModel; } return null; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } }
private RelayCommand<UserModel> addUserCommandWithObjPar; public RelayCommand<UserModel> AddUserCommandWithObjPar { get { if (addUserCommandWithObjPar == null) { addUserCommandWithObjPar = new RelayCommand<UserModel>(AddUserWithObjPar); } return addUserCommandWithObjPar; } set { addUserCommandWithObjPar = value; } } private void AddUserWithObjPar(UserModel par) { ObjParameter = "UserName: "+ par.UserName + " Password: " + par.PassWord; }
后记:
从MVVM的模式来说,其实命令中的参数传递未必是必要的。与其维护一段额外的参数代码,还不如把所有的交互参数细化成为当前DataContext下的全局属性。View开发人员和ViewModel开发人员共同维护好这份命令清单和属性清单即可。
四、EventToCommand
在WPF中,并不是所有控件都有Command,例如TextBox,那么当文本改变,我们需要处理一些逻辑,这些逻辑在ViewModel中,没有Command如何绑定呢?
这个时候我们就用到EventToCommand,事件转命令,可以将一些事件例如TextChanged,Checked等事件转换成命令的方式。
接下来我们就以ListBox为例子,来看看具体的实例:
View代码:(这边声明了i特性和mvvm特性,一个是为了拥有触发器和行为附加属性的能力,当事件触发时,会去调用相应的命令,EventName代表触发的事件名称;一个是为了使用MVVMLight中 EventToCommand功能。)
这边就是当ListBox执行SelectionChanged事件的时候,会相应去执行ViewModel中 SelectionChangedCommand命令。
代码片段如下:
<Window x:Class="MvvmLightDemo1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:MvvmLightDemo1" xmlns:cvt="clr-namespace:MvvmLightDemo1.Converter" xmlns:mvvm="http://www.galasoft.ch/mvvmlight" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" mc:Ignorable="d" Title="MVVMLIghtDemo1" Height="500" Width="700" > <Window.Resources> <cvt:UserInfoConverter x:Key="userInfoConverter"></cvt:UserInfoConverter> </Window.Resources> <Window.DataContext> <Binding Path="Main" Source="{StaticResource Locator}"></Binding> </Window.DataContext> <StackPanel> <StackPanel> <GroupBox Header="带string类型参数的命令" BorderBrush="#FF11519C" BorderThickness="1" FontSize="16" Foreground="#FFCDAA0C" Margin="2"> <StackPanel> <StackPanel Orientation="Horizontal"> <Label Content="UserList:" VerticalContentAlignment="Center" FontSize="20" ></Label> <Label Content="{Binding Path=UserList,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" FontSize="20" /> </StackPanel> <StackPanel Orientation="Horizontal"> <Label Content="UserName:" VerticalContentAlignment="Center" FontSize="20" ></Label> <TextBox Width="200" Name="tbUser"></TextBox> <Button Content="AddUser" Command="{Binding AddUserCommand}" CommandParameter="{Binding ElementName=tbUser,Path=Text}"></Button> <CheckBox Content="IsCanAdd" VerticalAlignment="Center" FontSize="16" IsChecked="{Binding IsCanAddUser, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></CheckBox> </StackPanel> </StackPanel> </GroupBox> </StackPanel> <StackPanel> <GroupBox Header="带对象类型参数的命令" BorderBrush="#FF11519C" BorderThickness="1" FontSize="16" Foreground="#FF127C0D" Margin="2"> <StackPanel> <StackPanel Orientation="Horizontal"> <Label Content="UserName:" FontSize="16" ></Label> <TextBox Width="200" Name="tbxUser" FontSize="16" /> <Label Content="Password:" FontSize="16" ></Label> <TextBox Width="200" Name="tbxPwd" FontSize="16" /> <Button Content="AddUser" Command="{Binding AddUserCommandWithObjPar}"> <Button.CommandParameter> <MultiBinding Converter="{StaticResource userInfoConverter}"> <Binding ElementName="tbxUser" Path="Text"/> <Binding ElementName="tbxPwd" Path="Text"/> </MultiBinding> </Button.CommandParameter> </Button> </StackPanel> <StackPanel Orientation="Horizontal"> <Label Content="Parameter:" FontSize="16" ></Label> <Label Content="{Binding ObjParameter}" FontSize="16" ></Label> </StackPanel> </StackPanel> </GroupBox> </StackPanel> <StackPanel> <GroupBox Header="事件转命令" BorderBrush="#FF11519C" BorderThickness="1" FontSize="16" Foreground="#FFCDAA0C" Margin="2"> <StackPanel> <StackPanel> <ListBox x:Name="lb" ItemsSource="{Binding ListBoxData}" BorderThickness="0" SelectedIndex="{Binding SelectIndex}" > <i:Interaction.Triggers> <i:EventTrigger EventName="SelectionChanged"> <mvvm:EventToCommand Command="{Binding SelectionChangedCommand}"/> </i:EventTrigger> </i:Interaction.Triggers> <ListBox.ItemsPanel> <ItemsPanelTemplate> <WrapPanel Width="{Binding ActualWidth,RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"/> </ItemsPanelTemplate> </ListBox.ItemsPanel> <ListBox.ItemTemplate> <DataTemplate> <Border BorderBrush="AntiqueWhite" BorderThickness="1"> <StackPanel Margin="2"> <Image Source="{Binding Img}" Width="96" Height="96"/> <TextBlock HorizontalAlignment="Center" Text="{Binding Info}"/> </StackPanel> </Border> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <Label Content="您选择的是:" FontSize="16" ></Label> <Label Content="{Binding Path=SelResult,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" FontSize="16" /> </StackPanel> </StackPanel> </GroupBox> </StackPanel> </StackPanel> </Window>
using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.CommandWpf; using MvvmLightDemo1.Model; using System.Collections; using System.Collections.ObjectModel; using System.Windows.Input; namespace MvvmLightDemo1.ViewModel { public class MainViewModel : ViewModelBase { private WelcomeModel welcomeModel; public WelcomeModel WelcomeModel { get { return welcomeModel; } set { welcomeModel = value; RaisePropertyChanged(() => WelcomeModel); } } private string objParameter = ""; public string ObjParameter { get { return objParameter; } set { objParameter = value; RaisePropertyChanged(); } } private int selectIndex = -1; public int SelectIndex { get { return selectIndex; } set { selectIndex = value; RaisePropertyChanged(); } } private string selResult = ""; public string SelResult { get { return selResult; } set { selResult = value; RaisePropertyChanged(); } } private IEnumerable listBoxData = new ObservableCollection<DataModel>() { new DataModel(){ Img="/MvvmLightDemo1;component/Img/1.png",Info=" Honey Peach " }, new DataModel(){ Img="/MvvmLightDemo1;component/Img/2.png",Info="Tomato" }, new DataModel(){ Img="/MvvmLightDemo1;component/Img/3.png",Info="Banana" }, new DataModel(){ Img="/MvvmLightDemo1;component/Img/4.png",Info="Chilli " }, new DataModel(){ Img="/MvvmLightDemo1;component/Img/5.png",Info="Apple" }, }; /// <summary> /// LisBox数据模板 /// </summary> public IEnumerable ListBoxData { get { return listBoxData; } set { listBoxData = value; RaisePropertyChanged(() => ListBoxData); } } /// <summary> /// Initializes a new instance of the MainViewModel class. /// </summary> public MainViewModel() { WelcomeModel = new WelcomeModel() { WelcomeMsg = "Welcome to MVVMLight World!" }; } private string userList = "Mary"; public string UserList { get { return userList; } set { userList = value; RaisePropertyChanged(); } } private string user = ""; public string User { get { return user; } set { user = value; } } private bool isCanAddUser = true; public bool IsCanAddUser { get { return isCanAddUser; } set { isCanAddUser = value; } } #region Command private RelayCommand<string> addUserCommand; public RelayCommand<string> AddUserCommand { get { if (addUserCommand == null) { addUserCommand = new RelayCommand<string>(AddUser, (string p) => { return IsCanAddUser; }); } return addUserCommand; } set { addUserCommand = value; } } private void AddUser(string par) { UserList = UserList + " " + par; } private RelayCommand<UserModel> addUserCommandWithObjPar; public RelayCommand<UserModel> AddUserCommandWithObjPar { get { if (addUserCommandWithObjPar == null) { addUserCommandWithObjPar = new RelayCommand<UserModel>(AddUserWithObjPar); } return addUserCommandWithObjPar; } set { addUserCommandWithObjPar = value; } } private void AddUserWithObjPar(UserModel par) { ObjParameter = "UserName: "+ par.UserName + " Password: " + par.PassWord; } private RelayCommand selectionChangedCommand; public RelayCommand SelectionChangedCommand { get { if (selectionChangedCommand == null) { selectionChangedCommand = new RelayCommand(SelectChange); } return selectionChangedCommand; } set { selectionChangedCommand = value; } } private void SelectChange() { int i = 0; foreach(var a in ListBoxData) { if(i == SelectIndex) { SelResult = (a as DataModel).Info; break; } i++; } } #endregion } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MvvmLightDemo1.Model { public class DataModel { private string img; public string Img { get { return img; } set { img = value; } } private string info; public string Info { get { return info; } set { info = value; } } } }
运行结果如下:
五、带原事件参数的命令绑定
代码片段如下:
<StackPanel> <GroupBox Header="带事件参数的命令" BorderBrush="#FF11519C" BorderThickness="1" FontSize="16" Foreground="#FF127C0D" Margin="2"> <StackPanel Orientation="Horizontal"> <Button Content="拖拽上传文件" AllowDrop="True"> <i:Interaction.Triggers> <i:EventTrigger EventName="Drop"> <mvvm:EventToCommand PassEventArgsToCommand="True" Command="{Binding DropCommand}" /> </i:EventTrigger> </i:Interaction.Triggers> </Button> <Label Content="FilePath:" FontSize="16" ></Label> <Label Content="{Binding DraggedFilePath}" FontSize="16" ></Label> </StackPanel> </GroupBox> </StackPanel>
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) { DraggedFilePath = ((System.Array)e.Data.GetData(System.Windows.DataFormats.FileDrop)).GetValue(0).ToString(); }