В приложении WPF существует a 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;
}
На той прокомментированной строке говорит Debugger: Ссылка на объект не набор к экземпляру объекта.
Помогите, что не так в моем коде?
Отредактированный (добавил):
Я пытался сделать то же, с помощью подхода Команды:
Я объявил a 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);
}
Но отладчик показывает ту же ошибку времени выполнения: Ссылка на объект не набор к экземпляру объекта.
Что отсутствует в моих обоих подходах?
Как я должен сослаться на объект, по которому щелкают правой кнопкой, в обработчике события Click объекта Контекстного меню WPF?
Обратите внимание на 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>
, просто замените текстовые блоки с тем, что находится ваш пользовательский тип объекта. Затем в обработчике событий замените GRID G = CM.Plactartarget в виде сетки
с TextBlock T = CM.PlaceTarget в качестве TextBlock
(или любой, какой ваш пользовательский тип объекта).
menu = отправитель как MenuItem
будет равен нулю, если отправитель не является MenuItem или его производным классом. Впоследствии попытка разыменования меню будет взорвана.
Скорее всего, отправителем является MenuItem, ContextMenu, ToolStripMenuItem или какой-либо другой вид меню, а не объект MenuItem. Используйте точку останова отладчика, чтобы остановить код здесь и изучить объект отправителя, чтобы увидеть, какой именно класс он представляет собой.
для RouteEventArgs
Так что. Насел должен быть ответ. Но это зависит от того, как добавляются меню мнойумии и связаны
, см. В этом Коллекция ответов и выберите метод, который будет работать для вашей ситуации!
Если вы не должны проверять RouteEventArgs.source
вместо отправителя
?
У вас было две разные проблемы. Обе проблемы привели к тому же исключительному, но в противном случае были не связаны:
Первая проблема
В вашем первом подходе Ваш код был правильным и пробежен хорошо, кроме проблем здесь:
SCurrent.Status = MyCustControl.Status.Sixth;
Имя «Состояние» используется как Статический элемент и как член экземпляра. Я думаю, что вы порезали и вставили код неправильно в ваш вопрос.
Также может быть необходимо добавить следующее после меню Menuitem = отправителя как меню;
, в зависимости от вашей точной ситуации:
if(menu==null) return;
Вторая проблема
Во втором подходе вы использовали «Отправитель» вместо "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
Примечание: нет никаких причин для привязки CommandParameter
для этого, если вы используете командный подход. Это значительно медленнее и требует большего количества кода. E.Source
всегда будет исходным объектом, поэтому нет необходимости использовать CommandParameter
, поэтому используйте это вместо этого.