C# 如何在 WPF 上下文菜单项单击事件处理程序中引用右键单击的对象?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2032832/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to reference right-clicked object in WPF Context Menu item click event handler?
提问by rem
In WPF application there is a Grid
with a number of objects (they are derived from a custom control). I want to perform some actions on each of them using context menu:
在 WPF 应用程序中,有Grid
许多对象(它们派生自自定义控件)。我想使用上下文菜单对它们中的每一个执行一些操作:
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Name="EditStatusCm" Header="Change status" Click="EditStatusCm_Click"/>
</ContextMenu>
</Grid.ContextMenu>
But in the event handler I cannot get know which of the objects was right-clicked:
但是在事件处理程序中,我无法知道右键单击了哪个对象:
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;
}
On that commented line Debugger says: Object reference not set to an instance of an object.
在那条注释行上,调试器说:未将对象引用设置为对象的实例。
Please help, what is wrong in my code?
请帮忙,我的代码有什么问题?
Edited (added):
编辑(添加):
I tried to do the same, using Commandapproach:
我尝试使用命令方法来做同样的事情:
I declared a DataCommands
Class with RoutedUICommand Requery
and then used Window.CommandBindings
我声明了一个DataCommands
类,RoutedUICommand Requery
然后使用Window.CommandBindings
<Window.CommandBindings>
<CommandBinding Command="MyNamespace:DataCommands.Requery" Executed="RequeryCommand_Executed"></CommandBinding>
</Window.CommandBindings>
XAML of MenuItem now looks like:
MenuItem 的 XAML 现在看起来像:
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Name="EditStatusCm" Header="Change status" Command="MyNamespace:DataCommands.Requery"/>
</ContextMenu>
</Grid.ContextMenu>
And event handler looks like:
事件处理程序看起来像:
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);
}
But debugger shows the same run-time error: Object reference not set to an instance of an object.
但调试器显示相同的运行时错误:未将对象引用设置为对象的实例。
What is missing in my both approaches?
我的两种方法都缺少什么?
How I should reference right-clicked object in WPF Context Menu item click event handler?
我应该如何在 WPF 上下文菜单项单击事件处理程序中引用右键单击的对象?
采纳答案by kenwarner
note the 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>
and use it in the handler to figure out which Grid it is
并在处理程序中使用它来确定它是哪个网格
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
}
}
}
}
Update:
If you want the menuitem handler to get to the Grid's children instead of the Grid itself, use this approach
更新:
如果您希望菜单项处理程序访问 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>
Just replace the TextBlocks with whatever your custom object type is. Then in the event handler, replace Grid g = cm.PlacementTarget as Grid
with TextBlock t = cm.PlacementTarget as TextBlock
(or whatever your custom object type is).
只需将 TextBlocks 替换为您的自定义对象类型即可。然后在事件处理程序中,替换Grid g = cm.PlacementTarget as Grid
为TextBlock t = cm.PlacementTarget as TextBlock
(或任何您的自定义对象类型)。
回答by Jason Williams
menu = sender as MenuItem
will be null if the sender is not a MenuItem or a derived class thereof. Subsequently trying to dereference menu will blow up.
menu = sender as MenuItem
如果发件人不是 MenuItem 或其派生类,则为 null。随后试图取消引用菜单会爆炸。
It's likely that your sender is a Menu or ContextMenu or a ToolStripMenuItem or some other form of menu item, rather than specifically being a MenuItem object. Use a debugger breakpoint to stop the code here and examine the sender object to see exactly what class it is.
您的发件人很可能是 Menu 或 ContextMenu 或 ToolStripMenuItem 或某种其他形式的菜单项,而不是专门作为 MenuItem 对象。使用调试器断点在此处停止代码并检查发送方对象以准确查看它是什么类。
回答by Vijay Patel
Shouldn't you be checking RoutedEventArgs.Source
instead of sender
?
你不应该检查RoutedEventArgs.Source
而不是sender
吗?
回答by TFD
For RoutedEventArgs
- RoutedEventArgs.sourceis the reference to the object that raised the event
- RoutedEventArgs.originalSourceis the reporting source as determined by pure hit testing, before any possible Source adjustment by a parent class.
- RoutedEventArgs.source是对引发事件的对象的引用
- RoutedEventArgs.originalSource是由纯命中测试确定的报告源,在父类进行任何可能的源调整之前。
So .Sender should be the answer. But this depends on how the menuitems are added and bound
所以 .Sender 应该是答案。但这取决于如何添加和绑定菜单项
See this answer collectionand choose the method that will work for you situation!
请参阅此答案集并选择适合您情况的方法!
回答by Ray Burns
You had two different problems. Both problems resulted in the same exception, but were otherwise unrelated:
你有两个不同的问题。这两个问题都导致了相同的异常,但在其他方面并不相关:
First problem
第一个问题
In your first approach your code was correct and ran well except for the problem here:
在您的第一种方法中,您的代码是正确的并且运行良好,除了这里的问题:
SCurrent.Status = MyCustControl.Status.Sixth;
The name "Status" is used both as a static member and as an instance member. I think you cut-and-pasted the code incorrectly into your question.
名称“Status”既用作静态成员又用作实例成员。我认为您错误地将代码剪切并粘贴到您的问题中。
It may also be necessary to add the following after MenuItem menu = sender as MenuItem;
, depending on your exact situation:
可能还需要在 之后添加以下内容MenuItem menu = sender as MenuItem;
,具体取决于您的具体情况:
if(menu==null) return;
Second problem
第二个问题
In your second approach you used "sender" instead of "e.Source". The following code works as desired:
在您的第二种方法中,您使用了“发件人”而不是“e.Source”。以下代码按需要工作:
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);
}
Final Note
最后说明
Note: There is no reason at all to bind CommandParameter
for this if you use the commanding approach. It is significantly slower and takes more code. e.Source
will always be the source object so there is no need to use CommandParameter
, so use that instead.
注意:CommandParameter
如果您使用命令式方法,则完全没有理由为此绑定。它明显更慢并且需要更多代码。 e.Source
将始终是源对象,因此无需使用CommandParameter
,因此请改用它。
回答by Tbonechk27
By binding the Data Context like so in the xaml:
通过像这样在 xaml 中绑定数据上下文:
ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource= {RelativeSource Self}}">
You can then do this:
然后你可以这样做:
private void Context_MenuClick(object sender, RoutedEventArgs e)
{
var menuItem = e.Source as MenuItem;
MyDoStuffFunction(menuItem.DataContext);
}
The data context will be bound to the object that was clicked to open the ContextMenu.
数据上下文将绑定到单击以打开 ContextMenu 的对象。
I got it from a codeproject article at this link:
我从这个链接的 codeproject 文章中得到它:
http://www.codeproject.com/Articles/162784/WPF-ContextMenu-Strikes-Again-DataContext-Not-Upda
http://www.codeproject.com/Articles/162784/WPF-ContextMenu-Strikes-Again-DataContext-Not-Upda
回答by Abhishek
This works for me:-
这对我有用:-
XAML:-
XAML:-
<DataGrid.ContextMenu>
<ContextMenu x:Name="AddColumnsContextMenu" MenuItem.Click="AddColumnsContextMenu_Click">
</ContextMenu>
For adding menu items:-
添加菜单项:-
foreach (String s in columnNames)
{
var item = new MenuItem { IsCheckable = true, IsChecked = true ,Header=s};
AddColumnsContextMenu.Items.Add(item);
}
And here comes the listener:-
听众来了:-
private void AddColumnsContextMenu_Click(object sender, RoutedEventArgs e)
{
MenuItem mi = e.Source as MenuItem;
string title = mi.Header.ToString();
MessageBox.Show("Selected"+title);
}
Thanks...
谢谢...
回答by Colin
In my case I was able to use:
就我而言,我能够使用:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
MenuItem menuItem = e.Source as MenuItem;
ContextMenu parent = menuItem.Parent as ContextMenu;
ListBoxItem selectedItem = parent.PlacementTarget as ListBoxItem;
}