UPDATE!!! I've discovered that even this doesn't work when I put it in the DataGrid:
<DataGrid.ContextMenu>
<ContextMenu >
<MenuItem Header="Add Divider" Click="MenuItem_Click" />
</ContextMenu>
</DataGrid.ContextMenu>
This definitely works in a dummy project set up from scratch. So I don't think I'm having binding problems or datacontext problems.... I'm not getting that far. I suspect that some other part of the program is intercepting and handling the context menu (or right click). I didn't write the original code and it's a large code base. Can someone give me an idea what I should be looking for? What would stop the ContextMenu on the DataGrid from being called. Note that the DataGrid is in a ScrollViewer and StackPanel and the whole control is part of a larger window. In fact, it is one of many tabs.
-END UPDATE
I'm pulling my hair out trying to get a context menu on my DataGrid in my WPF/MVVM project. I post the XAML and ViewModel below. I would like to be able to right click on a row and call a command in the ViewModel that would change one of the column entries for that row.
The XAML (I call out what I think are the key points below)
<UserControl x:Class="Sears.UserInterface.Views.Technician.Alerts.AlertsLogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Sears.UserInterface.Views.Technician.Alerts"
xmlns:converters="clr-namespace:Common.Controls.Converters;assembly=Common.Controls"
mc:Ignorable="d"
d:DesignHeight="800"
d:DesignWidth="1200">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter" />
</UserControl.Resources>
<StackPanel Orientation="Horizontal">
<ScrollViewer>
<DataGrid Width="1000"
Margin="0"
HorizontalAlignment="Left"
AutoGenerateColumns="False"
Background="Transparent"
DataContext="{Binding}"
HeadersVisibility="Column"
ItemsSource="{Binding Alerts}"
SelectedItem="{Binding SelectedItemProperty, Mode=TwoWay}"
RowBackground="Transparent"
RowHeight="30"
Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=UserControl}}"
>
**<DataGrid.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Command="{Binding CloseAlertCommand}" Header="Close Alert"/>
</ContextMenu>
</DataGrid.ContextMenu>**
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Foreground" Value="Black" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="FontWeight" Value="Bold" />
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.Columns>
<DataGridTemplateColumn Width="*" Header="Id">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding Id}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="TimeStamp">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding AlertTime}" >
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="Severity">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding Severity}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="AlertText">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding AlertText}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="Details">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding Details}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="Active">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding Active}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="Acknowledged">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding Acknowledged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="Reported">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding Reported}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="Status">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding Status}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="Resolution">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding Resolution}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="Category">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding Category}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</ScrollViewer>
</StackPanel>
Notice the DataGrid.ContextMenu part. I've searched StackOverflow and tried various ideas to get the contextmenu showing up. Nothing. This particular example uses "Tag". I also tried putting the contextmenu in the resources sections as:
<ContextMenu x:Key="DataRowContextMenu" DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=DataGrid, Mode=FindAncestor}}">
<MenuItem x:Name="RowContMenuTransfer" Header="Close Alert" Command="{Binding DataContext.CloseAlertCommand}" CommandParameter="{Binding}" >
</MenuItem>
</ContextMenu>
The "Tag" way of doing it results in no binding errors. You just right click and nothing! Other ways (which I can post) gave binding errors in the output window. Possibly of note is the fact that the DataGrid is inside a ScrollViewer and StackPanel.
Here is the ViewModel
public class AlertsLogViewModel : ViewModelBase, IAlertsLogViewModel, IDisposable
{
private IAlertManager _model;
private readonly object _lock = new object();
private ICommand _closeAlertCommand;
public AlertsLogViewModel(IAlertManager model) : base("AlertsLogViewModel")
{
Alerts = new ObservableCollection<IAlert>();
_model = model ?? throw new ArgumentNullException("model");
_model.AlertStatusUpdated += _model_AlertStatusUpdated;
UpdateAlerts();
}
public IAlert SelectedItemProperty { get; set; }
public ICommand CloseAlertCommand
{
get { return _closeAlertCommand ?? (_closeAlertCommand = new DelegateCommand(CloseAlert)); }
}
private void CloseAlert()
{
_model.CloseAlert(SelectedItemProperty.SystemErrorGuid);
}
public ObservableCollection<IAlert> Alerts { get; private set; }
private void _model_AlertStatusUpdated(object sender, DataAccess.AlertStatusEventArgs e)
{
UpdateAlerts();
}
private void UpdateAlerts()
{
RunOnDispatcher(() =>
{
lock (_lock)
{
var modelAlerts = _model.GetAlerts().OrderBy(p => (int)p.Status).ThenByDescending(p => p.AlertTime);
Alerts.Clear();
if (modelAlerts != null)
{
foreach (var alert in modelAlerts)
{
Alerts.Add(alert);
}
}
}
}
);
NotifyPropertyChanged("Alerts");
}
public void Dispose()
{
if (_model != null)
_model.AlertStatusUpdated -= _model_AlertStatusUpdated;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Note the CloseAlertCommand and SelectedItemProperty. I do see that SelectedItemProperty is being called when I left click, but not right click. CloseAlert is never called.
Finally, in case it's relevant, let me mention that the AlertsLogView is inside a Technicial.xaml page and it is here that it's bound to the ViewModel:
<TabItem Header="Alerts"
PreviewMouseDown="OnPreviewMouseDown"
PreviewTouchDown="OnPreviewTouchDown">
<alertlog:AlertsLogView Margin="5"
DataContext="{Binding AlertsLogViewModel}" />
</TabItem>
Many thanks for any hints, references, pointers or solutions!
-Dave