简体   繁体   中英

Context menu for specific dynamic column for datagrid wpf

I really need help to figure out how to add a context menu for a specific column on a datagrid.

Currently my datagrid looks like this: Datagrid and all columns are added to the datagrid dynamically with the itemsource: ItemsSource="{Binding Data.DefaultView}" and I have AutoGenerateColumns="True". Currently I can add different contextmenu for all the headers by using a style and a trigger. Example:

<Style TargetType="{x:Type DataGridColumnHeader}">
                <Style.Triggers>
                    <Trigger Property="Content" Value="CoreRefDes">
                        <Setter Property="ContextMenu"
                        Value="{StaticResource DataGridColumnHeaderContextMenuSpecific}" />
                    </Trigger>
                    <Trigger Property="Content" Value="CorePartNumber">
                        <Setter Property="ContextMenu"
                        Value="{StaticResource DataGridColumnHeaderContextMenuSpecific}" />
                    </Trigger>
                    <Trigger Property="Content" Value="CoreDescription">
                        <Setter Property="ContextMenu"
                        Value="{StaticResource DataGridColumnHeaderContextMenuSpecific}" />
                    </Trigger>
                    <Trigger Property="Content" Value="Split">
                        <Setter Property="ContextMenu"
                        Value="{StaticResource DataGridColumnHeaderContextMenuSpecific}" />
                    </Trigger>
                </Style.Triggers>

I want something similar but instead of the contextmenu on the header I want it on the cells or the datagrid based on the header name. Currently my contextmenu appears on the datagrid like this:

                <Style TargetType="{x:Type DataGrid}">
                <Setter Property="ContextMenu" Value="{DynamicResource DatagridCellContextmenuItems}"/>
            </Style>

Problem with this is I get the same contextmenu for all the columns. I want a different contextmenu for column 1 and 2. Any idea on how to solve this?

Thank you very much!

1) Change your style target yo DataGridCell

2) Each one of your column's DataTrigger will contain a multibinding. A converter will return the column header. Inspiration here IMultiValueConverter

2.a) The value you need to provide to the converter is that cell's column header content. Inspiration for the kind of binding you're looking for

<CheckBox IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type dg:DataGridColumnHeader}}, Path=Column.CellStyle, Mode=OneWayToSource, Converter={StaticResource CellBackgroundConverter}}" Content="Highlight Cells"/>

This one is a bit of a pain to achieve, but this is how I've done in the past.

I have a context menu on the data grid that contains every possible menu item that I might need. I then hide or show each one depending on which row/column/cell was r.clicked.

Here's the datagrid XAML snippet:

<DataGrid ContextMenuOpening="GridContextMenuOpening" ...>
    <DataGrid.ContextMenu>
        <ContextMenu>
            <MenuItem x:Name="mnuOpen" ... />
            <MenuItem x:Name="mnuNew" ... />
            <!-- and so on -->
        </ContextMenu>
    </DataGrid.ContextMenu>

    <!-- rest of datagrid - columns etc -->
</DataGrid>     

Note the DataGrid's ContextMenuOpening event handler - here is the code-behind:-

private void GridContextMenuOpening(object sender, ContextMenuEventArgs e)
{
    DataGridCell cell;
    DataGridRow row;

    var dep = DataGridMiscHelpers.FindVisualParentAsDataGridSubComponent(
        (DependencyObject)e.OriginalSource);
    if (dep == null)
    {
        return;
    }

    DataGridMiscHelpers.FindCellAndRow(dep, out cell, out row);
    if (dep is DataGridColumnHeader || dep is DataGridRow)
    {
        e.Handled = true;
        return;
    }

    // Hide/show the menu items depending on the cell and/or row clicked.
    // (You could programmatically add/remove menu items here instead).
    mnuOpen.Visibility = (cell.Column.Header == "whatever") ? Visibility.Hidden : Visibility.Visible;
    mnuNew.Visibility = ((row.Item as MyViewModel).SomeProperty == 123) ? Visibility.Hidden : Visibility.Visible;
}

And here's my "DataGridMiscHelpers" static class used by the above code:-

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Windows.Media.Media3D;

public static class DataGridMiscHelpers
{
    /// <summary>
    /// Finds the visual parent of the given object
    /// </summary>
    /// <param name="originalSource">The original source.</param>
    /// <returns></returns>
    public static DependencyObject FindVisualParentAsDataGridSubComponent(
        DependencyObject originalSource)
    {
        // iteratively traverse the visual tree
        while ((originalSource != null) 
            && !(originalSource is DataGridCell) 
            && !(originalSource is DataGridColumnHeader) 
            && !(originalSource is DataGridRow))
        {
            if (originalSource is Visual || originalSource is Visual3D)
            {
                originalSource = VisualTreeHelper.GetParent(originalSource);
            }
            else
            {
                // If we're in Logical Land then we must walk 
                // up the logical tree until we find a 
                // Visual/Visual3D to get us back to Visual Land.
                originalSource = LogicalTreeHelper.GetParent(originalSource);
            }
        }

        return originalSource;
    }

    /// <summary>
    /// Finds the cell and row data for the given source.
    /// </summary>
    /// <param name="originalSource">The original source.</param>
    /// <param name="cell">The cell.</param>
    /// <param name="row">The row.</param>
    public static void FindCellAndRow(DependencyObject originalSource, 
        out DataGridCell cell, out DataGridRow row)
    {
        cell = originalSource as DataGridCell;
        if (cell == null)
        {
            row = null;
            return;
        }

        // Walk the visual tree to find the cell's parent row.
        while ((originalSource != null) && !(originalSource is DataGridRow))
        {
            if (originalSource is Visual || originalSource is Visual3D)
            {
                originalSource = VisualTreeHelper.GetParent(originalSource);
            }
            else
            {
                // If we're in Logical Land then we must walk up the logical tree 
                // until we find a Visual/Visual3D to get us back to Visual Land.
                // See: http://www.codeproject.com/Articles/21495/Understanding-the-Visual-Tree-and-Logical-Tree-in
                originalSource = LogicalTreeHelper.GetParent(originalSource);
            }
        }

        row = originalSource as DataGridRow;
    }
}

}

Disclaimer : I've just copied/pasted some of these snippets out of a larger codebase so it may not be 100% correct, but hopefully enough to get you started.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM