2010-01-09 73 views
8

在WPF应用程序中有一个Grid与一些对象(它们是从一个自定义控件派生的)。我想使用上下文菜单上的每个人执行一些动作:如何在WPF上下文中引用右键单击的对象菜单项单击事件处理程序?

<Grid.ContextMenu> 
    <ContextMenu> 
     <MenuItem Name="EditStatusCm" Header="Change status" Click="EditStatusCm_Click"/> 
    </ContextMenu>     
    </Grid.ContextMenu> 

但在事件处理程序我不能知道哪些对象是右键单击:

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

在那个注释行调试器说:未将对象引用设置为对象的实例。

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

编辑(添加):

我试图做同样的,使用命令方法:

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

<Window.CommandBindings> 
    <CommandBinding Command="MyNamespace:DataCommands.Requery" Executed="RequeryCommand_Executed"></CommandBinding> 
</Window.CommandBindings> 

MenuItem的XAML现在看起来像:

<Grid.ContextMenu> 
<ContextMenu> 
    <MenuItem Name="EditStatusCm" Header="Change status" Command="MyNamespace:DataCommands.Requery"/> 
</ContextMenu>     
</Grid.ContextMenu> 

和事件处理程序是这样的:

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

但调试器显示相同的运行时错误:对象引用不设置到对象的实例。

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

我应该如何引用在WPF上下文菜单项右键单击事件处理程序?

+0

我试着用命令的方式为更多的WPF十岁上下,但得到了同样的错误。我编辑了我的问题并添加了我的命令方法尝试的步骤。我对如何获得点击的对象引用的理解在这两种情况下都缺少一些东西 – rem 2010-01-09 11:50:17

回答

21

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

并在处理程序使用它来弄清楚它是电网

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

更新:
如果你想在菜单项处理程序来获取到网格的孩子,而不是网格本身,使用这种方法

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

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

+0

谢谢!问题是,最后(我的意思是你的代码示例),我们得到“g” - 对Grid的引用(我的上下文菜单XAML声明被放置),但我需要引用单击的对象,它位于Grid内部网格我有数百个类似的对象,每个对象都可以右键单击以获得上下文菜单)。 – rem 2010-01-14 18:08:28

+0

,而不是将上下文菜单放在网格上,将它放在网格的子节点上。 – kenwarner 2010-01-14 18:35:59

+0

是的,它的工作原理。谢谢! – rem 2010-01-15 10:36:46

2

menu = sender as MenuItem如果发件人不是MenuItem或其派生类,它将为null。随后尝试解除引用菜单将炸毁。

您的发件人很可能是Menu或ContextMenu或ToolStripMenuItem或某种其他形式的菜单项,而不是具体是MenuItem对象。使用调试器断点在此处停止代码并检查发件人对象以查看它到底是什么类。

+0

我在这一行上使用了一个调试器断点,它说“sender”类型如下:“sender {System.Windows.Controls.MenuItem Header:Change status Items.Count:0} \t object {System.Windows.Controls.MenuItem}“ – rem 2010-01-09 09:04:25

+0

您可能从几个项目中获取该事件,其中一些是MenuItems(就像您在调试器中捕获的项目一样)以及一些其中不是(像导致你的崩溃的那个)。如果在处理代码周围使用if(menu!= null),则可以停止它尝试处理来自非MenuItem对象的任何事件,这可能有所帮助。或者它实际上在下一行崩溃,并且它是不是MyCustControl对象的menu.DataContext。只需单步执行调试器,查看每个值,直到找出哪一个为空。 – 2010-01-09 14:48:24

+0

menu = sender作为MenuItem 不起作用,因为默认情况下,MenuItem是System.Windows.Forms中的类, 但是它的工作方式与 sender类似System.Windows.Controls.MenuItem; – horiatu 2015-01-08 03:09:47

1

难道你不应该检查RoutedEventArgs.Source而不是sender

2

RoutedEventArgs对于

  • RoutedEventArgs.source是基准对引发事件
  • RoutedEventArgs.originalSource对象报告源如由纯命中测试确定的,之前的任何可能的源由父母班级调整。

所以.Sender应该是答案。但这取决于菜单项的添加和绑定方式

请参阅此answer collection并选择适合您的方法!

+0

感谢您的“答案收集”链接。 Certianly许多方法来剥皮猫。你认为微软现在已经让这个更清洁了! – user73993 2010-01-15 08:40:55

1

你有两个不同的问题。这两个问题导致了同样的异常,但在其它方面无关:

第一个问题

在你的第一种方法你的代码是正确的,运行良好,除了这里的问题:

SCurrent.Status = MyCustControl.Status.Sixth; 

的名称“状态”既用作静态成员又用作实例成员。我认为你将代码错误地粘贴到你的问题中。

它也可能需要添加以下MenuItem menu = sender as MenuItem;后,根据您的具体情况:

if(menu==null) return; 

问题二

在你的第二个方法,你使用“发件人”,而不是“E 。资源”。下面的代码工作期望:

private void RequeryCommand_Executed(object sender, ExecutedRoutedEventArgs e)  
{  
    IInputElement parent = (IInputElement)LogicalTreeHelper.GetParent((DependencyObject)e.Source); 
     // Changed "sender" to "e.Source" in the line above 
    MyCustControl SCurrent = new MyCustControl();  
    SCurrent = (MuCustControl)parent;  
    string str = SCurrent.Name.ToString();// Error gone 
    MessageBox.Show(str);  
} 

最后请注意

注:没有任何理由可言绑定CommandParameter此,如果您使用的指挥方式。它显着较慢,需要更多的代码。 e.Source将始终是源对象,因此不需要使用CommandParameter,所以请改用它。

+0

Ray,在使用您最后一段代码的情况下,我得到一个调试器错误:无法投入'System.Windows.Controls.Grid'类型的对象来键入'MyCustControl'。看起来,e.sourse不是指向被点击的对象,而是指向网格(我的上下文菜单XAML声明的位置)。 – rem 2010-01-14 17:32:06

+0

有趣。我记得我实际上是将你的代码剪切并粘贴到一个项目中并尝试使用它。我认为我可能将ContextMenu粘贴到模板中而不考虑它,而不是将其附加到网格上,因为如果ContextMenu附加到网格上,它显然不可能按预期工作。 – 2010-01-15 18:20:01

5

通过在XAML绑定的数据上下文,像这样:

ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource= {RelativeSource Self}}"> 

然后你可以这样做:

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

    MyDoStuffFunction(menuItem.DataContext); 
} 

数据上下文将被绑定到被点击打开的对象上下文菜单。

我是从这个链接一个CodeProject上的文章:

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

0

这个工作对我来说: -

XAML: -

<DataGrid.ContextMenu> 
<ContextMenu x:Name="AddColumnsContextMenu" MenuItem.Click="AddColumnsContextMenu_Click"> 
</ContextMenu> 

对于加入菜单项: -

foreach (String s in columnNames) 
{ 
var item = new MenuItem { IsCheckable = true, IsChecked = true ,Header=s}; 
AddColumnsContextMenu.Items.Add(item); 
} 

这里来监听器: -

private void AddColumnsContextMenu_Click(object sender, RoutedEventArgs e) 
{ 
    MenuItem mi = e.Source as MenuItem; 
    string title = mi.Header.ToString(); 
    MessageBox.Show("Selected"+title); 
} 

谢谢...

相关问题