WPF(命令)

       使用路由事件可响应广泛地鼠标和键盘动作。但是,事件是非常低级的元素。在实际应用程序中,功能被划分成一些高级的任务。通过将控件连接到命令,从而不需要重复编写事件处理代码,更重要的是,当连接的命令不可用时,命令特性通过自动禁用控件来管理用户界面状态。

一、命令模型 

WPF命令模型具有如下4个重要元素:

  • 命令:命令表示应用程序任务,并且跟踪任务是否能够执行。然而,命令实际上不包含执行应用程序任务的代码。
  • 命令绑定:每个命令绑定针对用户界面的具体局域,将命令连接到相关的应用程序逻辑。这种分解的设计非常重要,因为单个命令可用于应用程序中的多个地方,并且在每个地方具有不同的意义。为处理这一问题,需要将通一命令与不同的命令绑定。
  • 命令源:命令源触发命令。例如,MenuItem 和 Button 都是命令源,单击它们都会执行绑定命令。
  • 命令目标:命令目标是在其中执行命令的元素。根据命令的本质,目标可能很重要,也可能不重要。

二、命令库

WPF提供了基本的命令库,基本命令库中保存的命令超过100条。这些命令通过一下5个专门的静态类的静态属性提供:

  • ApplicationCommands:该类提供了通用命令,包括剪贴板命令(Copy、Cut、Paste)以及文档命令(New、Open、Save、Save As、Print等)。
  • NavigationCommands:该类提供了用于导航的命令,包括为基于页面的应用程序设计的一些命令(BrowseBack、BrowseForward、NextPage),以及其他适合于基于文档的应用程序的命令(IncreaseZoom、Refresh)。
  • EditingCommands:该类提供了许多重要的文档编辑命令,包括用于移动的命令(MoveToLineEnd、MoveLeftByWord、MoveUpByPage等),选择内容的命令(SelectToLineEnd、SelectLeftByWord),以及改变格式的命令(ToggleBold、ToggleUnderLine)。
  • ComponentCommands:该类提供了由用户界面组件使用的命令,包括用于移动和选择内容的命令,这些命令和 EditingCommands类中一些命令类似(甚至完全相同)。
  • MediaCommands:该类提供了一组用于处理多媒体的命令(如Play、Pause、NextTrack以及IncreaseVolume)。

三、命令源

 命令库中的命令始终可用。触发它们的最简单方法就是将它们关联到实现了 ICommandSource接口的控件,大多数是类似按钮类的控件,ICommandSource接口定义了三大属性:

ICommandSource 接口的属性
Command指向连接的命令,这是唯一必须的细节
CommandParameter提供其他希望随命令发送的数据
CommandTarget确定将在其中执行命令的元素
<Button Command="ApplicationCommands.New" Content="New"/>

四、命令绑定

当将命令关联到命令源时,由于命令还没有与其关联的绑定,所有按钮被认为是禁用的,通过控制 IsEnabled 属性。 

为改变这种状态,需要为命令创建绑定以明确以下三件事情:

  • 当命令被触发时执行什么操作
  • 如何确定命令是否能够被执行(可选,默认总是可用)
  • 命令在何处起作用。例如命令可被限制在单个按钮中使用或在整个窗口中 
<Window x:Class="WpfApp3.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:WpfApp3"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.CommandBindings>
        <CommandBinding Command="ApplicationCommands.New" Executed="CommandBinding_Executed"/>
    </Window.CommandBindings>
    <Grid>
        <Button Command="ApplicationCommands.New" Content="New"/>
    </Grid>
</Window>

通过代码创建绑定:

public MainWindow()
{
    InitializeComponent();

    CommandBinding commandBinding = new CommandBinding(ApplicationCommands.New);
    commandBinding.Executed += CommandBinding_Executed;
    this.CommandBindings.Add(commandBinding);
}

PS: 如是在CommandBindings中添加两个New命令,Button触发时只会触发第一个New命令Executed的事件。

       命令是通过事件冒泡进行工作的。尽管习惯上为窗口添加所有绑定,当CommandBindings属性实际实在UIElement基类中定义的。这意味着任何元素都支持该属性。也可处理CommandBinding.PreviewExecuted事件,首先在最高层次的容器(窗口)中引发该事件,然后隧道路由至按钮。和事件类似,在完成前,可通过事件隧道拦截和停止事件。如果将RoutedEventArgs.Handled属性设置为True,将永远不会发生 Executed 事件。 

可通过调用静态方法 CommandManager.InvalidateRequerySuggested() 强制WPF为所有正在使用的命令调用 CanExecute() 方法,然后命令管理器触发 RequerySuggested 事件,通知窗口中的命令源(按钮、菜单项等),此后命令源会重新查询它们链接的命令并相应地更新它们地状态。

五、自定义命令 

