CompositeCollection的数据源:为什么不能绑定到另一个控件的数据上下文,而必须使用CollectionViewSource?

5

我最近提出的另一个问题中,有人告诉我要使用CompositeCollection来访问ListBox的各种来源。

示例使用了XmlDataProvider来提供一些虚拟数据。但是,我有一个包含数据的视图模型。

我花了一些时间将我的ListBox与视图模型的数据绑定起来。最终我搞定了,但现在我想知道为什么我的以前的尝试没有成功。

成功的关键是CollectionViewSource。我的最初尝试是:

<CollectionContainer Collection="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Movies}"/>
<CollectionContainer Collection="{Binding ElementName=Window, Path=DataContext.Movies}"/>

我的想法是找到具有适当DataContext的Window,并针对数据进行绑定。您可以通过FindAncestor或通过ElementName来实现,因此我尝试了两种方法。这对我来说似乎非常合理,但显然我错了。当我运行应用程序时,我什么也没看到。
我还尝试了针对具有数据上下文的另一个控件进行绑定;例如StackPanel

那么,为什么我不能使用FindAncestorElementName1获取数据,而必须显式提供CollectionViewSource呢?

这是运行正常的代码。

<StackPanel DockPanel.Dock="Top">
    <ListBox ItemTemplateSelector="{StaticResource CustomDataTemplateSelector}">
        <ListBox.Resources>
            <CollectionViewSource x:Key="ViewSource" Source="{Binding Movies}"/>
        </ListBox.Resources>
        <ListBox.ItemsSource>
            <CompositeCollection>
                <CollectionContainer Collection="{Binding Source={StaticResource ViewSource}}"/>
                <CollectionContainer Collection="{Binding Source={StaticResource MyButtonsData}}"/>
            </CompositeCollection>
        </ListBox.ItemsSource>
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel IsItemsHost="True"
                   Width="{Binding (FrameworkElement.ActualWidth),
                               RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"/>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>
</StackPanel>

1 不,我没有忘记给窗口命名,也没有打错字。


在你的初始尝试中,调试控制台应该会出现一些绑定错误。当调试这种类型的问题时,它们非常有帮助。 - Pragmateek
@Pragmateek,实际上我并没有。我只收到了“无法解析对象类型属性”的提示,一旦通过DesignInstance添加数据上下文,问题就得到了解决。 - Em1
1个回答

6
我发现了一个microsoft.com上的讨论串,讨论了这个问题。
看起来这个“漏洞”已经存在多年了,但从未被修复。
我使用的解决方法(CollectionViewSource)也在那里被提出。
此外,确实不能使用ElementName。我不知道原因是什么,但x:Reference是用作另一个问题串中建议的解决方法的解决方法。
<CollectionContainer Collection="{Binding Source={x:Reference dummy}, Path=DataContext.Movies}"/>

有趣的是,在编辑时,XAML编译器会显示“对象引用未设置为对象实例”的错误。
但只要您不使用祖先类型,就可以编译和运行,因为那样会由于循环依赖而导致XmlParseException错误。
为避免循环依赖错误,您可以将CompositeCollection放入资源中,并通过StaticResource链接到该资源。然后您也可以使用祖先类型。
<ListBox.Resources>
    <CompositeCollection x:Key="CompositeCollection">
        <CollectionContainer Collection="{Binding Source={x:Reference stackPanel}, Path=DataContext.Movies}"/>
    </CompositeCollection>
</ListBox.Resources>
<ListBox.ItemsSource>
    <CompositeCollection>
        <CollectionContainer Collection="{Binding Source={StaticResource CompositeCollection}}"/>
    </CompositeCollection>
</ListBox.ItemsSource>

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