如何在WPF上下文菜单项单击事件处理程序中引用右键单击的对象?

rem*_*rem 10 c# wpf contextmenu event-handling

在WPF应用程序中,有一个Grid包含许多对象(它们来自自定义控件).我想使用上下文菜单对每个操作执行一些操作:

   <Grid.ContextMenu>
     <ContextMenu>
       <MenuItem  Name="EditStatusCm" Header="Change status" Click="EditStatusCm_Click"/>
     </ContextMenu>                   
   </Grid.ContextMenu> 
Run Code Online (Sandbox Code Playgroud)

但是在事件处理程序中,我无法知道哪些对象被右键单击:

    private void EditStatusCm_Click(object sender, RoutedEventArgs e)
    {
        MyCustControl SCurrent = new MyCustControl();
        MenuItem menu = sender as MenuItem;
        SCurrent = menu.DataContext as MyCustControl; // here I get a run-time error
        SCurrent.Status = MyCustControl.Status.Sixth;
    }
Run Code Online (Sandbox Code Playgroud)

在该注释行上调试器说:对象引用未设置为对象的实例.

请帮忙,我的代码有什么问题?

编辑(补充):

我尝试使用Command方法做同样的事情:

我宣布了一个DataCommands类,RoutedUICommand Requery然后使用Window.CommandBindings

<Window.CommandBindings>
  <CommandBinding Command="MyNamespace:DataCommands.Requery" Executed="RequeryCommand_Executed"></CommandBinding>
</Window.CommandBindings>
Run Code Online (Sandbox Code Playgroud)

MenuItem的XAML现在看起来像:

<Grid.ContextMenu>
 <ContextMenu>
  <MenuItem  Name="EditStatusCm" Header="Change status"  Command="MyNamespace:DataCommands.Requery"/>
 </ContextMenu>                   
</Grid.ContextMenu>
Run Code Online (Sandbox Code Playgroud)

事件处理程序如下所示:

    private void RequeryCommand_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        IInputElement parent = (IInputElement)LogicalTreeHelper.GetParent((DependencyObject)sender);
        MyCustControl SCurrent = new MyCustControl();
        SCurrent = (MuCustControl)parent;
        string str = SCurrent.Name.ToString();// here I get the same error
        MessageBox.Show(str);
    }
Run Code Online (Sandbox Code Playgroud)

但是调试器显示相同的运行时错误:对象引用未设置为对象的实例.

我的两种方法都缺少什么?

如何在WPF上下文菜单项单击事件处理程序中引用右键单击的对象?

ken*_*ner 25

注意CommandParameter

<Grid Background="Red" Height="100" Width="100">
    <Grid.ContextMenu>
        <ContextMenu>
            <MenuItem 
                Header="Change status" 
                Click="EditStatusCm_Click"
                CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Parent}" />
        </ContextMenu>
    </Grid.ContextMenu>
</Grid>
Run Code Online (Sandbox Code Playgroud)

并在处理程序中使用它来确定它是哪个Grid

    private void EditStatusCm_Click(object sender, RoutedEventArgs e)
    {
        MenuItem mi = sender as MenuItem;
        if (mi != null)
        {
            ContextMenu cm = mi.CommandParameter as ContextMenu;
            if (cm != null)
            {
                Grid g = cm.PlacementTarget as Grid;
                if (g != null)
                {
                    Console.WriteLine(g.Background); // Will print red
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

更新:
如果您希望menuitem处理程序转到Grid的子节点而不是Grid本身,请使用此方法

<Grid Background="Red" Height="100" Width="100">
    <Grid.Resources>
        <ContextMenu x:Key="TextBlockContextMenu">
            <MenuItem 
                Header="Change status" 
                Click="EditStatusCm_Click"
                CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Parent}" />
        </ContextMenu>

        <Style TargetType="{x:Type TextBlock}">
            <Setter Property="ContextMenu" Value="{StaticResource TextBlockContextMenu}" />
        </Style>
    </Grid.Resources>

    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>

    <TextBlock Text="Row0" Grid.Row="0" />
    <TextBlock Text="Row1" Grid.Row="1" />
</Grid>
Run Code Online (Sandbox Code Playgroud)

只需将TextBlocks替换为您的自定义对象类型即可.然后在事件处理程序中,替换Grid g = cm.PlacementTarget as GridTextBlock t = cm.PlacementTarget as TextBlock(或任何您的自定义对象类型).

  • 性能说明:读者应注意,这里实际上并不需要`CommandParameter`绑定,因为可以使用`e.Source`.这项工作的唯一基本要求是每个元素都有自己的`ContextMenu`.虽然由于绑定,`CommandParameter`的效率明显低于使用`e.Source`,但你很容易认为它更优雅,因此效率更低. (3认同)

Tbo*_*k27 6

通过在xaml中绑定数据上下文:

ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource=    {RelativeSource Self}}">
Run Code Online (Sandbox Code Playgroud)

然后你可以这样做:

private void Context_MenuClick(object sender, RoutedEventArgs e)
{
   var menuItem = e.Source as MenuItem;

   MyDoStuffFunction(menuItem.DataContext);
}
Run Code Online (Sandbox Code Playgroud)

数据上下文将绑定到单击的对象以打开ContextMenu.

我从这个链接的codeproject文章中得到了它:

http://www.codeproject.com/Articles/162784/WPF-ContextMenu-Strikes-Again-DataContext-Not-Upda