The application I'm working on uses a DataGrid to present entries to the user and these entries are grouped. The grouping is not tied to a single property of each entry, a single entry can be in multiple groups. The user is able to create groups at will and add entries to those groups.
We want the user to be able to edit the entries and the groups directly from this view. To remove a group, we'd like the user to be able to right click on the group and select "Delete group" from the context menu.
I've been able to give the GroupItem's Expander a context menu, but have no idea how to bind the Command or CommandParameter to the ViewModel.
How do I achieve the results I seek? I appreciate that this might require "moving" the context menu to a different part of the control, but we want a different context menu for the entries to the group headers. This we have achieved in our live code, but is not represented in the example below.
Here is a simplified example to represent what we are trying to achieve.
XAML:
<Window x:Class="DataGridHeaderTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<CollectionViewSource x:Key="GroupedEntriesSource" Source="{Binding Entries}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Key"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<Style x:Key="GroupContainerStyle" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" Background="#414040">
<Expander.ContextMenu>
<ContextMenu>
<!-- How do I bind Command and CommandParameter? -->
<MenuItem Header="Delete group" Command="{Binding DeleteGroupCommand}" CommandParameter="{Binding}" />
</ContextMenu>
</Expander.ContextMenu>
<Expander.Header>
<Grid>
<TextBlock Text="{Binding Path=Items[0].Key.Name}"/>
</Grid>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<DataGrid ItemsSource="{Binding Source={StaticResource GroupedEntriesSource}}" AutoGenerateColumns="False">
<DataGrid.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource GroupContainerStyle}">
<GroupStyle.Panel>
<ItemsPanelTemplate>
<DataGridRowsPresenter/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Value.Name, Mode=OneWay}"/>
<DataGridTextColumn Header="Data" Binding="{Binding Value.Val, UpdateSourceTrigger=LostFocus}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Code behind:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
namespace DataGridHeaderTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
CreateData();
DeleteGroupCommand = new TestCommand(DeleteGroup);
DataContext = this;
InitializeComponent();
}
void CreateData()
{
Entries = new ObservableCollection<KeyValuePair<Group, Entry>>();
Group group1 = new Group() { Name = "Group1" };
Group group2 = new Group() { Name = "Group2" };
Entry entry1 = new Entry() { Name = "Entry1", Val = "Val1" };
Entry entry2 = new Entry() { Name = "Entry2", Val = "Val2" };
Entry entry3 = new Entry() { Name = "Entry3", Val = "Val3" };
Entries.Add(new KeyValuePair<Group, Entry>(group1, entry1));
Entries.Add(new KeyValuePair<Group, Entry>(group1, entry3));
Entries.Add(new KeyValuePair<Group, Entry>(group2, entry2));
Entries.Add(new KeyValuePair<Group, Entry>(group2, entry3));
}
void DeleteGroup(object group)
{
// I want to run this when "Delete group" is selected from the context menu of the Group Expander.
// I want the Group object associated with the Group Expander passed as the parameter
}
public ObservableCollection<KeyValuePair<Group, Entry>> Entries { get; set; }
public ICommand DeleteGroupCommand { get; set; }
public class Group
{
public string Name { get; set; }
}
public class Entry
{
public string Name { get; set; }
public string Val { get; set; }
}
public class TestCommand : ICommand
{
public delegate void ICommandOnExecute(object parameter);
private ICommandOnExecute _execute;
public TestCommand(ICommandOnExecute onExecuteMethod)
{
_execute = onExecuteMethod;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_execute.Invoke(parameter);
}
}
}
}