DataGrid Context Menu Not Binding Properly

35 Views Asked by At

I have a DataGrid to which I am trying to add a ContextMenu with a command binding. The ContextMenu is defined like so:

<DataGrid Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="4" Name="DgResults" Margin="10, 25, 10,10" AutoGenerateColumns="False">
            <DataGrid.ContextMenu>
            <ContextMenu>
                <MenuItem Command="{Binding GenerateDutyCycleCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
                          Header="Generate DC"></MenuItem>
            </ContextMenu>
            </DataGrid.ContextMenu>
</DataGrid>

And in the code-behind for the Window I have the command property defined:

        private ICommand? _command;

        public ICommand GenerateDutyCycleCommand
        {
            get
            {
                var m = 0;
                return _command ??= new CommandHandler(GenerateDutyCycle, CanExecute);
            }
        }

        public void GenerateDutyCycle()
        {
            MessageBox.Show("Command Executed");
            var selectedItem = DgResults.SelectedItem;
            var p = 0;
        }

        public bool CanExecute()
        {
            return Results.Count > 0;
        }

From the XAML if I right click on GenerateDutyCycleCommand and select Go To Definition, it goes the the property as expected, so it seems like everything is correct.

When I run the code though, the ContextMenu is always shown and the CanExecute method is never hit. Similarly, if I click on the menu item the GenerateDutyCycle method is never hit (or the message box shown). I have tried several modifications (e.g., setting the DataContext of the MenuItem to the Window) without success.

I also see while running that there is a XAML Binding Failure which states Cannot find source: RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1'. I am not clear on why this binding is failing if it can identify the definition (via Go To Definition).

Any help/guidance would be appreciated. TIA.

UPDATE: I was able to get this partially working by removing the ContextMenu definition from the DataGrid and adding a style (to Window.Resources) like so:

        <Style TargetType="{x:Type DataGridRow}">
            <Setter Property="Tag" 
                    Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, 
                            Path=DataContext}"/>
            <Setter Property="ContextMenu">
                <Setter.Value>
                    <ContextMenu DataContext="{Binding PlacementTarget.Tag, 
                                               RelativeSource={RelativeSource Self}}">
                        <MenuItem Command="{Binding GenerateDutyCycleCommand}"
                                  Header="Generate DC"></MenuItem>
                    </ContextMenu>
                </Setter.Value>
            </Setter>
        </Style>

With this style, when there are no results in the DataGrid then the ContextMenu is not shown. However, the breakpoints in the CanExecute and GenerateDutyCycle methods still do not get hit. In addition, when there are results and the menu item is clicked, the MessageBox is not shown.

1

There are 1 best solutions below

0
Dominick On

I managed to find a solution by adding the ContextMenu as a resource of the DataGrid and then assigning it in the row's style like so:

            <DataGrid.Resources>
                <ContextMenu x:Key="CtxDataGrid">
                    <ContextMenu.Items>
                        <MenuItem
                            Header="Generate DC"
                            Command="{Binding GenerateDutyCycleCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"
                            CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource  Mode=FindAncestor, AncestorType=DataGrid}}"/>
                    </ContextMenu.Items>
                </ContextMenu>
            </DataGrid.Resources>
            <DataGrid.RowStyle>
                <Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}">
                    <Setter Property="ContextMenu" Value="{StaticResource CtxDataGrid}" />
                </Style>
            </DataGrid.RowStyle>