【问题标题】:WPF: How to implement Clear commandWPF:如何实现清除命令
【发布时间】:2020-04-30 01:40:11
【问题描述】:

我正在学习 WPF。

在其中一个练习中,我有一个文本框和剪切和粘贴按钮。以下内容足以实现剪切和粘贴功能:

XAML:

<DockPanel>
    <WrapPanel DockPanel.Dock="Top" Margin="3">
        <Button Command="ApplicationCommands.Cut"
                CommandTarget="{Binding ElementName=txtEditor}"
                Width="60">
                _Cut
        </Button>
        <Button Command="ApplicationCommands.Paste"
                CommandTarget="{Binding ElementName=txtEditor}"
                Width="60" Margin="3,0">
                _Paste<
        /Button>
    </WrapPanel>
    <TextBox AcceptsReturn="True" Name="txtEditor" />
    </DockPanel>

按下按钮后,Cut 按钮会在名称为 txtEditor 的 TextBox 上执行 ApplicationCommands.Cut。需要时,该按钮会询问名为 textEditor 的 TextBox 是否可以执行剪切命令,按下时会命令 textEditor 执行剪切命令。

相当简单。它工作正常。

只是为了好玩,我想实现另一个按钮:清除。当按下它应该清除文本框。 Textbox 类有一个 Clear 方法。

<Button Command="ApplicationCommands.Clear"
                CommandTarget="{Binding ElementName=txtEditor}"
                Width="60">
                Clear
        </Button>

唉,这行不通。 ApplicationCommands 没有 Clear。我应该按照this example 中的建议实现自定义命令吗?

我尝试了以下方法:

我在我的窗口中实现了 CanExecute 和 Executed 方法:

public partial class CustomCommandSample : Window
{
    public CustomCommandSample()
    {
        InitializeComponent();
    }

    private void ClearCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }

    private void ClearCommand_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        txtEditor.Clear();
    }
}

一个静态的 CustomCommands 类:

public static class CustomCommands
{
    public static RoutedUICommand Clear => new RoutedUICommand (
        "Clear",
        "Clear",
        typeof(CustomCommands));
}

最后是 XAML: (注意:本项目中的类在命名空间 WpfCommandDemo 中,Xaml 将其称为Local

<Window x:Class="WpfTutorialSamples.Commands.UsingCommandsSample"
    xmlns="...
    xmlns:local="clr-namespace:WpfCommandDemo"
    Title="UsingCommandsSample" Height="100" Width="200">

    <Window.CommandBindings>
        <CommandBinding Command="CustomCommands.Clear"
                        CanExecute="ClearCommand_CanExecute"
                        Executed="ClearCommand_Executed" />
    </Window.CommandBindings>

 <DockPanel>
    <WrapPanel DockPanel.Dock="Top" Margin="3">
        <Button Command="CustomCommands.Clear"
                CommandTarget="{Binding ElementName=txtEditor}"
                Width="60">
                Clear
        </Button>
        ... (other buttons: cut / paste, as above
    </WrapPanel>
        <TextBox AcceptsReturn="True" Name="txtEditor" />
    </DockPanel>

虽然可以编译,但 CustomCommandSample 的构造函数会抛出 XamlParseException:

Type reference cannot find type named 
'{http://schemas.microsoft.com/winfx/2006/xaml/presentation}CustomCommands'.

我应该使用自定义命令解决问题吗?我应该改变什么?还是我完全错了,我应该以不同的方式解决这个问题

【问题讨论】:

  • 你可以实现你自己的中继命令,看看这个thread

标签: c# wpf


【解决方案1】:

要在 XAML 中使用 CustomCommands,您需要添加对它的引用。在元素中,添加一行:

xmlns:custom="clr-namespace:MyApplication.NamespaceWithCustomInIt"

根据需要替换命名空间值。然后你应该可以在 XAML 中的任何地方引用 CustomCommands 为custom:CustomCommands(可能需要绑定,我稍后会检查)。

【讨论】:

    【解决方案2】:

    我应该使用自定义命令解决问题吗?

    是的。这是使用模型-视图-视图模型 (MVVM) 设计模式解决此问题的方法,这是在开发基于 XAML 的 UI 应用程序时推荐使用的设计模式。

    来自this 博文:

    WPF 提供了ICommand 接口的两种实现; System.Windows.Input.RoutedCommandSystem.Windows.Input.RoutedUICommand 其中后者是前者的子类,它只是添加了一个描述命令的Text 属性。但是,这些实现都不是特别适合在视图模型中使用,因为它们从焦点元素搜索可视化树,并在其CommandBindings 集合中查找具有匹配System.Windows.Input.CommandBinding 对象的元素,然后执行@ 987654328@ 代表这个特定的CommandBinding。由于命令逻辑应该驻留在视图模型中,因此您不想在视图中设置CommandBinding 以将命令连接到可视元素。相反,您可以通过创建实现ICommand 的类来创建自己的命令。下面的实现是为ExecuteCanExecute 方法调用委托的常见实现:

    public class DelegateCommand: System.Windows.Input.ICommand
    {
        private readonly Predicate<object> _canExecute;
        private readonly Action<object> _execute;
    
        public DelegateCommand(Action<object> execute)
            : this(execute, null) { }
    
        public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }
    
        public bool CanExecute(object parameter) => _canExecute == null ? true : _canExecute(parameter);
    
        public void Execute(object parameter) => _execute(parameter);
    
        public event EventHandler CanExecuteChanged;
    
        public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
    

    一旦你实现了ICommand 接口,它就很容易在你的视图模型中使用:

    public class ViewModel : INotifyPropertyChanged
    {
        public ViewModel()
        {
            ClearCommand = new DelegateCommand(Clear);
        }
    
        private string _text;
        public string Text
        {
            get { return _text; }
            set { _text = value; NotifyPropertyChanged(); }
        }
    
        public ICommand ClearCommand { get; }
    
        private void Clear(object parameter)
        {
            Text = string.Empty;
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    

    在视图中,您只需绑定到视图模型的属性:

    <TextBox AcceptsReturn="True" Name="txtEditor" Text="{Binding Text}" />
    <Button Content="Clear" Command="{Binding ClearCommand}" />
    

    只需记住将视图的DataContext 设置为视图模型的实例,以便绑定起作用:

    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-22
      • 2012-10-07
      • 2011-04-28
      • 2018-03-15
      • 1970-01-01
      相关资源
      最近更新 更多