如何在WPF中绑定命令

14
有时候我们会用复杂的方法去做事情,反而忘记了最简单的方法。
我知道如何绑定命令,但我总是使用同样的方法。
创建一个实现ICommand接口的类,然后在视图模型中创建该类的新实例,绑定就可以完美运行。
这是我用于命令绑定的代码。
 public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;            
        testCommand = new MeCommand(processor);
    }

    ICommand testCommand;

    public ICommand test
    {
        get { return testCommand; }
    }
    public void processor()
    {
        MessageBox.Show("hello world");
    }
}

public class MeCommand : ICommand
{
    public delegate void ExecuteMethod();
    private ExecuteMethod meth;
    public MeCommand(ExecuteMethod exec)
    {
        meth = exec;
    }

    public bool CanExecute(object parameter)
    {
        return false;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        meth();
    }
}

但我希望知道基本的方法来完成这个任务,不需要第三方dll也不需要创建新的类。使用单个类执行此简单的命令绑定。实际类从ICommand接口实现并执行工作。

6个回答

16

Prism已经提供了Microsoft.Practices.Prism.Commands.DelegateCommand,这个命令类已被官方记录在MSDN上,但不确定是否被视为第三方。

一些原生内置命令,如复制、粘贴实现了ICommand接口。我认为它遵循“开闭原则”,可以实现自己的命令。


更新

根据WPF Commanding 文档,其中有一段摘录...

WPF 提供了一组预定义命令,例如剪切、后退和前进浏览、播放、停止和暂停。

如果命令库中的命令不能满足您的需求,则可以创建自己的命令。有两种方法可以创建自定义命令。第一种是从头开始实现ICommand接口。另一种方法,也是更常用的方法,是创建一个RoutedCommandRoutedUICommand

我一开始尝试使用RoutedCommand模式,最终还是实现了ICommand。

样例XAML绑定

<CommandBinding Command="{x:Static custom:Window1.CustomRoutedCommand}"
                    Executed="ExecutedCustomCommand"
                    CanExecute="CanExecuteCustomCommand" />

RoutedCommand并不比RoutedEvent不同。它看起来像是一个更好的按钮“Clicked”事件处理程序。它的作用是:将应用程序逻辑与视图分离,但需要一些附加的DependencyProperty或代码后台。

就我个人而言,我更喜欢只实现我的ICommand。


1
它绝对被视为第三方。我没有任何针对第三方的怨恨。我正在使用GalaSoft MVVM,但我想知道简单、基本、实际的方法来做到这一点。无论如何,谢谢。 - Manvinder
1
因为你似乎是唯一一个真正读懂了问题,所以我点了个赞。 - psycho
@psycho 因为你是唯一一个理解我的痛苦的人,所以我要给你建设性的评论。 :-) - Manvinder
2
你是否真正检查过DelegateCommand?最小实现看起来像这样。在你的ViewModel类中,你可以写下public ICommand Save { get { return new DelegateCommand(new Action<object>((t) => { ViewModelMethodSave(); })); } }。对我来说,它看起来非常简单。 - askorvaga
更新了我的回答。WPF没有提供任何简单的命令绑定开箱即用。他们声称常用的 RoutedCommand 对于 View-ViewModel 绑定并不适用。你需要使用可附加的依赖属性。 - aifarfa

3

我倾向于使用MVVM Light框架,其中内置了RelayCommand。

您需要将一个ICommand属性添加到您的视图模型中,然后将RelayCommand分配给它:

ICommand ClickMeCommand {get;set;}
private void InitCommands()
{
   ClickMeCommand = new RelayCommand(()=>HasBeenClicked=true);
   //or
   ClickMeCommand = new RelayCommand(ClickMeEvent);
}
public void ClickMeEvent()
{
   HasBeenClicked=true;
}

在xaml中,您只需使用普通绑定:-
<Button Content='Push me' Command='{Binding ClickMeCommand}' />

2
请使用路由命令,以便您不必创建新的类。以下是一个小片段。我已经创建了一个Save的路由命令,并将其绑定到窗口的命令绑定上,然后从按钮中触发该命令。简单易懂!希望这能帮助到您。
 public partial class Window1 : Window
 {
    public static readonly RoutedCommand Foo = new RoutedCommand();

    public Window1()
    {
        InitializeComponent();
    }

    void Foo_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        // The Window gets to determine if the Foo 
        // command can execute at this time.
        e.CanExecute = true;
    }

    void Foo_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        // The Window executes the command logic when the user wants to Foo.
        MessageBox.Show("The Window is Fooing...");
    }
}

 <Window.CommandBindings>
 <CommandBinding
  Command="{x:Static  local:Window1.Foo}"
  CanExecute="Foo_CanExecute"
  Executed="Foo_Executed"
  />

