【问题标题】:C# WPF built in ICommand Interface and the Command Design PattternC# WPF 内置 ICommand 接口和命令设计模式
【发布时间】:2019-03-11 19:19:42
【问题描述】:

我是 WPF 和 c# 的新手。我正在阅读命令设计模式,并期待在我的新应用程序上实现它,以便它可以具有执行和撤消功能。然后我意识到 WPF 已经带有一个 ICommand 接口,并且 de xml 按钮可以实现它。所述接口有以下方法:

public bool CanExecute(object parameter)
{
}

private void OnCanExecuteChanged()
{
}

public void Execute(object parameter)
{
}

那么,我是否应该通过从它继承一些其他接口并添加撤消方法来将撤消功能添加到已经内置的 ICommand 接口中?

这个界面只适用于按钮吗?我的意思是,我想创建许多其他不需要连接到 xnml 按钮的命令

提前致谢!

【问题讨论】:

    标签: c# wpf design-patterns command icommand


    【解决方案1】:

    首先,命令是 MVVM 模式的一部分,您应该首先了解它。

    C# 中的接口不提供任何功能,它们只描述继承该接口的类应该如何工作。如果你想让类做某事,你不应该把这些方法留空。

    WPF 中的命令代表一种框架,稍后将向其传输逻辑。命令最合乎逻辑的用法是将它们绑定到按钮。

    ICommand 实现示例:

    public class RelayCommand : ICommand
    {
        private readonly Action<object>     execute;
        private readonly Func<object, bool> canExecute;
    
        public event EventHandler CanExecuteChanged {
            add => CommandManager.RequerySuggested    += value;
            remove => CommandManager.RequerySuggested -= value;
        }
    
        public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
        {
            this.execute    = execute;
            this.canExecute = canExecute;
        }
    
        public bool CanExecute(object parameter)
        {
            return canExecute == null || canExecute(parameter);
        }
    
        public void Execute(object parameter)
        {
            execute(parameter);
        }
    }
    

    命令使用示例:

     public static RelayCommand NavigateToSignInPage => new RelayCommand(
            actionParameter => Application.Instance.Navigation.NavigateTo(new LoginForm()));
    
        public static RelayCommand NavigateToSignUpPage => new RelayCommand(
            actionParameter => Application.Instance.Navigation.NavigateTo(new RegistrationForm()));
    
        public static RelayCommand NavigateToStartPage => new RelayCommand(
            actionParameter => Application.Instance.Navigation.NavigateTo(new StartPage()));
    
        public static RelayCommand NavigateBack => new RelayCommand(
            actionParameter => Application.Instance.Navigation.NavigateBack(),
            actionPossibilityParameter => Application.Instance.Navigation.BackNavigationPossible);
    

    命令绑定示例: 在视图中 (xaml):

    <Button x:Name="CancelButton"
                Content="Cancel"
                Command="{Binding CancelCommand}"
                Grid.Row="2"
                IsCancel="True"
                HorizontalAlignment="Left"
                Margin="44,0,0,0"
                Width="118" Height="23"
                VerticalAlignment="Center" />
    

    在 ViewModel 中:

            public RelayCommand CancelCommand => NavigationCommands.NavigateBack;
    

    【讨论】:

      【解决方案2】:
      1. CanExecute会引导你是否执行Command,对于Command实现,你可以看到here
      2. 您的命令绑定到 Button 是绑定到单击而不是 Button 控件。所以所有的“命令”绑定,不限于点击,还可以检查、放下、聚焦等工作

      【讨论】:

        【解决方案3】:

        我认为您在这里对不同的事情感到困惑。 ICommand Interface 用于将 UI 事件绑定到设计模式中使用的代码,例如 MVVM。您尝试实现的Command Design Patternundoing actions 的状态架构,就像按下按钮一样。

        【讨论】:

        • 所以,我最好创建自己的 ICustomCommand 接口来实现 do-undo 功能。对吗?
        • 是的,如果您选择使用 MVVM,那将是额外的事情,我认为您不会这样做,因为它是一种有点复杂的设计模式。你想做的事情在这里描述得很好:how to use command pattern for undo
        【解决方案4】:

        许多答案都强调了使用委托的 ICommand 接口的有用实现。您甚至可以通过如下所示绑定到模型更改或任意过滤来走得更远,如果您愿意的话。这对于根据模型状态重新评估是否应该能够触发命令很有用,因为这直接与按钮的“启用”状态相关联。

        public class ObservableCommand<T> : ICommand where T : System.ComponentModel.INotifyPropertyChanged
        {
            Predicate<object> _predicate;
            Action<object> _execute;
            public ObservableCommand(T model, Action<object> execute, Predicate<object> predicate)
            {
                model.PropertyChanged += ModelChanged;
                _execute = execute;
                _predicate = predicate;
            }
        
            public event EventHandler CanExecuteChanged;
        
            private void ModelChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            {
                CanExecuteChanged?.Invoke(this, EventArgs.Empty);
            }
        
            public bool CanExecute(object parameter)
            {
                return _predicate(parameter);
            }
        
            public void Execute(object parameter)
            {
                _execute(parameter);
            }
        } 
        

        关于撤消操作的问题。您当然可以使用命令来调用它,但跟踪对象的状态是更棘手的部分。您可以使用memento design pattern 并将您的命令绑定到恢复状态功能。这是一种过度简化,但一直是我过去完成的实现的核心。

        public class Memento<T>
        {
            public Memento(T startState)
            {
                this.State = startState;
                this.OriginalState = startState;
            }
        
            public T State { get; set; }
        
            public T OriginalState { get; }
        
            public void RestorOriginalState()
            {
                State = OriginalState;
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2018-08-14
          • 2016-08-25
          • 1970-01-01
          • 1970-01-01
          • 2014-06-01
          • 1970-01-01
          • 2010-12-07
          • 1970-01-01
          • 2014-08-26
          相关资源
          最近更新 更多