Как сослаться на объект, по которому щелкают правой кнопкой, в обработчике события Click объекта Контекстного меню WPF?

В приложении 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?

10
задан rem 12 January 2010 в 14:04
поделиться

5 ответов

Обратите внимание на 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 (или любой, какой ваш пользовательский тип объекта).

25
ответ дан 3 December 2019 в 14:06
поделиться

menu = отправитель как MenuItem будет равен нулю, если отправитель не является MenuItem или его производным классом. Впоследствии попытка разыменования меню будет взорвана.

Скорее всего, отправителем является MenuItem, ContextMenu, ToolStripMenuItem или какой-либо другой вид меню, а не объект MenuItem. Используйте точку останова отладчика, чтобы остановить код здесь и изучить объект отправителя, чтобы увидеть, какой именно класс он представляет собой.

2
ответ дан 3 December 2019 в 14:06
поделиться

для RouteEventArgs

  • RouteEventArgs.source является ссылкой на объект, который поднял событие
  • RouteTeventArgs.originalsource , является источником отчетов, как определено тестированием чистого удара, прежде чем любую возможную регулировку источников родительского класса.

Так что. Насел должен быть ответ. Но это зависит от того, как добавляются меню мнойумии и связаны

, см. В этом Коллекция ответов и выберите метод, который будет работать для вашей ситуации!

2
ответ дан 3 December 2019 в 14:06
поделиться

Если вы не должны проверять RouteEventArgs.source вместо отправителя ?

1
ответ дан 3 December 2019 в 14:06
поделиться

У вас было две разные проблемы. Обе проблемы привели к тому же исключительному, но в противном случае были не связаны:

Первая проблема

В вашем первом подходе Ваш код был правильным и пробежен хорошо, кроме проблем здесь:

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 , поэтому используйте это вместо этого.

1
ответ дан 3 December 2019 в 14:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: