绑定到不在代码后台中的RoutedUICommand

7

我有一个包含RoutedUICommand的静态类,我想在绑定中使用它。

public static class CommandLibrary
{
    public static ProjectViewModel Project { get; set; }

    public static RoutedUICommand AddPage { get; private set; }

    static CommandLibrary()
    {
        AddPage = new RoutedUICommand("AddPage", "AddPage", typeof(CommandLibrary));

    }

    public static void AddPage_Executed(object sender, ExecutedRoutedEventArgs args)
    {
        Project.AddPage();
    }

    public static void AddPage_CanExecute(object sender, CanExecuteRoutedEventArgs args)
    {
        // We need a project before we can add pages.
        if (Project != null)
        {
            args.CanExecute = true;
        }
        else
        {
            // Did not find project, turning Add Page off.
            args.CanExecute = false;
        }
    }
}

当我尝试为此AddPage命令创建CommandBinding时,VS会抱怨说它找不到Window1中的AddPage_CanExecute...这毫无意义,因为我放置的代码应该使得这个XAML没问题:
<Window x:Class="MyProject.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MyProject">
    <Menu>
        <Menu.CommandBindings>
            <CommandBinding Command="local:CommandLibrary.AddPage" 
                            Executed="AddPage_Executed" CanExecute="AddPage_CanExecute" />
        </Menu.CommandBindings>
        <MenuItem Header="_Page">
            <MenuItem Header="_New" Command="local:CommandLibrary.AddPage" />
        </MenuItem>
    </Menu>
</Window>

我也尝试了不包含Menu.CommandBindings部分,只是简单地使用这段代码(根据这个问题的建议,但没有具体说明):

<MenuItem Header="_New" Command="{x:Static local:CommandLibrary.AddPage}" />

这样做可以停止错误的流,但是生成的菜单项总是被禁用!CanExecute似乎从未被调用。我认为在这种情况下绑定失败了,尽管更加安静。

为什么VS会讨厌我的命令并拒绝查找Executed和CanExecute方法的正确位置?我看到过很多例子(例如Matthew McDonald的Pro WPF和几个自定义命令教程),都像我一样这样做。

1个回答

12

CommandBinding与你的视觉树中的任何其他元素一样。在其上指定的任何事件都将由您的视觉树的根(在这种情况下是您的Window)处理。这意味着,如果您将AddPage_ExecutedAddPage_CanExecute移动到窗口的代码后台,它将起作用。这使您能够在许多UI组件中使用相同的命令但具有不同的处理程序。

我看到你的命令对你的视图模型执行了一些逻辑。为了节省时间和烦恼,请理解路由命令在这里是错误的解决方案。相反,将您的命令封装在您的视图模型中,类似于以下内容:

public class ProjectViewModel
{
    private readonly ICollection<PageViewModel> _pages;
    private readonly ICommand _addPageCommand;

    public ProjectViewModel()
    {
        _pages = new ObservableCollection<PageViewModel>();
        _addPageCommand = new DelegateCommand(AddPage);
    }

    public ICommand AddPageCommand
    {
        get { return _addPageCommand; }
    }

    private void AddPage(object state)
    {
        _pages.Add(new PageViewModel());
    }
}

DelegateCommandICommand的一种实现方式,它通过调用委托来执行和查询命令。这意味着命令逻辑都被封装在了命令里面,你不需要一个CommandBinding提供处理程序(你甚至不需要任何CommandBinding)。因此,你的视图只需要绑定到你的VM:

<MenuItem Header="_New" Command="{Binding AddPageCommand}"/>

我建议您阅读这一系列帖子以获得更多的上下文:


很棒的回答。给了我所需的一切。我已经阅读了一些MVVM教程,并在此之前看到过这种命令使用方式,但是我记不清如何实现它,因此我求助于我的书籍和搜索结果。感谢您的帮助。 - evizaer
漂亮。我为这个问题苦恼了很久...学习WPF+MVVM并不像我希望的那样直接。对于我的意图来说,RoutedUICommands太复杂了,我知道应该有更简单的方法。@Kent,感谢您的示例和博客。 - David Lay

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