0
votes

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

1
In simplified form your code works for me.. Try to bind not the command but Header to some property in VM and see, whether it gets value. Can it be, that you have dead lock somehow? You use lock()..Rekshino
Intriguing. Can you tell (or share) what the simplified code is? What did you have to take out? Thanks for reading my question!Dave
Rekshino, I removed Command binding and put in a binding from Header (MenuItem in ContextMenu to ViewModel property. No luck.Dave
Have you tried to check what do you have as ViewModel for your MenuItem(you can do it if you put a converter in your binding and set a brake point in it)? I have put a user control directly in window(not into TabItem) and set a DataContext from window's static resource. User control was nearly the same, the same binding for MenuItem. You say, that binding for selected item works?! Hmm.. Strangely..Rekshino
Thanks. See me update. I think something is stopping the Contextmenu from even being called. Maybe the right click is intercepted somewhere else?Dave

1 Answers

1
votes

So, as I have already written in comments, the binding works. I have thought, that you get ContextMenu invoked, but you write you didn't. MouseRightButtonClick can be intercepted by several things:

  • Implicit or explicit styles for grid or it's parents(If explicit - comment it out, if implicit - set your own dummy style)
  • Behaviors for grid or it's parents(comment it out)
  • Code behind(check, whether there are event handlers in grid or it's parents)

Make it consistently and you will find what is responsible for the issue.