Nostromo
Nostromo

Reputation: 1264

WPF: How to control the popup of context menus in a listview/gridview

I have a two-part-question, I hope it's ok to ask them both at once.

I have a WPF ListView with a GridView as it's view.

A while ago I was looking for a way to have a ContextMenu in the ListView that is only popping up when the user right clicks an actual item of the ListView.

All the solutions I found online suggested to put this ContextMenu inside the ItemContainerStyle, so I did this and it works.

1. question, the general one

But now it got me thinking... if my ListView contains thousands of items, does that mean I created thousands of identical ContextMenus in memory as well? And if I did, isn't there a better way to get one item ContextMenu?

2. question, the specific one (obsolete, see the EDIT below)

Now I try to get another ContextMenu to pop up, when the user right clicks on the GridViews header, and again all the solutions suggest to put it inside the GridViewColumnHeader.

I have two problems with that. First, it again creates identical ContextMenus for each of the columns, and second, the ContextMenu only pops up, when the user right clicks on one of the column headers. When there is free space inside the header area (because all the columns don't need all the space available), the ContextMenu won't pop up when right clicking on the free space of the header area.

Is there a way to pop up one ContextMenu, when right clicking the header area of a GridView?

I'd prefer the solution to be in XAML only, if that is possible.

Thank you for your help.

EDIT:

Please forget the second part of the question. I found the ColumnHeaderContextMenu property of the GridView that is exactly doing what I was looking for.

Upvotes: 0

Views: 580

Answers (2)

mm8
mm8

Reputation: 169320

if my ListView contains thousands of items, does that mean I created thousands of identical ContextMenus in memory as well?

No.

You can confirm this by creating your own custom ContextMenu type, set a breakpoint in its constructor and debug your application:

Public Class MyContextMenu
    Inherits ContextMenu

    Public Sub New()
        Items.Add(New MenuItem() With {.Header = "1"})
        Items.Add(New MenuItem() With {.Header = "2"})
        Items.Add(New MenuItem() With {.Header = "3"})
    End Sub

End Class

<ListView x:Name="listView1">
    <ListView.Resources>
        <local:MyContextMenu x:Key="cm" />
    </ListView.Resources>
    <ListView.ItemContainerStyle>
        <Style TargetType="ListViewItem">
            <Setter Property="ContextMenu" Value="{StaticResource cm}" />
        </Style>
    </ListView.ItemContainerStyle>
</ListView>

The breakpoint won't be hit once for each item that you add to the Items collection of the ListView so don't worry about this.

And it doesn't matter whether the property is set using a StaticResource:

<ListView.ItemContainerStyle>
    <Style TargetType="ListViewItem">
        <Setter Property="ContextMenu">
            <Setter.Value>
                <local:MyContextMenu />
            </Setter.Value>
        </Setter>
    </Style>
</ListView.ItemContainerStyle>

Setting the ContextMenu property of the ItemContainerStyle is the correct way of associating a context menu with a ListViewItem container.

Upvotes: 1

rmojab63
rmojab63

Reputation: 3631

Assume the following custom controls:

public class MTextBlock : TextBlock
{
    public MTextBlock ()
    {// breakpoint

    }
}
public class MContextMenu : ContextMenu
{
    public MContextMenu()
    {// breakpoint

    }
}

And the following ItemsControl in Xaml:

  <ItemsControl Name="ic" ItemTemplate="{StaticResource res}"/>

in which the ItemsSource will be set in code behind.

If you use the following setup

    <DataTemplate x:Key="res">
        <TextBlock Text="A a a a a ">
            <TextBlock.ContextMenu>
                <local:MContextMenu>
                    <MenuItem Header="Item1">
                        <local:MTextBlock Text="u" />
                    </MenuItem>
                </local:MContextMenu>
            </TextBlock.ContextMenu>
        </TextBlock> 
    </DataTemplate>

multiple instances of MContextMenu and MTextBox will be generated. You can check this by setting breakpoints on the constructors.

However, if you declare the control as a static resource, such as:

<Window.Resources>
    <local:MContextMenu x:Key="cm">
        <MenuItem Header="Item1">
            <local:MTextBox Text="u" />
        </MenuItem>
    </local:MContextMenu>
    <DataTemplate x:Key="res">
        <TextBlock Text="{Binding Path=Name}" Background="Transparent" Width="100" ContextMenu="{StaticResource cm}"> 
        </TextBlock> 
    </DataTemplate>
</Window.Resources>

only one instance of them will be generated. Again check it by setting the proper breakpoints. Note that in this case, if you change the Text property of the MTextBox, if affects all _MTextBox_es in different _ContextMenu_s.

Upvotes: 1

Related Questions