绑定到 List<> 的 WPF 上下文菜单依赖属性

发布于 2024-08-25 16:17:29 字数 2073 浏览 2 评论 0 原文

我试图使作为依赖属性的列表的内容显示在 WPF 上下文菜单中。

我有一个具有以下依赖属性的类,即 Foo 的列表(数据保存类):

    public List<Foo> FooList
    {
        get { return (List<Foo>)GetValue(FooListProperty); }
        set { SetValue(FooListProperty, value); }
    }
    public static DependencyProperty FooListProperty =
        DependencyProperty.Register("FooList", typeof(List<Foo>),
            typeof(FooButton));

在 XAML 中,我设置了以下静态资源,我认为需要它,因为上下文菜单不是可视化树的一部分:

<UserControl.Resources>
    <ResourceDictionary>            
        <CollectionViewSource 
            x:Key="FooListSource"
            Source="{Binding FooList}"/>

        <!-- ... -->

    </ResourceDictionary>
</UserControl.Resources>

也是 ResourceDictionary 的一部分上面是一个 CompositeCollection,需要它才能使项目显示在实际的上下文菜单中。如果 UserControl CanStop 属性为 true,我们还会显示分隔符和停止命令。尽管菜单项本身会显示,但这些绑定也会失败。因此,如果我能找出这些失败的原因,那么列表可能会更容易。

<CompositeCollection x:Key="FooListItems">
    <CollectionContainer 
        Collection="{Binding Source={StaticResource FooListSource}}"/>
    <Separator 
        Visibility="{Binding CanStop,
            Converter={StaticResource VisibleIfTrue}}" />
    <MenuItem 
        Command="{x:Static Buttons:FooButton.Stop}"
        Header="Stop"
        Visibility="{Binding CanStop,
            Converter={StaticResource VisibleIfTrue}}"/>
</CompositeCollection>

最后是上下文菜单本身,也在 ResourceDictionary 中:

<ContextMenu 
    x:Key="FooButtonMenu"
    ItemsSource="{Binding Source={StaticResource FooListItems}}" 
    ItemTemplate="{StaticResource FooListTemplate}"
    <ContextMenu.CommandBindings>
        <CommandBinding  
                Command="{x:Static Buttons:FooButton.Stop}"
                Executed="Stop_Executed" />
    </ContextMenu.CommandBindings>
</ContextMenu>

我觉得我发布了很多代码,但我不确定我是否可以使这一部分变得更简单。仅显示分隔符和硬编码的菜单项。所以一定是绑定出了什么问题。绑定通常并不难,但现在当我想绑定不属于同一棵树的东西时,我感到有点迷失。

欢迎任何建议。 :)

Im trying to make the contents of a List thats a dependency property show up in a WPF context menu.

I have a class with the following dependency property, a list of Foo's (data holding class):

    public List<Foo> FooList
    {
        get { return (List<Foo>)GetValue(FooListProperty); }
        set { SetValue(FooListProperty, value); }
    }
    public static DependencyProperty FooListProperty =
        DependencyProperty.Register("FooList", typeof(List<Foo>),
            typeof(FooButton));

In XAML I set up the following static resource, I assume its needed since the context menu isnt part of the visual tree:

<UserControl.Resources>
    <ResourceDictionary>            
        <CollectionViewSource 
            x:Key="FooListSource"
            Source="{Binding FooList}"/>

        <!-- ... -->

    </ResourceDictionary>
</UserControl.Resources>

Also part of the ResourceDictionary above is a CompositeCollection which is needed to make the items show up in the actual context menu. If the UserControl CanStop property is true, we also show a separator and a stop command. These bindings does also fail, although the MenuItems themselves show up. So If I can figure out why these fail, the List might be easier.

<CompositeCollection x:Key="FooListItems">
    <CollectionContainer 
        Collection="{Binding Source={StaticResource FooListSource}}"/>
    <Separator 
        Visibility="{Binding CanStop,
            Converter={StaticResource VisibleIfTrue}}" />
    <MenuItem 
        Command="{x:Static Buttons:FooButton.Stop}"
        Header="Stop"
        Visibility="{Binding CanStop,
            Converter={StaticResource VisibleIfTrue}}"/>
</CompositeCollection>

And finally the context menu itself, also in the ResourceDictionary:

<ContextMenu 
    x:Key="FooButtonMenu"
    ItemsSource="{Binding Source={StaticResource FooListItems}}" 
    ItemTemplate="{StaticResource FooListTemplate}"
    <ContextMenu.CommandBindings>
        <CommandBinding  
                Command="{x:Static Buttons:FooButton.Stop}"
                Executed="Stop_Executed" />
    </ContextMenu.CommandBindings>
</ContextMenu>

I feel Im posting way to much code but Im not sure I can make this piece any simpler. Only the separator and the hardcoded menuitem shows up. So something must be messed up with the bindings. Bindings are usually not that hard but now when I want to bind something thats not really part of the same tree I feel a bit lost.

Any suggestions are welcome. :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

稀香 2024-09-01 16:17:29

正如您所怀疑的,您的问题似乎确实是由使用 List 而不是 ObservableCollection 引起的。由于 List 不会通知属性更改,因此让 WPF 识别您已添加或删除项目的唯一方法是将 FooList 属性临时设置为其他内容,然后再设置它后退。

无需切换到 CLR 属性。只需将 List 更改为 ObservableCollection 即可。

CompositeCollection 中的绑定不起作用的原因是 CompositeCollection 不是 DependencyObject,因此它无法继承 DataContext

As you suspected, your problem does seem to be caused by the use of List<Foo> instead of ObservableCollection<Foo>. Since List<Foo> doesn't notify on property changes, the only way to get WPF to recognize you've added or removed an item is to temporarily set the FooList property to something else and then set it back.

There is no need to switch to a CLR property. Just change List<Foo> to ObservableCollection<Foo>.

The reason the bindings in your CompositeCollection aren't working is that CompositeCollection is not a DependencyObject, so it can't inherit a DataContext.

握住你手 2024-09-01 16:17:29

我不明白为什么您将 FooList 设置为依赖属性。您没有将其作为绑定的目标,这是创建依赖项属性的最常见原因。您尚未实现回调,因此它无法执行更改通知(创建依赖项属性的第二个最常见原因)。您没有将它用于值继承。那么为什么呢?

在我看来,您真正想要的是 FooList 成为 ObservableCollection 类型的普通 CLR 属性(或任何实现 INotifyCollectionChanged 的类)代码>)。这将完成您需要的所有更改通知 - 至少是您迄今为止发布的代码所需的。

I don't see why you've made FooList a dependency property. You're not making it the target of a binding, which is the most common reason to create a dependency property. You haven't implemented a callback, so it can't do change notification (the second most common reason to create a dependency property). You're not using it for value inheritance. So why, then?

It seems to me that what you really want is for FooList to be a normal CLR property of type ObservableCollection<Foo> (or any class that implements INotifyCollectionChanged). That will do all the change notification that you need - at least, that you need for the code you've posted so far.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文