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

3

我正在尝试使依赖属性列表中的内容显示在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>

以上资源字典的一部分是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>

我觉得我发了太多的代码,但我不确定我能不能让这一部分更简单。只有分隔符和硬编码的菜单项出现了。所以绑定肯定出了问题。通常绑定并不难,但是现在当我想绑定一些不属于同一树的东西时,我感到有点迷失。

欢迎任何建议。 :)


你是如何使用FooList DependencyProperty的?特别是,你是将一个完全填充的列表分配给属性,还是将一个空列表分配给属性,然后在之后填充列表?DependencyProperty会传播到一个全新的列表,但如果你想监视列表中的更改,你需要使用ObservableCollection或其他实现INotifyCollectionChanged的方法。 - Dan Bryant
调用一个函数来触发从遗留系统读取数据,我循环遍历数据并执行 FooList.Add(myNewFoo);。您的意思是我应该将 List<> 简单地替换为 ObservableCollection<> 吗? - Mizipzor
嗯... 在我看来一切似乎都没问题!你可以把演示项目上传到某个地方吗?也许你缺少一个 DataContext? - mg007
2个回答

1

正如你所猜测的那样,你的问题似乎是由于使用List<Foo>而不是ObservableCollection<Foo>引起的。由于List<Foo>不会在属性更改时通知,因此让WPF识别你已添加或删除项目的唯一方法就是暂时将FooList属性设置为其他内容,然后再重新设置。

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

你的CompositeCollection中绑定无法工作的原因是CompositeCollection不是DependencyObject,因此它不能继承DataContext。


有没有可以替代 CompositeCollection 的 DependencyObject?虽然项目只有在上下文菜单显示时才会更新,我以前是在代码后台简单地处理它,但现在我想尝试使用数据绑定。 - Mizipzor
仅仅将List替换为ObservableCollection似乎可以使其正常工作。 - Mizipzor

0

我不知道你为什么要将 FooList 做成依赖属性。因为你没有将它作为绑定的目标,这是创建依赖属性最常见的原因之一。你也没有实现回调,所以它不能进行更改通知(创建依赖属性的第二个最常见的原因)。你也没有使用它来进行值继承。那么,为什么呢?

在我看来,你真正想要的是 FooList 成为普通的CLR属性,类型为 ObservableCollection<Foo> (或者任何实现了 INotifyCollectionChanged 的类)。这将会提供你所需的所有更改通知-至少针对你目前发布的代码。


这对我来说听起来像是一个误导。我们可以整天争论是否将FooList设为DependencyProperty还是CLR属性才合适。底线是,DependencyProperty可以做到CLR属性能做的一切,而且更多。虽然在某些情况下可能被认为是一个不好的选择(我不确定这是否是其中之一),但它与mizipzor正在遇到的问题无关。 - Ray Burns
你说得对,它是不是一个依赖属性并不重要。实际上,重要的是它的类型是实现了“INotifyCollectionChanged”的集合。 - Robert Rossney

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接