在 MenuItem 的 CustomControls 中触发和捕获自定义 RoutedCommands

Firing and Capturing of Custom RoutedCommands within CustomControls for MenuItems

WPF。 .NET 4.6

我正在尝试学习 RoutedCommands。在下面的代码中,我的愿望是让我的自定义 class 的 MenuItem 通过触发自定义路由命令 来响应用户点击,该命令被 监听 parent menuitem. 当parent menuitem接收到它包含的children的routedcommand时,就是设置一个dependency 属性 允许visual tree中的其他元素绑定使用 ElementName 绑定。到目前为止,我一直无法在父容器中捕获命令——所以我不确定它是否真的在冒泡。

  1. 命令 ChangeInkAttributes 如何通过元素树向上路由到顶部 "InkMenu" 元素?
  2. 子元素如何,例如"InkAndGestures",触发 routedcommand,ChangeInkAttributes, 自定义控件,InkMenuItem?
  3. 父容器"InkMenu"如何捕获路由命令"InkAndGestures"?(最好在自定义控件中捕获路由命令——这是甚至可能?)

注意:我想避免代码隐藏中的 any/all 代码。

我很感激任何帮助,因为 Google 的彻底搜索未能展示这种方法的一个很好的例子。

TIA

例如:

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

我将 InkMenuItem 自定义控件简单地定义为:

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

    }

A RoutedCommand 从聚焦元素搜索可视化树并向上搜索具有匹配 CommandBinding 的元素,为该特定 CommandBinding 执行 Execute 委托然后停下来。

它不会像 Microsoft 的 Nick Kramer 在这里所说的那样进一步路由或冒泡:https://www.vistax64.com/avalon/852-routedcommand-doesnt-route-ui-tree.html

在拔掉我遗漏的头发后,我可以放心地保证@mm8 是 100% 正确的。但是,我确实希望它能冒泡。所以,我改变了

ChangeInkAttributes_Executed 事件重新抛出其父 InkMenuItem:

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

            // this only causes a loop, it does not bubble.
            // inkmenuitem.RaiseEvent(e);


            InkMenuItem parent = inkmenuitem.Parent as InkMenuItem;
            if (parent != null)
                parent.RaiseEvent(e);
        }

希望这对某人有所帮助。