zoukankan      html  css  js  c++  java
  • 基于wpf的相关设计问题Command的使用

         这篇来讨论Command基于ViewModel的基本使用.

    以prism内置Command Demo为例子,效果图如下

    image

    View相对应的ViewModel

    image

    1.OrderEditorView的Model就是OrdersEditorPresentationModel了

    2.SaveAllOrdersCommand可以在数据通过验证后同时保存,即同时触发三个Command

    3.每个Order都有一个Save的Command,这里Orders是一个OrderPresentationModel列表,注意这里OrdersEditorPresentationModel有一个View的属性(因为我们还没有Presenter来操作调配)

    一.路由命令

    路由命令由RoutedCommand实现,必须由当前使用的控件的父级控件在CommandBindings集合中注册命令.

    这里需要注意的是wpf内置的Command,如ApplicationCommands类有很多Command,其只是定义而已,并未真正实现,像TextBox这些控件内置已经实现了一些,复制粘贴功能,所以不要以为是内置Command实现,在注册这些内置的Command时,必须手动实现才可以.

    有些控件实现了内置的Command逻辑,你可以复用这些功能,这时候你就必须指定CommandTarget这个属性

    下面我们来看一下做法

    1.CreateCommandBinding负责注册命令,注册的Model均继承自CommandModel

    CommandModel commandModel = e.NewValue as CommandModel;
    if (commandModel != null)
    {
        element.CommandBindings.Add(new CommandBinding(commandModel.Command, commandModel.OnExecute, commandModel.OnQueryEnabled));
    }

    2.Model

    /// <summary>
    /// Model for a command
    /// </summary>
    public abstract class CommandModel
    {
        public CommandModel()
        {
            _routedCommand = new RoutedCommand();
        }
    
        /// <summary>
        /// Routed command associated with the model.
        /// </summary>
        public RoutedCommand Command
        {
            get { return _routedCommand; }
        }
    
        /// <summary>
        /// Determines if a command is enabled. Override to provide custom behavior. Do not call the
        /// base version when overriding.
        /// </summary>
        public virtual void OnQueryEnabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
            e.Handled = true;
        }
    
        /// <summary>
        /// Function to execute the command.
        /// </summary>
        public abstract void OnExecute(object sender, ExecutedRoutedEventArgs e);
    
        private RoutedCommand _routedCommand;
    }

    3.使用方法,这里分析一下其用法

    CreateCommandBinding附加属性Command省却了手动注册Command这一步骤

    Command还是绑定ViewModel的一个属性,但其处理的对象本应该在ViewModel中的,这里却用CommandParameter将一个与ViewModel绑定的参考重新传了进去,这是一个不好的地方.这是可以通过设计来改善的,因为在ViewModel定义出来的Command大多数是操作ViewModel对象的,只要想办法把定义的CommandModel的Command提取到ViewModel中,就可以直接操作ViewModel了,依靠CommandParamter来传值并不是一个好的办法.但注册Command这一步骤还是少不了的.

    <Button
    Command="{Binding UnblockCommandModel.Command}"
    CommandParameter="{Binding Path=SelectedItem, ElementName=_skillList}"
    luna:CreateCommandBinding.Command="{Binding UnblockCommandModel}">

       

    二.由prism提供的DelegateCommand和CompositeCommand


    DelegateCommand实现了ICommand接口(SaveOrderCommand便是一个DelegateCommand),wpf有一些控件实现了ICommandSource接口,具有Command属性和CommandParameter属性,两者皆为依赖属性,这就为数据绑定提供了便利.CompositeCommand的职责在于实现多个Command同时执行.如wpf的Button控件(注意:silverlight当前版本很多控件并未具有Command属性,所以prism使用附加属性的形式实现了这一过程),如下

    <Button Grid.Row="6" Grid.Column="1" Content="Save" 
            cal:Click.Command="{Binding Path=SaveOrderCommand}" 
             />

    wpf版本的

    <Button
            Grid.Row="6" Grid.Column="1" Content="Save" 
            Command="{Binding SaveOrderCommand}"></Button>

    其在ViewModel中的定义方法(参考包含Execute,CanExecute两个委托)

    this.SaveOrderCommand = new DelegateCommand<object>(this.Save, this.CanSave);

    CompositeCommand有两个方法用于注册(RegisterCommand)和注销(UnregisterCommand)想要同时触发Command的方法.其可以是一个静态属性

    <Button Command="{x:Static inf:OrdersCommands.SaveAllOrdersCommand}">Save All Orders</Button>

    这种做法其实也是为了把View和逻辑分开,将事件替换成Command,从而直接对ViewModel进行操作.

    三.阻止Command的触发

    由于RoutedCommand在设计时,做了相对多的封装,所以具备的功能也比较多.我们举个例子,在我们删除数据时,常要用MessageBox来提醒用户”确认要删除吗?”

    image

    CommandManager提供了4个额外的附加事件,Preview事件可以通过设置ExecutedRoutedEventArgs的Handled为True阻止Command的触发

    image

    <Button CommandManager.PreviewExecuted="Button_PreviewExecuted"
          Command="vm:PersonViewModel.SpeakCommand"
          CommandParameter="Howdy partner!"
          Content="Speak"
          Margin="0,0,6,0"
          Width="60"
          />

    private void Button_PreviewExecuted(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)
    {
        MessageBoxResult result = MessageBox.Show("Are you sure?", "MessageBox", MessageBoxButton.OKCancel);
        if (result == MessageBoxResult.OK)
        {
    
            e.Handled = false;
        }
        else
        {
            e.Handled = true;
        }
    }

    目前prism的DelegateCommand并为提供类似功能,你也可以通过在PreviewMouseLeftButtonDown事件中设置Handled为True阻止其触发.至于CallBack回调更是没有了,这些就需要我们自己想了.这也是在开发中常用到的问题.

  • 相关阅读:
    为什么 JVM 不用 JIT 全程编译?
    JVM Internals
    JIT与JVM的三种执行模式:解释模式、编译模式、混合模式
    Dart编译技术与平台
    Dart 库预览
    使用VSCode开发Flutter
    环境变量
    使用Homebrew管理你的mac开发包
    brew 又叫Homebrew,是Mac OSX上的软件包管理工具
    使用async/await消除callback hell
  • 原文地址:https://www.cnblogs.com/Clingingboy/p/1398882.html
Copyright © 2011-2022 走看看