0
votes

I realize this question has been asked before, but I'm unable to make do with the minimal code samples. I'm requesting a review on the entire thing.

I have a WPF DataGrid that displays a bunch of rows. Each row has a right-click ContextMenu with its own commands.

I'm not able to get the ContextMenu part right. It's complaining that "ContextMenu cannot have a logical or visual parent." Any help would be appreciated.

Thanks.

  • XAML File:

      <DataGrid ItemsSource="{Binding MyModules}" AutoGenerateColumns="False">
    
          <!-- Begin uncertain part -->
    
          <ContextMenu ItemsSource="{Binding Configuration.Commands}">
              <ContextMenu.ItemContainerStyle>
                  <Style TargetType="x:Type ContextMenuItem">
                      <Setter Property="Command" Value="Binding Command}" />
                  </Style>
              </ContextMenu.ItemContainerStyle>
          </ContextMenu>
    
          <!-- End uncertain part -->
    
          <DataGrid.Columns>
              <DataGridTemplateColumn Header="Module Name" Width="*" IsReadOnly="True">
                  <DataGridTemplateColumn.CellTemplate>
                      <DataTemplate>
                          <TextBlock Text="{Binding Configuration.Name}" />
                      </DataTemplate>
                  </DataGridTemplateColumn.CellTemplate>
              </DataGridTemplateColumn>
    
              <DataGridTemplateColumn Header="Module Description" Width="3*" IsReadOnly="True">
                  <DataGridTemplateColumn.CellTemplate>
                      <DataTemplate>
                          <TextBlock Text="{Binding Configuration.Description}" />
                      </DataTemplate>
                  </DataGridTemplateColumn.CellTemplate>
              </DataGridTemplateColumn>
    
          </DataGrid.Columns>
    
  • ViewModel Files:

      public ObservableCollection<IModule> MyModules { get; set; }
    
      // ...
    
      public interface IModule
      {
          Configuration Configuration { get; }
          void ExecuteCommand(Command command);
      }
    
      public class Configuration
      {
          public Guid Id { get; private set; }
          public string Name { get; private set; }
          public string Description { get; private set; }
          public List<Command> Commands = new List<Command>();
    
          public Configuration(string name, string description, List<Command> commands)
          {
              Id = new Guid();
              Name = name;
              Description= description;
              Commands = commands;
          }
      }
    

EDIT 1:

I got samples from a bunch of places. Now it compiles. The right-click context menu is completely empty, however.

<DataGrid ItemsSource="{Binding MyModules}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTemplateColumn Header="Configuration Name" Width="*" IsReadOnly="True">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Configuration.Name}">
                        <TextBlock.ContextMenu>
                            <ContextMenu ItemsSource="{Binding Configuration.Commands}">
                                <ContextMenu.ItemContainerStyle>
                                    <Style TargetType="MenuItem">
                                        <Setter Property="Command" Value="{Binding ItemAction}" />
                                        <Setter Property="Header" Value="{Binding ItemHeader}" />
                                    </Style>
                                </ContextMenu.ItemContainerStyle>
                            </ContextMenu>
                        </TextBlock.ContextMenu>
                    </TextBlock>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

        <DataGridTemplateColumn Header="Description" Width="3*" IsReadOnly="True">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Configuration.MainMenuDescription}">
                        <TextBlock.ContextMenu>
                            <ContextMenu ItemsSource="{Binding Configuration.Commands}">
                                <ContextMenu.ItemContainerStyle>
                                    <Style TargetType="MenuItem">
                                        <Setter Property="Command" Value="{Binding ItemAction}" />
                                        <Setter Property="Header" Value="{Binding ItemHeader}" />
                                    </Style>
                                </ContextMenu.ItemContainerStyle>
                            </ContextMenu>
                        </TextBlock.ContextMenu>
                    </TextBlock>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

Although I don't really like the second approach. I'm having to duplicate the ContextMenu markup for each column...

1
Try adding some dummy converter to your {Binding Configuration.Commands} to see what you actually get. It could be you get nothing there(maybe you initialize you collections late and you don't implement INotifyPropertyChanged), or that your ContextMenu doesn't share the TextBlock's DataContext.o_w
@o_w: Thank you for the suggestion. DataContext was fine, and the ViewModel was correct. It was just some XAML syntax I was looking for. mm8 solved it for me via the ItemContainerStyle approach.Stout

1 Answers

1
votes

You could define an ItemContainerStyle where you set the ContextMenu property for all rows:

<DataGrid ItemsSource="{Binding MyModules}" AutoGenerateColumns="False">
    <DataGrid.ItemContainerStyle>
        <Style TargetType="DataGridRow">
            <Setter Property="ContextMenu">
                <Setter.Value>
                    <ContextMenu ItemsSource="{Binding Configuration.Commands}">
                        <ContextMenu.ItemContainerStyle>
                            <Style TargetType="{x:Type ContextMenuItem}">
                                <Setter Property="Command" Value="{Binding Command}" />
                            </Style>
                        </ContextMenu.ItemContainerStyle>
                    </ContextMenu>
                </Setter.Value>
            </Setter>
        </Style>
    </DataGrid.ItemContainerStyle>


    <DataGrid.Columns>
        ...
    </DataGrid.Columns>
</DataGrid>