WPF Command(命令)详解

        WPF命令是一个任务的完整封装,例如保存,复制,剪切这些操作都可以理解为一个个独立的命令。乍一看,命令和传统的事件机制似乎很相似,但命令和事件并不冲突,命令和事件的区别就在于命令是具有约束力

WPF命令系统模型主要由以下四个元素组成:

  • 命令(Command):命令表示一个任务单元,并且可跟踪该任务的状态,实际上是实现了ICommand接口的类。
  • 命令源(CommandSource):即命令的发送者/行为的触发者,实际上是实现了ICommandSource接口的类。
  • 命令目标(CommandTarget):命令的接收者/命令的作用对象,命令目标必须是实现了 IInputElement接口的类。
  • 命令关联(CommandBinding):负责把一些外围逻辑与命令关联起来,是将命令逻辑映射到命令的对象,包括命令是否可以执行前后的操作、命令执行前后的操作。

              

命令的特点如下:
复用: 统一命令逻辑,减少代码冗余,封装独立、可复用的任务执行单元。
分离: 通过命令可以使命令源和命令目标分离,减少代码耦合。
状态同步: 命令可以使得控件的启用状态和相应的命令状态保持同步,从而指示操作是否可用。

ICommand接口
WPF命令模型的核心是System.Windows.Input.ICommand接口,该接口定义了命令的工作原理。该接口包含两个方法和一个事件:

public interface ICommand
{
    //触发程序事件处理
    void Execute(object parameter);
    //命令可用,返回true;命令不可用,返回false
    bool CanExecute(object parameter);
    //当命令状态改变时引发
    event EventHandler CanExecuteChanged;
}

RoutedCommand类
        当创建自己的命令时,不会直接实现ICommand接口;而是使用System.Windows.Input.RoutedCommand类,该类自动实现了ICommand接口。RoutedCommand类是WPF中唯一实现了ICommand接口的类。换句话说,所有WPF命令都是RoutedCommand类及其派生类的实例

        RoutedCommand类为事件冒泡和隧道添加了一些额外的基础结构。鉴于ICommand接口封装了命令的思想——可被触发的动作并可被启用或禁用——RoutedCommand类对命令进行了修改,使命令可在WPF元素层次结构中冒泡,以便获得正确的事件处理程序。
  为支持路由事件,RoutedCommand类私有地实现了ICommand接口,并添加了ICommand接口方法的一些不同版本。最明显的变化是,Execute()和CanExecute()方法使用了一个额外参数。下面的新的签名:

public void Execute(object parameter,IInputElement target)
{
    //...
}
 
public void CanExecute(object parameter,IInputElement target)
{
    //...
}

        参数target是开始处理事件的元素。事件从target元素开始,然后冒泡至高层的容器,直到应用程序为了执行合适的任务而处理了事件(为了处理Executed事件,元素还需要借助于另一个类——CommandBinding类的帮助)。
  除上面的修改外,RoutedCommand类还引入了三个属性:命令名称(Name属性)、包含命令的类(OwnerType)以及任何可用于触发命令的按键或鼠标操作(位于InputGestures集合中)。

RoutedUICommand类
        在程序中处理的大部分命令不是RoutedCommand对象,而是RoutedUICommand类的实例,RoutedUICommand类继承自RoutedCommand类(实际上,WPF提供的所有预先构建好的命令都是RoutedUICommand对象)。
  RoutedUICommand类用于具有文本的命令,这些文本显示在用户界面中的某些地方(例如菜单项文本、工具栏按钮的工具栏提示)。RoutedUICommand类只添加了Text属性,该属性是为命令显示的文本。
  为命令定义命令文本(而不是直接在控件上定义文本)的优点是可在某个位置执行本地化。但如果命令文本永远不会在用户界面的任何地方显示,那么RoutedUICommand类和RoutedCommand类是等效的。

以上三个类继承结构为RoutedUICommand->RoutedCommand->ICommand

三种命令实现方式可参考:WPF之命令Command - 简书

