Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding to a RoutedUICommand that's not in the codebehind

I have a static class which contains a RoutedUICommand that I would like to use in binding.

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;
        }
    }
}

When I attempt to create a CommandBinding for this AddPage command, VS throws a tantrum, complaining that it can't find AddPage_CanExecute in Window1... Which makes no sense considering that all the examples I've seen indicate this XAML should be fine considering the code I have in place:

<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>

I've also tried not including the Menu.CommandBindings section and simply using this (as per this question which suggests this but is not specific):

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

That staunched the flow of errors but the menu item it generates is always disabled! CanExecute never seems to get called. I'm assuming the binding is failing in this case, as well, though more quietly.

Why does VS hate my command and refuse to look in the right place to find the Executed and CanExecute methods? I've seen a number of examples (in Pro WPF by Matthew McDonald and on several custom command tutorials online) that have done this as I am doing it.

like image 516
evizaer Avatar asked Aug 15 '09 04:08

evizaer


1 Answers

A CommandBinding is just like any other element in your visual tree. Any events specified on it will be handled by the root of your visual tree (your Window in this case). That means if you move the AddPage_Executed and AddPage_CanExecute to your Window's code behind, it will work. This allows you to use the same command in many UI components but have different handlers.

I see, however, that your command executes some logic against your view model. To save you some time and frustration, understand that routed commands are the wrong solution here. Instead, encapsulate your command in your view model something like this:

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());
    }
}

A DelegateCommand is an implementation of ICommand that invokes delegates to execute and query the command. That means the command logic is all wrapped up in the command and you don't need a CommandBinding to provide handlers (you don't need a CommandBinding at all). So your view just binds to your VM as follows:

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

I suggest you read through this series of posts to give you more context:

  • View Models: POCOs versus DependencyObjects
  • ViewModel
  • DelegateCommand
  • ActiveAwareCommand
like image 197
Kent Boogaart Avatar answered Sep 19 '22 18:09

Kent Boogaart