Search code examples
c#wpfxamlcustom-controlsrouted-commands

Firing and Capturing of Custom RoutedCommands within CustomControls for MenuItems


WPF. .NET 4.6

I'm trying to learn RoutedCommands. In the below code, my desire is to have my custom class of MenuItem respond to the user click by firing a custom routedcommand which is listened to by the parent menuitem. When the parent menuitem receives the routedcommand from its contained children, it is to set a dependency property allowing other elements in the visual tree to bind using ElementName binding. So far, I have been unable to capture the command at the parent container--so I am not sure if it is actually bubbling at all.

  1. How can the command, ChangeInkAttributes, be routed up through the element tree to the top "InkMenu" element?
  2. How does a child element, e.g. "InkAndGestures", fire the routedcommand, ChangeInkAttributes, from within the customcontrol, InkMenuItem?
  3. How can the parent containter, "InkMenu", capture the routedcommand, "InkAndGestures"? (Preferrably capture the routedcommand within the custom control -- is this even possible?)

Note: I would like to avoid any/all code in code-behind.

I appreciate any help with this as a thorough search with Google has failed to show a good example of this approach.

TIA

As as example:

<pn:InkMenuItem x:Name="InkMenu" Header="Ink"  > <---**Parent containter to set
the dependency property and provide for ElementName binding.

THESE ARE THE CHILDREN WHICH SHOULD SEND THE COMMAND TO THE PARENT "InkMenu"

                    <pn:InkMenuItem Header="Pen"    />
                    <pn:InkMenuItem Header="HighLighter"  />
                    <Separator />
                    <pn:InkMenuItem Header="RePen Selection"  />
                    <pn:InkMenuItem Header="Select Strokes"  />
                    <pn:InkMenuItem Header="Select All Strokes"  />
                    <Separator />
                    <pn:InkMenuItem Header="Erase By Stroke" />
                    <pn:InkMenuItem Header="Erase By Point"  />
                    <pn:InkMenuItem Header="Clear Strokes"   />
                    <Separator />
                    <pn:InkMenuItem Header="Show Recognition Layout" />
                    <pn:InkMenuItem x:Name="InkAndGestures"  Header="Ink And Getsures" IsCheckable="True"/>
                </pn:InkMenuItem>

I've defined the InkMenuItem customcontrol simply as:

Generic.XAML:

 <Style TargetType="{x:Type local:InkMenuItem}" BasedOn="{StaticResource {x:Type MenuItem}}"/>

InkMenuItem.cs:
    public class InkMenuItem : MenuItem
    {
        public static RoutedUICommand ChangeInkAttributes { get; }

        static InkMenuItem()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(InkMenuItem), new FrameworkPropertyMetadata(typeof(InkMenuItem)));

            //Instanciate the command
            ChangeInkAttributes = new RoutedUICommand("Change Ink Attributes", "ChangeInkAttributes", typeof(InkMenuItem));

            //Create the command binding
            CommandManager.RegisterClassCommandBinding(typeof(InkMenuItem), 
                new CommandBinding(
                    /*Command Object */  ChangeInkAttributes, 
                    /*Execute*/          ChangeInkAttributes_Executed, 
                    /*Can Execute? */    ChangeInkAttributes_CanExecute));

        }

        public static void ChangeInkAttributes_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            InkMenuItem inkmenuitem = sender as InkMenuItem;

            CommandParameter parameter = e.Parameter as CommandParameter;

            if (parameter != null)
                parameter.CanEditBeExecuted = true;

            if (inkmenuitem != null)
                inkmenuitem.changeInkAttributes();
        }


        public static void ChangeInkAttributes_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;  // can this command be executed?
            e.Handled = false;    // has this event been handled?
        }


        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
        }

        // Raise the MyChecked event when the InkMenuItem is clicked
        protected override void OnClick()
        {
           // base.OnClick();
            ChangeInkAttributes.Execute(null,this);
        }

        private void changeInkAttributes()
        {
            var h = Header.ToString();
            MessageBox.Show(h+"23");
        }

    }

Solution

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

    It doesn't route or bubble up any further as Nick Kramer of Microsoft has stated here: https://www.vistax64.com/avalon/852-routedcommand-doesnt-route-ui-tree.html.