【问题标题】:Access Data Context properties from Context Menu从上下文菜单访问数据上下文属性
【发布时间】:2022-01-23 19:40:03
【问题描述】:

我尝试了解一下列表框视图与上下文菜单结合的工作原理,因此我编写了以下 XAML 代码:

<UserControl>
   ...
     <ListView
                x:Name="level1Lister"
                Grid.Row="1"
                behaviours:AutoScrollListViewBehaviour.ScrollOnNewItem="True"
                ItemsSource="{Binding LogBuffer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                <ListView.ItemContainerStyle>
                    <Style TargetType="ListViewItem">
                        <Setter Property="Padding" Value="1" />
                        <Setter Property="Margin" Value="2,0" />
                        <Setter Property="BorderThickness" Value="0" />
                    </Style>
                </ListView.ItemContainerStyle>
                <ListView.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Vertical" />
                    </ItemsPanelTemplate>
                </ListView.ItemsPanel>
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <StackPanel.ContextMenu>
                                <ContextMenu>
                                    <MenuItem Command="{Binding Path=DataContext.ValidateAllCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, FallbackValue=9999999999}" Header="Copy" />
                                </ContextMenu>
                            </StackPanel.ContextMenu>
                            <TextBlock Foreground="{Binding Color, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Text="{Binding Message, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                        </StackPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
</UserControl>

我的主要问题是我无法访问我的“ValidateAllCommand”函数,出于某种原因......我认为它与“RelativeSource={RelativeSource FindAncestor ...”部分有关,但我可以'不知道怎么做。

【问题讨论】:

    标签: c# wpf xaml


    【解决方案1】:

    ContextMenu 的“问题”是它不是可视化树的一部分。原因是MenuItem 使用Popup 来托管内容。虽然Popup 本身是可视化树的一部分,但它的子内容是断开的,因为它在新的Window 实例中呈现。我们知道树中只能有一个Window,而这个Window 必须是根。
    由于Binding.RelativeSource 遍历可视化树,从Popup 的分离树中开始寻找绑定源,因此Bindig 无法解析。

    Popup.Child 内容继承了PopupDataContext。在ContextMenu 的情况下,这意味着MenuItemContextMenuContextMenu 的父级继承DataContext,更准确地说。在您的场景中,DataContextListBoxItem 的数据模型,即DataTemplateDataContext

    这意味着,一种解决方案可能是在项目模型中实现命令。

    第二种解决方案是使用路由命令。在您的场景中,此解决方案可能更合理。路由的命令/事件可以跨越两棵树之间的边界。

    在以下示例中,我使用ApplicationCommands.Copy 命令,这是预定义的路由命令之一。 MenuItem.Parameter 绑定到DataContext,这是项目数据模型(继承自ContextMenu,如前所述)。这样命令处理程序就可以知道数据源。
    可以使用UIElement.CommandBindings 属性将事件处理程序附加到任何父元素。在示例中,处理程序附加到 ListBox 元素:

    MainWindow.xaml

    <ListBox>
      <ListBox.CommandBindings>
        <CommandBinding Command="{x:Static ApplicationCommands.Copy}"
                        Executed="CopyCommand_Executed" />
      </ListBox.CommandBindings>
      <ListBox.ItemTemplate>
        <DataTemplate DataType="{x:Type local:DataItem}">
          <StackPanel>
            <StackPanel.ContextMenu>
              <ContextMenu>
          
                <!-- 
                  Here we are in a detached visual tree. 
                  The DataContext is inherited from the ContextMenu element. 
                  The framework allows routed events to cross the boundaries between the trees.
                -->
                <MenuItem Command="{x:Static ApplicationCommands.Copy}"
                          CommandParameter="{Binding}"
                          Header="Copy" />
              </ContextMenu>
            </StackPanel.ContextMenu>
    
            <TextBlock />
          </StackPanel>
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>
    

    MainWindow.xaml.cs

    private void CopyCommand_Executed(object sender, ExecutedRoutedEventArgs e)
    {
      var listBoxItemModel = e.Parameter as LogBufferItem;
    
      // TODO::Handle Copy command. For example:
      var commandTarget = this.DataContext as ICommandModel;
      commandTarget.ValidateAllCommand.Execute(listBoxItemModel);
    }
    

    第三种但不推荐的解决方案是将ContextMenu 父级的DataContext 绑定到感兴趣的上下文:

      <ListBox.ItemTemplate>
        <DataTemplate DataType="{x:Type local:DataItem}">
          <StackPanel>
            <!-- DataTemplate DataContext -->
    
            <Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=ListBox}, Path=DataContext}">
    
              <!-- ListBox DataContext -->
              <Grid.ContextMenu>
                <ContextMenu>
          
                  <!-- 
                    Here we are in a detached visual tree. 
                    The DataContext is inherited from the ContextMenu/Grid element. 
                  -->
                  <MenuItem Command="{Binding ValidateAllCommand}"
                            Header="Copy" />
                </ContextMenu>
              </Grid.ContextMenu>
            </Grid>
    
            <!-- DataTemplate DataContext -->
            <TextBlock />
          </StackPanel>
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox
    

    【讨论】:

    • 非常感谢,您的解释是圣诞礼物!
    • 哈哈,不错。圣诞快乐!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多