namespace WpfApp3
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Input;

    public class DataCommands
    {
        public static RoutedUICommand Requery { get; private set; }

        static DataCommands()
        {
            InputGestureCollection inputs = new InputGestureCollection();
            inputs.Add(new KeyGesture(Key.R, ModifierKeys.Control, "Ctrl+R"));
            Requery = new RoutedUICommand("Requery", nameof(Requery), typeof(DataCommands), inputs);
        }
    }
}
<Window x:Class="WpfApp3.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:local="clr-namespace:WpfApp3" 
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        Title="MainWindow" Width="800" Height="450" mc:Ignorable="d">
    <Window.CommandBindings>
        <CommandBinding Command="ApplicationCommands.New" Executed="CommandBinding_Executed" />
        <CommandBinding Command="local:DataCommands.Requery" Executed="CommandBindingRequery_Executed" />
    </Window.CommandBindings>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Button Command="ApplicationCommands.New" Content="New" />
        <TextBox Grid.Row="1" />
    </Grid>
</Window>

这个时候在窗体的任何地方按下 Ctrl+R都会触发Requery命令。 


1.CommandParameter  

<Window x:Class="WpfApp3.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:local="clr-namespace:WpfApp3" 
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        Title="MainWindow" Width="800" Height="450" mc:Ignorable="d">
    <Window.CommandBindings>
        <CommandBinding Command="local:DataCommands.Requery" Executed="CommandBindingRequery_Executed" />
    </Window.CommandBindings>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Button x:Name="btn1" Command="local:DataCommands.Requery" CommandParameter="1" Content="New" />
        <Button x:Name="btn2" Grid.Row="1" Command="local:DataCommands.Requery" CommandParameter="2" Content="Copy" />
    </Grid>
</Window>
namespace WpfApp3
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;

    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void CommandBindingRequery_Executed(object sender, ExecutedRoutedEventArgs e)
        {
        }
    }
}
  • 当我们在按钮上通过快捷键 Ctrl+R 触发命令时,sender=Window,e.OriginalSource=Window,e.Parameter=null
  • 当我们单击btn1时,sender=Window,e.OriginalSource=btn1,e.Parameter=1
  • 当我们单击btn2时,sender=Window,e.OriginalSource=btn2,e.Parameter=2

2.CommandTarget

<Window x:Class="WpfApp3.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:local="clr-namespace:WpfApp3" 
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        Title="MainWindow" Width="800" Height="450" mc:Ignorable="d">
    <Window.CommandBindings>
        <CommandBinding Command="local:DataCommands.Requery" Executed="CommandBindingRequery_Executed" />
    </Window.CommandBindings>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Button x:Name="btn1" Command="local:DataCommands.Requery" CommandParameter="1" Content="New" CommandTarget="{Binding ElementName=btn2}"/>
        <Button x:Name="btn2" Grid.Row="1" Command="local:DataCommands.Requery" CommandParameter="2" Content="Copy" />
    </Grid>
</Window>
  • 当我们在按钮上通过快捷键 Ctrl+R 触发命令时,sender=Window,e.OriginalSource=Window,e.Parameter=null
  • 当我们单击btn1时,sender=Window,e.OriginalSource=btn2,e.Parameter=1
  • 当我们单击btn2时,sender=Window,e.OriginalSource=btn2,e.Parameter=2

 六、ViewModel Command

namespace WpfApp3
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Input;

    public class RelayCommand : ICommand
    {
        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (_canExecute != null)
                {
                    CommandManager.RequerySuggested += value;
                }
            }
            remove
            {
                if (_canExecute != null)
                {
                    CommandManager.RequerySuggested -= value;
                }
            }
        }

        public bool CanExecute(object parameter)
        {
            if (_canExecute != null)
            {
                return _canExecute(parameter);
            }
            return true;
        }

        public virtual void Execute(object parameter)
        {
            if (_execute != null && CanExecute(parameter))
            {
                _execute(parameter);
            }
        }

        Action<object> _execute;
        Func<object, bool> _canExecute;

        public RelayCommand(Action<object> execute) : this(execute, null)
        {
        }

        public RelayCommand(Action<object> execute, Func<object, bool> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }

        public void RaiseCanExecuteChanged()
        {
        }

    }
}
namespace WpfApp3
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    public class ViewModel
    {

        public string Name { get; set; }
        public int Age { get; set; }

        public RelayCommand Update { get; private set; }

        public ViewModel()
        {
            Update = new RelayCommand(update);
        }

        void update(object param)
        {

        }
    }
}
<Window x:Class="WpfApp3.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:local="clr-namespace:WpfApp3" 
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        Title="MainWindow" Width="800" Height="450" mc:Ignorable="d" d:DataContext="{d:DesignInstance Type=local:ViewModel}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <TextBlock Text="Name:"/>
        <TextBox Text="{Binding Name}" Grid.Column="1"/>

        <TextBlock Text="Age:" Grid.Row="1" Grid.Column="0"/>
        <TextBox Text="{Binding Age}" Grid.Row="1" Grid.Column="1"/>

        <Button Command="{Binding Update}" Grid.Row="2" Grid.ColumnSpan="2" Content="Update" CommandParameter="8"/>
    </Grid>
</Window>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bridge_go

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值