ContextMenu 在按钮单击时不触发命令

ContextMenu on button click not firing command

提问人:Justin Mathew 提问时间:1/26/2014 最后编辑:Vadim OvchinnikovJustin Mathew 更新时间:5/25/2017 访问量:3705

问:

这是一个问题。

我在单击按钮时显示上下文菜单,并且菜单命令绑定到视图模型中。菜单显示在按钮单击和右键单击时。问题是当我单击按钮然后单击上下文菜单时,菜单单击没有触发,但是当我右键单击按钮然后单击菜单时,我可以确认菜单正在工作。ICommand

 <Button Grid.Row="3" Width="500" Height="30" Name="cmButton"  >
    Button with Context Menu
    <Button.ContextMenu>
        <ContextMenu DataContext="{Binding Path=PlacementTarget.DataContext, RelativeSource={RelativeSource Mode=Self}}"  >
            <MenuItem  DataContext="{Binding}" Header="New Layout Element..." Command="{Binding Path=SubmitBtn}" />                  
        </ContextMenu>
    </Button.ContextMenu>
    <Button.Style>
        <Style TargetType="{x:Type Button}">
            <Style.Triggers>
                <EventTrigger RoutedEvent="Click">
                    <EventTrigger.Actions>
                        <BeginStoryboard>
                            <Storyboard>
                                <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.IsOpen">
                                    <DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True"/>
                                </BooleanAnimationUsingKeyFrames>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>
            </Style.Triggers>                    
        </Style>
    </Button.Style>
 </Button>

我可以确认我的视图模型没有问题,因为当我右键单击按钮然后单击上下文菜单时,命令正在触发。

WPF 按钮 onclick 上下文菜单

评论


答:

9赞 Rohit Vats 1/26/2014 #1

PlacementTarget是当您手动设置属性时,因为只有在右键单击目标控件打开它时,它才会设置为实际值。(类负责将此值设置为实际目标)。nullContextMenu.IsOpenPopUpService

由于 PlacementTarget 在通过 Storyboard 打开它时为 null,因此绑定无法解析它绑定到的实际命令。

因此,问题是您需要将 ButtonDataContext 传递给 MenuItem,以便可以解析绑定。(与按钮的可视化树不同)。这可以通过两种方式实现:MenuItem


使用 x:Reference(在 WPF 4.0 及更高版本中可用),但您需要声明虚拟控件,以便可以引用它以获取 设置为 .DataContextVisibilityCollapsed

<FrameworkElement x:Name="dummyControl" Visibility="Collapsed"/>
   <Button Width="100" Height="30" Name="cmButton">
      <Button.ContextMenu>
         <ContextMenu>
           <MenuItem Header="New Layout Element..."
                     Command="{Binding Path=DataContext.SubmitBtn,
                                       Source={x:Reference dummyControl}}" />
         </ContextMenu>
      </Button.ContextMenu>
   </Button>

另一个有趣的事情是 Freezable 对象继承,即使它们不在里面,所以我们可以使用此功能来克服我们需要继承的情况。DataContextVisualTreeDataContext

首先,我们需要公开 DP,它可以绑定到:create class inheriting from Freezable

public class BindingProxy : Freezable
{
    #region Overrides of Freezable

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    #endregion

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    public static readonly DependencyProperty DataProperty =
     DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy));
}

现在我们可以在 XAML 中使用它,如下所示:

<Button Width="100" Height="30" Name="cmButton">
    <Button.Resources>
        <local:BindingProxy x:Key="proxy" Data="{Binding}"/>
    </Button.Resources>
    <Button.ContextMenu>
        <ContextMenu>
            <MenuItem Header="New Layout Element..."
                      Command="{Binding Path=Data.SubmitBtn,
                                        Source={StaticResource proxy}}" />
        </ContextMenu>
    </Button.ContextMenu>
</Button>

评论

1赞 Justin Mathew 1/27/2014
Rohit,我尝试了第二种方法,效果很好。非常感谢,以后与您联系。
0赞 Rohit Vats 1/27/2014
确定。很高兴帮助贾斯汀。:)
1赞 gabriel de sousa 5/9/2014 #2

之所以发生这种情况,是因为 是 ,你只需要在事件点击时设置他。查看示例:DataContextContextMenunullButton

XAML:

<Button Content="More..." Click="ButtonMoreClick" ContextMenu="{StaticResource ContextMenu1}"/>

代码背后

private void ButtonMoreClick(object sender, RoutedEventArgs e)
{
    var menu = (sender as Button).ContextMenu;
    menu.DataContext = DataContext;
    menu.IsOpen = true;
}

我希望能帮到你