Как ссылаться на щелкните правой кнопкой мыши объект в 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.источник является ссылкой на объект, который вызвал событие
  • RoutedEventArgs.originalSource является источником отчетов, как определено чистым тестированием попадания, перед любой возможной корректировкой источника родительским классом.

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

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


разве вы не должны проверять RoutedEventArgs.Source вместо sender?


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

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

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

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