WPF命令库
        WPF设计者认识到,每个应用程序可能都有大量命令,并且对于许多不同的应用程序,很多命令时通用的,例如,所有基于文档的应用程序都有他们自己版本的New、Open以及Save命令。为减少创建这些命令所需的工作,WPF提供了基本命令库,基本命令库中保存的命令超过100条。这些命令通过以下5个专门的静态类的属性提供:

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

ApplicationCommands类提供了一组基本命令,在所有类别的应用程序中都经常会用到这些命令:New、Open、Close、Copy、Cut、Paste、Save、Delete、Stop等,如果你的程序中需要这些命令,那就不需要自己声明,直接拿来使用就可以。

以下示例显示了如何设置 MenuItem,以便在单击时它将调用 TextBox 上的 Paste 命令,假定 TextBox 具有键盘焦点:

<StackPanel>
    <Menu>
        <MenuItem Command="ApplicationCommands.Paste" />
    </Menu>
    <TextBox />
</StackPanel>

无Command属性控件实现Command绑定:

方法一:使用InputBingdings(包含KeyBinding和MouseBinding)

 <!--没有Command属性的控件可以使用KeyBinding或MouseBinding来绑定命令-->
 <TextBox Text="KeyBinding and MouseBinding" Height="40">
     <TextBox.InputBindings>
         <!--按下Alt+D,最大化窗体-->
         <KeyBinding Key="D" Modifiers="Alt" Command="SystemCommands.MaximizeWindowCommand"></KeyBinding>
         <!--KeyBinding绑定对象需要可聚焦,MouseBinding中MouseAction的优先级高于Gesture,当MouseAction设置以后,Gesture将失效-->
         <MouseBinding Gesture="LeftClick" MouseAction="RightClick"  Command="SystemCommands.RestoreWindowCommand"></MouseBinding>
     </TextBox.InputBindings>
 </TextBox>

方法二:使用Microsoft.Xaml.Behaviors库(System.Windows.Interactivity已被弃用)

<Window x:Class="WpfCommandDemo.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:WpfCommandDemo"
        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
        mc:Ignorable="d" WindowStartupLocation="CenterScreen"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
            <Border Width="100" Height="40" Grid.Row="1" Background="LightBlue">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="MouseLeftButtonDown">
                        <i:InvokeCommandAction Command="{Binding SelectedCommand}" CommandParameter="Microsoft.Xaml.Behaviors"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </Border>
    </Grid>
</Window>

方法三:使用附加属性绑定命令

后台:


    public class ElementAttachProperty
    {
        public static ICommand GetTextBoxItemSelectCommand(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(TextBoxItemSelectCommandProperty);
        }

        public static void SetTextBoxItemSelectCommand(DependencyObject obj, ICommand value)
        {
            obj.SetValue(TextBoxItemSelectCommandProperty, value);
        }

        public static readonly DependencyProperty TextBoxItemSelectCommandProperty =
            DependencyProperty.RegisterAttached("TextBoxItemSelectCommand", typeof(ICommand), typeof(ElementAttachProperty), new PropertyMetadata(OnTextBoxItemSelectCommand));

        private static void OnTextBoxItemSelectCommand(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var element = d as TextBox;
            element.TextChanged += Element_ItemSelecting;
        }

        private static void Element_ItemSelecting(object sender, EventArgs e)
        {
            var element = sender as TextBox;
            var command = (ICommand)element.GetValue(TextBoxItemSelectCommandProperty);
            command?.Execute(element.Text);
        }
    }

    public class MainViewModel
    {
        public MainViewModel()
        {

        }
        public ICommand ItemSelectedCmd
        {
            get { return new DelegateCommand(ItemSelected); }
        }
        private void ItemSelected(object o)
        {
            MessageBox.Show($"Command param is {o}");
        }
    }

前台:

 <TextBox Width="100" Height="30"   local:ElementAttachProperty.TextBoxItemSelectCommand="{Binding ItemSelectedCmd}" Margin="0,10,0,0"/>

