Search code examples
wpfxamlrouted-commandsroutedcommand

RoutedCommands bound by sub elements never fire


I'm trying to get my head around RoutedCommands in WPF. I like how they decrease coupling between different UI elements and the models but I can't seem to make the bindings work for custom controls that are children to the window. I guess this will be some easy creds for any of you WPF wizards out there! :-)

Here's some example code that can be tried out:

The routed command:

using System.Windows.Input;

namespace Wpf.RoutedCommands
{
    public static class Commands
    {
        public static readonly RoutedCommand SayHi = new RoutedCommand();
    }
}

Main window XAML:

<Window x:Class="Wpf.RoutedCommands.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:Wpf.RoutedCommands"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<!-- uncommenting the below makes it work but introduces coupling -->
<!--<Window.CommandBindings>
    <CommandBinding Command="{x:Static local:Commands.SayHi}" Executed="cmdSayHi" CanExecute="cmdCanSayHi"></CommandBinding>
</Window.CommandBindings>-->
<DockPanel LastChildFill="True">
    <Button DockPanel.Dock="Bottom" Content="Say Hi!" Command="{x:Static local:Commands.SayHi}" />
    <local:Greeter x:Name="Greeter" />
</DockPanel>    

Main window code:

using System.Windows.Input;

namespace Wpf.RoutedCommands
{
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        // these two will be used if you uncomment the command bindings in XAML
        private void cmdCanSayHi(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = true;

        private void cmdSayHi(object sender, ExecutedRoutedEventArgs e) => Greeter.SayHi();
    }
}

Custom control ("Greeter") XAML:

<UserControl x:Class="Wpf.RoutedCommands.Greeter"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:Wpf.RoutedCommands"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<UserControl.CommandBindings>
    <CommandBinding Command="{x:Static local:Commands.SayHi}" Executed="cmdSayHi"  CanExecute="cmdCanSayHi"/>
</UserControl.CommandBindings>
<Grid>
    <Label x:Name="Label" />
</Grid>

Greeter code:

using System.Windows.Input;

namespace Wpf.RoutedCommands
{
    public partial class Greeter
    {
        public Greeter()
        {
            InitializeComponent();
        }

        private void cmdSayHi(object sender, ExecutedRoutedEventArgs e) => SayHi();

        private void cmdCanSayHi(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = true;

        public void SayHi() => Label.Content = "Hi!";
    }
}

If you create a WPF project and use the above you will see that the button in main window is disabled. Debugging it shows that the Greeter.cmdCanSayHi method never gets called.

If you uncomment the dormant XAML in main window everything works. So: Why can I bind to commands from the window but not from its child controls? Is it to do with rendering timing or something?


Solution

  • A RoutedCommand searches the visual tree from the focused element and up for an element that has a matching CommandBinding and then executes the Execute delegate for this particular CommandBinding.

    Your UserControl is located below the Button in the element tree.