【问题标题】:Context Menu for listboxitem bound to observablecollection MVVM style绑定到 observablecollection MVVM 样式的 listboxitem 的上下文菜单
【发布时间】:2012-05-16 12:35:15
【问题描述】:

我有 3 个列表框,它们都绑定了两个相同类型的 3 个单独的可观察集合。我的 ViewModel 具有通过属性公开的可观察集合。这是一些拖放分组,源列表框可以将项目拖到两个不同的列表中。但我想让用户能够右键单击列表框并设置项目的属性。类型、名称等。我在其中使用数据模板,因为我希望所有三个框在功能上都相同。这很好用,当我单击单个项目时,我可以毫无问题地弹出上下文菜单。我的麻烦是我有一个名为 FieldType 的属性。它是一个具有 4 个潜在值的枚举。在我的一生中,我无法弄清楚如何将 MenuItem 的 IsChecked 属性绑定到该属性......无论如何在功能上。这是我尝试过的......

<DataTemplate x:Key="SFTemplateWithContextMenu">
        <TextBlock Text="{Binding Path=FieldName}" ><!--Tag="{Binding DataContext, ElementName=Window}"-->
         <TextBlock.ContextMenu>
                <ContextMenu >
                    <ContextMenu.Resources>
                        <Configurator:EnumToBooleanConverter x:Key="EnumToBooleanConverterc" />
                    </ContextMenu.Resources>
                    <MenuItem  Header="Rename..." />
                    <MenuItem Header="Field Type">
                        <MenuItem.Resources>
                            <Configurator:EnumToBooleanConverter x:Key="EnumToBooleanConverter" />
                        </MenuItem.Resources>
                        <MenuItem  Header="String" IsCheckable="True" IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Path=DataContext.FieldType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource EnumToBooleanConverterc}, ConverterParameter={x:Static Configurator:TypeDesc.String}, PresentationTraceSources.TraceLevel=High}"/>
                        <MenuItem  Header="Date" IsCheckable="True" IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Path=DataContext.FieldType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static Configurator:TypeDesc.Date}}"/>
                        <MenuItem  Header="Barcode" IsCheckable="True" IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Path=DataContext.FieldType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static Configurator:TypeDesc.BarCode}}" />
                    </MenuItem>
                </ContextMenu> 
            </TextBlock.ContextMenu>
        </TextBlock>
    </DataTemplate>

在上面的代码中,您可以在 String、Date 和 Barcode MenuItems 上看到我正在尝试做的事情(必须喜欢正在开发的代码)。我的问题是它应该调用的暴露属性。我不知道如何在我的 ViewModel 属性中获取与单击的项目相对应的可观察集合中的项目。我有一个值转换器 EnumToBoolean 将位于绑定上以获取检查或不检查。问题是在可观察集合中设置/获取该特定项目的属性。

有什么想法吗?需要更多代码?需要我澄清什么吗?我离我有多近?顺便说一句,ViewModel 代码是用 VB 2010 编写的。

谢谢 布莱斯

编辑: 我使用天使的建议尝试了以下...

        <DataTemplate x:Key="SFTemplateWithContextMenu">
        <TextBlock x:Name="Field" Text="{Binding Path=FieldName}" >
         <TextBlock.ContextMenu PlacementTarget="{Binding ElementName=Field}">
                    <MenuItem  Header="Rename..." />
                    <MenuItem Header="Field Type">
                        <MenuItem.Resources>
                            <Configurator:EnumToBooleanConverter x:Key="EnumToBooleanConverter" />
                        </MenuItem.Resources>
                        <MenuItem  Header="Date" IsCheckable="True" IsChecked="{Binding PlacementTarget.DataContext.FieldType, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static Configurator:TypeDesc.String}, PresentationTraceSources.TraceLevel=High}"/>
                    </MenuItem>
            </TextBlock.ContextMenu>
        </TextBlock>
    </DataTemplate>

但这给了我一个错误,说...无法在属性元素上设置属性。不确定这是否是 TextBlox 与 TextBox 的问题?您在示例中使用了 TextBox ...我的猜测是您的代码也会这样做。所以我然后尝试了以下...

        <DataTemplate x:Key="SFTemplateWithContextMenu">
        <TextBlock x:Name="Field" Text="{Binding Path=FieldName}" ><!--Tag="{Binding DataContext, ElementName=Window}"-->
         <TextBlock.ContextMenu>
                <ContextMenu PlacementTarget="{Binding ElementName=Field}" >
                    <MenuItem  Header="Rename..." />
                    <MenuItem Header="Field Type">
                        <MenuItem.Resources>
                            <Configurator:EnumToBooleanConverter x:Key="EnumToBooleanConverter" />
                        </MenuItem.Resources>
                        <MenuItem  Header="Date" IsCheckable="True" IsChecked="{Binding PlacementTarget.DataContext.FieldType, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static Configurator:TypeDesc.String}, PresentationTraceSources.TraceLevel=High}"/>
                    </MenuItem>
                </ContextMenu>
            </TextBlock.ContextMenu>
        </TextBlock>
    </DataTemplate>

但这会导致绑定错误... System.Windows.Data 错误:4:找不到与引用“ElementName = Field”绑定的源。 BindingExpression:(无路径);数据项=空;目标元素是'ContextMenu'(名称='');目标属性是“PlacementTarget”(类型“UIElement”)

看来绑定不起作用。有什么想法吗?

【问题讨论】:

    标签: wpf xaml listbox contextmenu observablecollection


    【解决方案1】:

    ContextMenu 不是可视化树的一部分。因此,默认情况下,它不会连接 \ 绑定到应用它的 TextBlock 的数据上下文...

    所以有两种方法可以做到这一点......

    1. 设置 ContextMenu.PlacementTarget 并在单个 MenuItem 的绑定中将其称为 Path

    例如

       <TextBox x:Name="MyTextBlock">
            <TextBox.ContextMenu PlacementTarget="{Binding ElementName=MyTextBlock}">
                <MenuItem 
                     Header="{Binding PlacementTarget.DataContext.MyHeader, 
                                      RelativeSource={RelativeSource
                                          AncestorType={x:Type ContextMenu}}}"
            </TextBox.ContextMenu>
      </TextBox>
    

    所以在上面的示例中...您希望将菜单项与文本框的数据上下文连接。所以你在ContextMenu 上定义PlacementTarget。此放置目标只能使用 2 种类型的绑定设置...ElementNameStaticResource。一旦上下文菜单通过PlacementTarget 连接到可视元素,使用meuitem 绑定中的Path 来解析数据上下文属性,即MyHeader

    使用代理元素方法...

    Bind datagrid column visibility MVVM

    【讨论】:

    • 由于 TextBox 是通过 DataTemplate 从绑定到可观察集合生成的,因此它们都将具有相同的 x:Name。 x:Name 必须是唯一的吗?如果它们都具有相同的名称,会不会混淆绑定到哪个?
    • 名称在 WPF 中是唯一的作用域,所以我猜ElementName 方法会起作用。请参阅有关名称范围的此链接...msdn.microsoft.com/en-us/library/ms746659.aspx
    • 我已经更新了我的帖子,以反映我在使用您建议的解决方案时遇到的当前问题...请您看一下,让我知道这对您是否有意义?
    • 我想代理元素方法会起作用。请参阅我提供的链接。 stackoverflow.com/questions/7711275/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-08
    • 2011-04-19
    • 1970-01-01
    • 2010-09-27
    • 2013-09-18
    • 2013-03-12
    相关资源
    最近更新 更多