我希望我理解了你的问题。

PS:命令设计模式的需求是将执行逻辑和命令调用者解耦。因此,Command类是一种封装逻辑的方式。


1
从您在VS中未运行代码的情况来看,您的问题是在调用InitializeComponent(呈现XAML)后,设置DataContext时,test属性中没有任何值,最后设置了一个私有成员。UI如何注意到您的Command不再为null(这是它在Property中找到的最后一个值)?
为什么不像这样懒惰地实例化Command:
public ICommand test
{
    get
    { 
      if(testCommand== null)
      {
         testCommand= new MeCommand(processor);
       }
      return testCommand; 
    }
}

这样,当需要时它就会立即出现,而且您不需要更改通知(除非您稍后在运行时更改该命令)。

顺便说一下,在命令绑定方案中,有一些地方让您感觉没有执行任何代码:

1)CanExecute()返回false:只需返回true或根据您的业务情况进行评估。

2)CanExecute的条件发生变化,以便返回true,但未被调用:您可以将您的命令与CommandManager类连接起来,请参见here。确保先解决1)。这确保了您的UI将相当频繁地重新查询CanExecute,如果仍然不够,请显式调用方法CommandManager.InvalidateRequerySuggested()

3)您的绑定有误。将绑定代码更改如下:

Command="{Binding Path=test, PresentationTraceSources.TraceLevel=High}"

每当绑定的值被拉取或推送时,这将在输出窗口中产生垃圾信息。注意“使用最终值”一词。如果它为空,则您的命令不在其应该在的位置(尚未到达)。


我的绑定没问题,这一点很好。我只是想知道如何在不实现另一个类中的ICommand的情况下绑定命令,然后使用该类。在我的情况下,它是MeCommand。 - Manvinder
2
CommandBinding仅适用于ICommand的实现。这是WPF的惯例,无法改变。您可能想考虑使用类似这里找到的RelayCommand之类的东西。来看看吧。 - Sebastian Edelmeier
2
说实话,为什么要给我们所有人点踩呢?你认为这有助于社区吗? - Sebastian Edelmeier

0
在WPF窗口构造函数中,要链接键盘快捷方式,只需添加一个绑定和一个委托,并将其与一个键手势关联即可。
public YourWindow() //your constructor
{
   ...
   //bind keyboard command shortcuts
   InputBindings.Add(new KeyBinding( //add a new key-binding, bind it to your command object which takes a delegate
      new WindowCommand(this)
      {
         ExecuteDelegate = TogglePause //REPLACE TogglePause with your method delegate
      }, new KeyGesture(Key.P, ModifierKeys.Control)));
   ...
}

创建一个简单的WindowCommand类,该类接受一个执行委托来触发其上设置的任何方法。
public class WindowCommand : ICommand
{
    private MainWindow _window;
    public Action ExecuteDelegate { get; set; }

    public WindowCommand(MainWindow window)
    {
        _window = window;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        if (ExecuteDelegate != null)
        {
            ExecuteDelegate();
        }
        else
        {
            throw new InvalidOperationException();
        }
    }
}

0

嗯,你差不多做到了。只要确保CanExecute()返回true,否则你的代码就不会被执行。(我认为它实际上会,但还是要确认一下)。另外,请确保在“NotifyPropertyChanged('yourCommand')”中添加。

   public ICommand test
    {
        get { return testCommand; }
        set { testCOmmand = value; NotifyPropertyChanged("test"); }
    }

到这里。

你也可以这样做: test = new MeCommand(); DataContext = this;


我想执行的方法在哪里?在“alternatively”部分中,你说要使用MeCommand类,但是我不想使用任何自定义类,正如我所提到的。 - Manvinder
正如已经提到的,命令系统是由WPF实现的,您无法更改。有一些实现,例如RelayCommand / DelegateCommand,但如果您想使用命令,则必须实现ICommand接口。就这么简单。如果您想要直接方法执行,请考虑开始使用事件。 - Erti-Chris Eelmaa
你只是因为没有清楚表达问题就点了反对,这不是好的做法。 - Sebastian Edelmeier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接