WPF 命令Command
MVVM的目的是为了最大限度地降低了Xaml文件和CS文件的合度,分离界面和业务逻辑,所以我们要尽可能的在View后台不写代码。
但是这个例子中,我们将更新ViewModel的代码写在了View里。我们能否把按钮的响应处理代码也不写在后台代码里呢? WPF引入Command(命令),通过为Button设置Command来做响应: 命令:Command是一种不同于输入设备的语义级别上的入处理机制 Command的目的:1)降低代码耦合度,将Command的逻辑和调用对象进行分离2)可以指定对象是否可用;Command允许多个不同的对象可以调用同一个命令,也可以为不同的对象定义特殊的逻辑;
命令分类: 1.预定义的命令(predefined command)
1)ApplicationCommands 提供一组与应用程序相关的标准命令(可以直接使用
2)ComponentCommands 提供一组标准的与组件相关的命令
3)Navigationcommands,提供一-组标准的与导航相关的命令
还有很多诸如:MediaCommands,EditingCommands 2.自定义Command (1)command:要执行的操作。 所有的命令需要继承自接口ICommand. Execute:执行与命令关联的操作, CanExecute:决定对于当前目标command能否被执行。 CanExecuteChanged(事件):当出现影响是否应执行该命令的更改时发生
public class RelayCommand : ICommand { public event EventHandler CanExecuteChanged; private Action<object> executeActions; private Func<object, bool> canExecuteFunc; public RelayCommand() { } public RelayCommand(Action<object> execute) : this(execute, null) { } public RelayCommand(Action<object> execute, Func<object, bool> canExecute) { this.executeActions = execute; this.canExecuteFunc = canExecute; } public bool CanExecute(object parameter) { if (canExecuteFunc != null) return this.canExecuteFunc(parameter); else return true; } public void Execute(object parameter) { if (executeActions == null) return; this.executeActions(parameter); } public void OnCanExecutechanged() { this.CanExecuteChanged?.Invoke(this, new EventArgs()); } }
二、建立业务类 MainViewModel类 public class MainViewModel { //先实例化这个命令(这是属于ViewModel的命令,等下要被送到View中去) public RelayCommand MyCom { get; set; }public MainViewModel() { //在ViewModel的构造函数中,完成对命令的设置 MyCom = new RelayCommand(); MyCom.canExecuteFunc = new Func<object, bool>(this.CanDoSomething);//命令执行先进行判断,这里和MyCommand中的CanExecute都可以进行判断。 MyCom.executeActions = new Action<object>(this.DoSomething);//执行命令,带一个参数 //MyCom.ExecuteAction0 = new Action(this.DoSomething0);//执行命令,不带参数 } public void DoSomething(object param) { MessageBox.Show("123" + param);//执行方法,返回界面的命令带的参数 } public void DoSomething0() { MessageBox.Show("123");//执行方法,返回界面的命令带的参数 } public bool CanDoSomething(object param) { //这里可以和界面进行数据交互,进行判断。判断能否做这个事情,大部分时候返回true就行了,返回false,方法就不执行了。 return true; } }
3.在MainWindow中关联上MainViewModel。 public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.DataContext = new MainViewModel(); } }
<Window x:Class="WpfApp1.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:WpfApp1" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <Button Content="Button" HorizontalAlignment="Left" Margin="222,185,0,0" Command="{Binding MyCom}" CommandParameter="{Binding ElementName=txt, Path=Text}" VerticalAlignment="Top" Width="75"/> <TextBox x:Name="txt" HorizontalAlignment="Left" Height="23" Margin="222,128,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/> </Grid> </Window>
上面是传递单个值,下面说说多值传递的问题,传值有2种方法
第一种,建立一个对象,把对象当做整体传入
首先建立stu类
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public int Gender { get; set; }
public double Left { get; set; }
public double Top { get; set; }
}
public class MainViewModel { //先实例化这个命令(这是属于ViewModel的命令,等下要被送到View中去) public RelayCommand MyCom { get; set; } public Person person { get; set; } public MainViewModel() { //在ViewModel的构造函数中,完成对命令的设置 MyCom = new RelayCommand(); MyCom.canExecuteFunc = new Func<object, bool>(this.CanDoSomething);//命令执行先进行判断,这里和MyCommand中的CanExecute都可以进行判断。 MyCom.executeActions = new Action<object>(this.DoSomething);//执行命令,带一个参数 //MyCom.ExecuteAction0 = new Action(this.DoSomething0);//执行命令,不带参数 person = new Person() { Name = "aaa", Age = 3 }; } public void DoSomething(object param) { MessageBox.Show("123" + param);//执行方法,返回界面的命令带的参数 } public void DoSomething0() { MessageBox.Show("123");//执行方法,返回界面的命令带的参数 } public bool CanDoSomething(object param) { //这里可以和界面进行数据交互,进行判断。判断能否做这个事情,大部分时候返回true就行了,返回false,方法就不执行了。 return true; } }
第二种,使用多参数的方式传值
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data; namespace WpfApp1 { public class ObjectConvert : IMultiValueConverter { #region IMultiValueConverter Members public static object ConverterObject; public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return values.ToArray(); } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } #endregion } }
<Window x:Class="WpfApp1.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:WpfApp1" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Window.Resources> <local:ObjectConvert x:Key="objectConverter"></local:ObjectConvert> </Window.Resources> <Grid> <!--<Button Content="Button" HorizontalAlignment="Left" Margin="222,185,0,0" Command="{Binding MyCom}" CommandParameter="{Binding ElementName=txt, Path=Text}" VerticalAlignment="Top" Width="75"/>--> <!--<Button Content="Button" HorizontalAlignment="Left" Margin="222,185,0,0" Command="{Binding MyCom}" CommandParameter="{Binding a}" VerticalAlignment="Top" Width="75"/>--> <Button Content="Button" HorizontalAlignment="Left" Margin="222,185,0,0" Command="{Binding MyCom}" VerticalAlignment="Top" Width="75"> <Button.CommandParameter> <MultiBinding Converter="{ StaticResource ResourceKey=objectConverter}"> <Binding ElementName="txt"></Binding><!--传整个控件--> <Binding ElementName="txt_Copy" Path="Text"></Binding><!--传控件的值--> <Binding Path="a" ></Binding><!--传对象--> <Binding Source="自定义值" ></Binding> <!--传自定义的值--> </MultiBinding> </Button.CommandParameter> </Button> <TextBox x:Name="txt" HorizontalAlignment="Left" Height="23" Margin="222,128,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/> <TextBox x:Name="txt_Copy" HorizontalAlignment="Left" Height="23" Margin="412,172,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/> </Grid> </Window>
来源:https://blog.csdn.net/u012563853/article/details/124892976
内置命令(了解) WPF预定了一些命令及相关操作,方便我们在开发过程中快速的实现控件的行为处理。 1、常见内置命令 媒体命令(共24个) MediaCommands.Play、MediaCommands.Stop、MediaCommands.Pause、……. 应用命令(共23个) ApplicationCommands.New、ApplicationCommands.Open、ApplicationCommands.Copy、ApplicationCommands.Cut、ApplicationCommands.Print、……… 导航命令(共16个) NavigationCommands.GoToPage、NavigationCommands.LastPage、NavigationCommands.Favorites、…… 联合命令(共27个) ComponentCommands.ScrollByLine、ComponentCommands.MoveDown、ComponentCommands.ExtendSelectionDown、…… 编辑命令(共54个) EditingCommands.Delete、EditingCommands.ToggleUnderline、EditingCommands.ToggleBold、…… 2、内置命令的使用 内置命令的使用主要分以下几个步骤: 绑定内置命令 通过控件的Command属性绑定内置命令 <Button Command="ApplicationCommands.Open" ....../> 定义命令内容 在控件中,通过设置CommandManager的相关属性完成命令的执行内容以及命令可用判定条件的定义。 CommandManager.Executed:当前控件命令的执行内容。 CommandManager.CanExecute:当前控件命令的可用判定条件。 <Button Command="ApplicationCommands.Open" CommandManager.Executed="Button_Executed" CommandManager.CanExecute="Button_CanExecute" Content="{Binding RelativeSource={RelativeSource Self},Path=Command.Text}"/> private bool flag = true; private void Button_Executed(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show("命令被触发了"); flag = false; } private void Button_CanExecute(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = flag; } 在使用时候发现以下几点需要注意的地方: 绑定了内置命令后可以通过Command.Text属性获得内置命令对应的文本内容,这个好处是可以根据运行环境进行语言切换的。 内置命令具有快捷键,比如ApplicationCommands.Open的快捷键是Ctrl+o,快捷键会执行命令,但传给执行函数的sender参数为窗体对象。 不需要显示触发状态更新事件,例如上文中自定义命令时,是需要去触发CanExecuteChanged事件来更新命令的可用状态的。但这里是通过CommandManager来对命令进行管理的,WPF会自动触发去触发命令状态更新事件。当然,我们也可以通过CommandManager.InvalidateRequerySuggested()来进行主动触发。但由于WPF对于命令可用状态更新事件的触发是很频繁的,基本上没必要。 3、内置命令业务的统一定义 在上面例子中,虽然可以在控件中通过CommandManager来对命令的执行内容、判定条件进行定义,但如果由很多个控件使用了内置命令的话,一个个去定义会显得很繁杂。WPF为此提供了解决方案,可以通过容器元素的CommandBindings属性,对同一类型的内置命令进行统一的逻辑定义,使得在作用域范围内的命令都使用都一套业务逻辑。 <Window.CommandBindings> <CommandBinding Command="ApplicationCommands.Open" Executed="Button_Executed" CanExecute="Button_CanExecute"/> </Window.CommandBindings> <Grid> <StackPanel> <Button Command="ApplicationCommands.Open" Content="{Binding RelativeSource={RelativeSource Self},Path=Command.Text}" /> <Button Command="ApplicationCommands.Open" Content="{Binding RelativeSource={RelativeSource Self},Path=Command.Text}" /> </StackPanel> </Grid> 上面是通过Window元素进行ApplicationCommands.Open命令的统一,如果希望缩小作用域范围也可以通过<StackPanel.CommandBindings>来进行统一定义。 4、内置命令的终止 内置命令实质上是路由命令,普通的路由事件的消息传递是一样的,有隧道跟冒泡机制,可以通过设置Hendled属性进行命令的终止。 private void Button_Executed(object sender, ExecutedRoutedEventArgs e) { ...... e.Handled = true; } 5、使用内置命令自带业务 在使用过程中发现大部分的内置业务是需要自己重新做业务定义的,但也有个别内置命令自带的业务也挺好用的,比如ApplicationCommands.Copy,其自带业务是,当文本框绑定该命令时,如果用光标选中文本内容,则该命令对应控件为可用状态,执行命令则将选中内容进行复制。 <TextBox Name="tb"/> <Button Command="ApplicationCommands.Copy" CommandTarget="{Binding ElementName=tb}" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"/> 除了Copy外,比较好用的内置命令还有Cut、 Paste等等。 ———————————————— 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/jjailsa/article/details/135576133
五、路由命令
查阅内置命令的源码,可以看到两种路由命令类型:RoutedUICommand和RoutedCommand。
其实RoutedUICommand就是在继承了RoutedCommand的基础上新增了Text属性。
自定义路由命令
在xaml后台代码中定义对应属性:
public partial class MainWindow : Window
{
......
public RoutedUICommand MyRoutedCommand { get; set; }
public MainWindow()
{
InitializeComponent();
//快捷键组合
InputGestureCollection inputGestureCollection = new InputGestureCollection()
{
new KeyGesture(Key.T, ModifierKeys.Alt)
};
MyRoutedCommand = new RoutedUICommand("textContent", "commandName", typeof(MainWindow),inputGestureCollection);
}
......
}
在xaml中使用自定义路由命令
<Button Command="{Binding RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor},Path=MyRoutedCommand}"
CommandManager.Executed="Button_My_Executed"
CommandManager.CanExecute="Button_My_CanExecute"
Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"/>
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/jjailsa/article/details/135576133