Reputation: 21
I've got a standard WPF <DataGrid> with specific columns (i.e. not auto-generated). It's populated in C# by setting the ItemsSource to a list of objects which have various properties, and the various columns in the datagrid are bound (via Binding="{Binding XXX}" in the XAML) to the appropriate properties.
Now I want to have a context menu appear when the user right-clicks on the cell, but the content of the menu should vary depending on what cell the user clicks. I can't figure out how to do this, so any suggestions would be appreciated.
The ideal scenario is that each cell in a given column (not the column heading) would have the same context menu, and different columns would have different context menus. Some of the items in the menu might be greyed out if they don't apply to that particular row. The contents of the menu can be hard-coded based on which column the user clicked in, but I'd have to have some sort of dynamic mechanism (ideally via a C# function, not endless XAML) to decide when to grey out the menu items and when not to.
EDIT: I'm OK with a solution that lets me add menu items dynamically (in C# code) as the menu is appearing, if I can tell what cell it's appearing from. Actually I'd prefer such a thing, as I like C# better than XAML.
Upvotes: 2
Views: 4515
Reputation: 13354
Here is a full sample:
The VM:
public class MainViewModel : INotifyPropertyChanged
{
public ObservableCollection<Employee> Employees { get; set; }
public RelayCommand<Employee> FireCommand { get; set; }
private Employee selectedEmployee;
public Employee SelectedEmployee
{
get { return selectedEmployee; }
set
{
if (value != selectedEmployee)
{
selectedEmployee = value;
PropertyChanged(this, new PropertyChangedEventArgs("SelectedEmployee"));
FireCommand.RaiseCanExecuteChanged();
}
}
}
public MainViewModel()
{
Employees = new ObservableCollection<Employee>
{
new Employee{ Name = "Mickey Mouse", CanBeFired = true },
new Employee{ Name = "Barack Obama", CanBeFired = true },
new Employee{ Name = "Chuck Norris", CanBeFired = false /* Don't even try!!! */ }
};
foreach (Employee e in Employees)
{
e.PropertyChanged += (s, a) =>
{
if (a.PropertyName == "CanBeFired")
{
FireCommand.RaiseCanExecuteChanged();
}
};
}
FireCommand = new RelayCommand<Employee>(_ => Employees.Remove(SelectedEmployee), _ => SelectedEmployee != null && SelectedEmployee.CanBeFired);
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
The XAML:
<DataGrid ItemsSource="{Binding Employees}" SelectedValue="{Binding SelectedEmployee}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridCheckBoxColumn Binding="{Binding CanBeFired,UpdateSourceTrigger=PropertyChanged}"></DataGridCheckBoxColumn>
</DataGrid.Columns>
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Fire" Command="{Binding FireCommand}">
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
Note that you don't need a generics RelayCommand
so you can ignore the generic parameter and the _
parameters of the lambdas.
Upvotes: 1