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!
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.