Как ссылаться на щелкните правой кнопкой мыши объект в WPF пункт контекстного меню нажмите обработчик событий?
в приложении WPF есть Grid
С рядом объектов (они являются производными от пользовательского элемента управления). Я хочу выполнить некоторые действия над каждым из них с помощью контекстного меню:
<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>
XAML MenuItem теперь выглядит так:
<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 контекстного меню пункт нажмите обработчик событий?
8 ответов
обратите внимание на 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
}
}
}
}
обновление:
Если вы хотите, чтобы обработчик menuitem добрался до дочерних элементов сетки вместо самой сетки, используйте этот подход
<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
(или независимо от типа настраиваемого объекта есть.)
путем привязки контекста данных, как в 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);
}
контекст данных будет привязан к объекту, который был нажат для открытия ContextMenu.
Я получил его из статьи codeproject по этой ссылке:
http://www.codeproject.com/Articles/162784/WPF-ContextMenu-Strikes-Again-DataContext-Not-Upda
menu = sender as MenuItem
будет иметь значение null, если отправитель не является MenuItem или его производным классом. Впоследствии попытка разыменования меню взорвется.
вполне вероятно, что ваш отправитель-это меню или ContextMenu или ToolStripMenuItem или какая-либо другая форма элемента меню, а не объект MenuItem. Используйте точку останова отладчика, чтобы остановить код здесь и изучить объект отправителя, чтобы точно узнать, какой это класс.
- RoutedEventArgs.источник является ссылкой на объект, который вызвал событие
- RoutedEventArgs.originalSource является источником отчетов, как определено чистым тестированием попадания, перед любой возможной корректировкой источника родительским классом.
Так .Отправитель должен быть ответом. Но это зависит от того, как меню добавляются и привязываются
посмотреть этот коллекция ответа и выбрать метод, который будет работать для вас ситуации!
у вас были две разные проблемы. Обе проблемы привели к одному и тому же исключению, но в остальном не были связаны:
первая проблема
в вашем первом подходе ваш код был правильным и работал хорошо, за исключением проблемы здесь:
SCurrent.Status = MyCustControl.Status.Sixth;
имя "Status" используется как статический член, так и как член экземпляра. Я думаю, что вы вырезали и вставили код неправильно в свой вопрос.
может также быть необходимо добавить после MenuItem menu = sender as MenuItem;
в зависимости от вашей конкретной ситуации:
if(menu==null) return;
вторая проблема
во втором подходе вы использовали "sender" вместо " 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
, поэтому используйте это вместо этого.
это работает для меня:-
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);
}
спасибо...
в моем случае я смог использовать:
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;
}