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


 

posted @ 2024-08-16 18:00  【君莫笑】  阅读(148)  评论(0编辑  收藏  举报