Search code examples
c#wpflistviewcontextmenu

C# ListView How to create ContextMenu programatically


I have this code behind:

private void InitializeListView()
    {
        //RAZ
        lv.Items.Clear();

        GridView gridView = new GridView();
        gridView.AllowsColumnReorder = true;
        
        GridViewColumn gvc1 = new GridViewColumn();
        gvc1.DisplayMemberBinding = new System.Windows.Data.Binding("SN");
        gvc1.Header = "SN";

        GridViewColumn gvc2 = new GridViewColumn();
        gvc2.DisplayMemberBinding = new System.Windows.Data.Binding("a1");
        gvc2.Header = "A1";
        gridView.Columns.Add(gvc2);

        GridViewColumn gvc3 = new GridViewColumn();
        gvc3.DisplayMemberBinding = new System.Windows.Data.Binding("a2");
        gvc3.Header = "A2";
        gridView.Columns.Add(gvc3);

        for (int i = 0; i < lv.Count; i++)
        {
            this.lv.Items.Add(
                new dataToUse
                {
                    sn= tab[i][0],
                    a1= tab[i][1],
                    a2 = tab[i][2]
                });
        }

        this.lv.View = gridView;
    }

in order to dynamically generate this:

<ListView  x:Name="lv" HorizontalAlignment="Left" Height="360" Margin="305,192,0,0" VerticalAlignment="Top" Width="607" SelectionMode="Extended"  >

        <ListView.View >
            <GridView  AllowsColumnReorder="true">
                <GridViewColumn DisplayMemberBinding="{Binding sn}" >
                    <GridViewColumnHeader>
                        <GridViewColumnHeader.ContextMenu>
                            <ContextMenu>
                                <MenuItem Header="Asc"/>
                                <MenuItem Header="Desc" />
                            </ContextMenu>
                        </GridViewColumnHeader.ContextMenu>
                    </GridViewColumnHeader>
                </GridViewColumn>

                <GridViewColumn DisplayMemberBinding="{Binding a1}">
                    <GridViewColumnHeader >
                        <GridViewColumnHeader.ContextMenu>
                            <ContextMenu >
                                <MenuItem Header="Asc"  />
                                <MenuItem Header="Desc" />
                            </ContextMenu>
                        </GridViewColumnHeader.ContextMenu>
                    </GridViewColumnHeader>
                </GridViewColumn>

                <GridViewColumn DisplayMemberBinding="{Binding a2}">
                    <GridViewColumnHeader >
                        <GridViewColumnHeader.ContextMenu>
                            <ContextMenu >
                                <MenuItem Header="Asc" />
                                <MenuItem Header="Desc"/>
                            </ContextMenu>
                        </GridViewColumnHeader.ContextMenu>
                    </GridViewColumnHeader>
                </GridViewColumn>
            </GridView>
        </ListView.View>
        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Vertical"/>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
    </ListView>

The problem is that when I generate the project, I cannot see the ContextMenu (and so the MenuItems)

I already use this kind of code in another project, but here, it doesn't work... I guess the code behind is the last one which is generated, so if you could explain me how to create a ContextMenu programmatically. Could be good.

Could you help me please?

Thanks!


Solution

  • Here is an example for the A2 column. The others follow the same approach.

    // Remove:
    //gvc3.Header = "A2";
    // Replace by:
    gvc3.Header = new GridViewColumnHeader()
    {
        Content = "A2",
        ContextMenu = new ContextMenu()
        {
            Items =
            {
                new MenuItem() { Header="Asc"},
                new MenuItem() { Header="Desc"}
            }
        }
    };
    

    In order to implement the actual sorting with possibility to sort by multiple columns, the menu items could be created with the following helper functions:

    private MenuItem CreateAscendingSortMenuItem(string prop)
    {
        var result = new MenuItem() { Header = "Asc" };
        result.Click += (s, e) =>
        {
            var toRemove = lv.Items.SortDescriptions.Where(x => x.PropertyName == prop).ToList();
            foreach (var item in toRemove)
            {
                lv.Items.SortDescriptions.Remove(item);
            }
            lv.Items.SortDescriptions.Insert(0, new SortDescription(prop, ListSortDirection.Ascending));
        };
        return result;
    }
    private MenuItem CreateDescendingSortMenuItem(string prop)
    {
        var result = new MenuItem() { Header = "Desc" };
        result.Click += (s, e) =>
        {
            var toRemove = lv.Items.SortDescriptions.Where(x => x.PropertyName == prop).ToList();
            foreach (var item in toRemove)
            {
                lv.Items.SortDescriptions.Remove(item);
            }
            lv.Items.SortDescriptions.Insert(0, new SortDescription(prop, ListSortDirection.Descending));
        };
        return result;
    }
    

    Then just create the items using the helper function instead of calling the constructor directly.

    gvc3.Header = new GridViewColumnHeader()
    {
        Content = "A2",
        ContextMenu = new ContextMenu()
        {
            Items =
            {
                CreateAscendingSortMenuItem("a2"),
                CreateDescendingSortMenuItem("a2")
            }
        }
    };