j.xavier.atero
j.xavier.atero

Reputation: 584

Access ListViewItem information from MenuItem in ContextMenu

I have a ListView with several items. I would like to copy in the system clipboard, information associated to a given row (e.g. id of the item in that row).

I have created a ContextMenu that opens when right clicking a row, and it displays a MenuItem to copy the id:

<ListView x:Name="MyListView" MouseDoubleClick="MyListView_MouseDoubleClick">
    <ListView.Columns>
        <GridViewColumn Header="Id" DisplayMemberBinding="{Binding MyItem.Id}"/>
    </ListView.Columns>
    <ListView.ItemTemplate>
        <HierarchicalDataTemplate DataType="{x:Type mynamespace:MyItemDefinition}" ItemsSource="{Binding MyGroupOfItems, Mode=OneWay}" />
    </ListView.ItemTemplate>
    <ListView.ContextMenu>
        <ContextMenu>
            <MenuItem x:Name="MenuItemCopyId" Header="Copy ID" Click="MenuItemCopyId_Click"/>
        </ContextMenu>
    </ListView.ContextMenu>
</ListView>

and

private void MenuItemCopyId_Click(object sender, RoutedEventArgs e)
{
     var menu_item = ((System.Windows.Controls.MenuItem)sender);
}

For what I read (ListViewItem Context Menu Get Data from ListViewItem) it needs to access the DataContext of the item. Which can be achieved through the PlacementTarget of the ContextMenu, but I cannot manage to do it. Any suggestions?

Thanks in advance

Upvotes: 1

Views: 677

Answers (1)

Jason Tyler
Jason Tyler

Reputation: 1389

You can often use the PlacementTargetto perform some relative binding and give ContextMenuinformation it wouldn't normally have because it's outside the visual tree. Given the way you have things setup here, I'm not sure that would work out too well for you. It looks like you want a context menu that belongs to the ListView but works on the row that is right clicked on. You can do that by manually attaching the DataContext in the ContextOpening event. For example, I loosely followed what you had to set up a ListView

<ListView ItemsSource="{Binding MyGroupOfItems}">
    <ListView.Resources>
        <Style TargetType="ListViewItem">
            <EventSetter Event="ContextMenuOpening" Handler="ListViewItem_ContextMenuOpening" />
        </Style>
    </ListView.Resources>
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Id" DisplayMemberBinding="{Binding Id}" />
        </GridView>
    </ListView.View>
    <ListView.ContextMenu>
        <ContextMenu Name="menu">
            <MenuItem Header="Copy ID" Click="MenuItem_Click" />
        </ContextMenu>
    </ListView.ContextMenu>
</ListView>

I added a style for the ListViewItem that handles ContextMenuOpening events.This will allow you to know which row you're over when the context menu is opening. In the code behind, you can attach the DataContext for that row.

private void ListViewItem_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
    ListViewItem listItem = sender as ListViewItem;
    menu.DataContext = listItem.DataContext;
}

In this case, menu is the name of the ContextMenu. I simply grab the DataContext from the ListItemView and attach it to the ContextMenu.

Now when you handle the menu click event, you can fetch that data and use it to do whatever you need to do.

private void MenuItem_Click(object sender, RoutedEventArgs e)
{
    MyItem item = (MyItem)(sender as MenuItem).DataContext;
    //Now you can do stuff with MyItem
}

Just a note: I omitted the stuff you were doing with the HierarchicalDataTemplate because I didn't understand the intent, and for me it was causing nothing to render. Regardless, this strategy for attaching the DataContext when opening the ContextMenu should transfer to other use cases.

Upvotes: 2

Related Questions