使用自定义控件添加COMMAND依赖属性传递命令:

        右键项目添加"WPF自定义控件",命名CommandControl,在项目中会生成一个Themes文件夹,存放自定义控件样式主题文件Generic.xaml。具体代码如下:

CommandControl.cs文件:

    public class CommandControl : ContentControl
    {
        static CommandControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(CommandControl), new FrameworkPropertyMetadata(typeof(CommandControl)));
        }

        public CommandControl()
        {
            MouseLeftButtonDown += CommandControl_MouseLeftButtonDown;
        }

        private void CommandControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (Command != null)
            {
                if (Command.CanExecute(CommandParameter))
                {
                    Command.Execute(CommandParameter);
                }
            }
        }

        private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
        {
            if (Command != null)
            {
                if (Command.CanExecute(CommandParameter))
                {
                    Command.Execute(CommandParameter);
                }
            }
        }

        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.Register("Command", typeof(ICommand),
                typeof(CommandControl),
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));

        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.Register("CommandParameter", typeof(object),
                typeof(CommandControl),
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));

        public object CommandParameter
        {
            get { return (object)GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }

    }

Generic.xaml样式主题文件:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfCommandDemo">
    <Style TargetType="{x:Type local:CommandControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:CommandControl}">
                    <ContentControl>
                        <ContentPresenter/>
                    </ContentControl>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

自定义控件调用:

<Border Grid.Row="1" Grid.Column="1" Width="100" Height="40" Background="LightCoral">
    <local:CommandControl Command="{Binding ItemSelectedCmd}" CommandParameter="自定义控件命令">
        <TextBlock Text="UserCommand"/>
    </local:CommandControl>
                          
</Border>

参考:https://www.shuzhiduo.com/A/kmzLM3lbJG/

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
WPF(Windows Presentation Foundation)是一个用于创建 Windows 应用程序的框架,而 Prism 是一个基于 WPF 的框架,它提供了一组设计模式和工具,用于帮助开发人员创建可扩展、可重用和易于维护的应用程序。 在 Prism 中,ViewModel 是一个重要的组件,它的作用是将 View(即用户界面)与 Model(即数据和业务逻辑)解耦,使得两者可以独立进行开发和测试。 在 Prism 中,ViewModel 通常是一个简单的类,它实现了 INotifyPropertyChanged 接口,用于实现数据绑定。ViewModel 还包含了一些命令Command),用于处理用户交互事件,比如按钮点击、菜单选择等。ViewModel 还可以使用服务(Service)来访问 Model,以获取或操作数据。 下面是一个简单的 ViewModel 的示例: ```csharp public class MainViewModel : BindableBase { private readonly IMyService _myService; private string _name; public string Name { get { return _name; } set { SetProperty(ref _name, value); } } private ICommand _helloCommand; public ICommand HelloCommand { get { if (_helloCommand == null) { _helloCommand = new DelegateCommand(ExecuteHelloCommand); } return _helloCommand; } } public MainViewModel(IMyService myService) { _myService = myService; } private void ExecuteHelloCommand() { string message = _myService.GetMessage(Name); MessageBox.Show(message); } } ``` 在上面的代码中,MainViewModel 包含了一个字符串属性 Name 和一个 ICommand 属性 HelloCommand。Name 属性用于将用户界面中的文本框绑定到 ViewModel 中,而 HelloCommand 则表示用户点击“Hello”按钮时要执行的命令。HelloCommand 的实现是通过调用一个服务(IMyService)来获取一条消息,然后通过 MessageBox 显示出来。 需要注意的是,MainViewModel 的构造函数中注入了一个 IMyService 接口,这个接口封装了对数据和业务逻辑的访问,使得 ViewModel 可以与具体的实现解耦。这种依赖注入的方式可以使得应用程序的组件更加松散耦合,更加易于维护和测试。 总之,ViewModel 是 WPF Prism 中非常重要的一个组件,它用于实现视图与模型之间的解耦和交互。开发人员可以使用 ViewModel 实现数据绑定、命令处理、服务访问等功能,从而创建可扩展、可重用和易于维护的应用程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

无熵~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值