【问题标题】:Is there a clean way to make CanExecute always return true with an attribute?有没有一种干净的方法可以使 CanExecute 始终返回带有属性的 true ?
【发布时间】:2010-03-03 01:16:18
【问题描述】:

我想我已经知道我的问题的答案,但我还是想把它放在那里。我有一个包含大量命令处理程序的应用程序,每个命令处理程序在其 CanExecute 方法中都有特殊逻辑,以适当地启用绑定按钮。

现在我处于不希望执行任何逻辑的情况,因为执行会导致调用我不希望发生的库只是为了 GUI 更新 .我无法删除库调用,因为它们对于应用程序其余部分的功能很重要。

我查看了 .NET 中的 Conditional 属性,遗憾的是,这不起作用,因为它们仅适用于返回 void 的方法。我可以使用#if 和#define 来使用我的逻辑或只返回true。我还可以在 viewmodel 中查询一个属性,并允许它确定是否只返回 true。

问题是,我并不懒惰,但我也不想做一堆繁重的工作来做出我猜想的修改是不可避免的。但是,如果有人知道在 .NET 中使用某些东西来自动启用我的按钮而无需调用 CanExecute 或至少避免使用底层逻辑的方法,请发布答案! :)

【问题讨论】:

  • 程序员编写程序来为他们做繁重的工作;-)
  • 这很有趣,我从来没有写过修改我的源代码的程序。我总是会编写实用程序来生成测试数据或做其他类型的繁重工作,但不会使用我的源代码。我应该这样做,嗯。 :)

标签: .net wpf data-binding attributes


【解决方案1】:

我不知道是否有属性的方法,但试试这个。在窗口启动代码中的某处循环遍历所有命令绑定并为每个绑定处理 PreviewCanExecute 事件。

public MainWindow()
{
    foreach (CommandBinding cb in CommandBindings)
    {
        cb.PreviewCanExecute += new CanExecuteRoutedEventHandler(cb_PreviewCanExecute);
    }
}

void cb_PreviewCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = false; // disable / enable all commands
    e.Handled = true; // Set this to skip calling your existing logic
}

【讨论】:

  • 哦,太棒了!今晚我会试一试。谢谢!
  • Brian,通常如何从 ViewModel 获取 CommandBindings?
  • CommandBindings 属性位于 Window 上,因此您需要在窗口中循环它们或将属性传递给我猜的视图模型。
  • 抱歉回复晚了 - 我确实查看了窗口中的 CommandBindings,但集合是空的。所以一定有某个对象包含所有这些对象。我会做一些搜索。
【解决方案2】:

我质疑为什么调用这个库的 GUI 逻辑不好。您不想启用不会执行任何操作的按钮,因此在它们实际上无法工作时禁用的命令可能是一件好事。

使用 MVVM 模式时,我的 ViewModel 通常会直接公开 ICommand。如果您使用 Prism,则有一个 DelegateCommand 实现,可以轻松地在您的 ViewModel 中实现这些命令。如果没有,或者 CanExecute 可能会改变,我会在单独的类中实现 ICommand。

如果你有一个长时间运行/昂贵的操作,你总是可以缓存库调用的结果。我假设如果状态更改将启用/禁用命令,您将收到事件或回调。在这种情况下,在您的 ICommand 实现中,您将引发 CanExecuteChanged 事件,以便 GUI 反映对命令的更改。下面是一个使用支持服务的命令示例,该服务知道何时可以完成操作:

public class ExpensiveCommand : ICommand
{
    private readonly IExpensiveService service;
    private bool canExecute;

    public ExpensiveCommand (IExpensiveService service)
    {
        this.service = service;
        canExecute = service.CanExecute();
        service.CanExecuteChanged += OnCanExecuteChanged;
    }

    public void Execute(object parameter)
    {
        service.Execute();
    }

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

    public event EventHandler CanExecuteChanged;
    private void OnCanExecuteChanged(object sender, EventArgs e)
    {
        canExecute = service.CanExecute();

        if (CanExecuteChanged != null)
            CanExecuteChanged(this, EventArgs.Empty);
    }
}

或者,如果您总是希望命令可执行,您可以只从 CanExecute 方法返回 true。

【讨论】:

  • 原因很简单,任何时候重新绘制窗口时,都会调用 Button 的所有 CanExecute 方法,这些方法会调用与物理设备对话的库。然后,这会导致通信总线上出现额外流量,如果没有必要,我们不希望对其进行筛选。
  • 您所说的关于 ICommands 的内容很有趣。到目前为止,我已经通过两种方式完成了按钮处理程序:实现按钮 Click 事件处理程序,以及将按钮绑定到 ICommand。从您所说的来看,听起来您几乎是在使用 ICommands,但不要使用 CanExecute / Execute。您能否指出我以前从未见过的这种方法的方向?
  • 我认为这就是你认为我正在做的事情:codingcontext.wordpress.com/2008/12/10/commandbindings-in-mvvm,但我正在使用 ICommands。
  • 是的,当您说“命令绑定”时,我以为您正在对 RoutedCommands 执行 CommandBindings。我会根据您的情况更新答案以反映 CanExecute/Execute。
  • 感谢您的更新,安倍。让我确认一下,我明白你在说什么。 View 绑定的 ExpensiveCommand 只是将其所有操作委托给从 IExpensiveService 继承的东西。这种“昂贵的服务”存在于 ViewModel 中,然后可能通过 VM 中的某些参数来决定 Command 是否可以执行;因此,ExpensiveCommand 也会知道,但不需要存在逻辑。总的来说,听起来像“一揽子”为所有命令禁用 CanExecute 的行为是相同的工作量。 (续)
猜你喜欢
  • 2021-02-18
  • 1970-01-01
  • 2018-06-30
  • 1970-01-01
  • 2011-06-12
  • 2012-10-14
  • 2015-03-08
  • 2011-07-28
  • 1970-01-01
相关资源
最近更新 更多