与CollectionViewSource绑定

时间:2011-03-01 20:24:34

标签: c# wpf data-binding collectionviewsource

我正在尝试使用CollectionViewSource实现一些组合框排序。此组合框实际上是数据模板的一部分,并在列表视图中重复。我的第一种方法似乎有效(使用CollectionViewSource),但我的所有组合框都共享相同的数据上下文。这使得每当其他一个盒子被改变时,所有其他盒子都改变以反映 - 而不是期望的副作用。

我决定退回并尝试使用内联xaml实现一个基本的组合框(不在数据模板中)来指定CollectionViewSource(而不是将cvs创建为静态资源)。我无法成功显示数据。因为我还是WPF的新手,所以我可能会完全错误。

这是我的组合框的xaml:

<ComboBox>
    <ComboBox.ItemsSource>
        <Binding>
            <Binding.Source>
                <CollectionViewSource Source="{Binding Path=Configurations}">
                    <CollectionViewSource.SortDescriptions>
                        <scm:SortDescription PropertyName="AgencyName" />
                    </CollectionViewSource.SortDescriptions>
                </CollectionViewSource>
            </Binding.Source>
        </Binding>
    </ComboBox.ItemsSource>
</ComboBox>

此组合框所在的用户控件的DataContext绑定到一个对象,该对象具有名为Configurations的ObservableCollection,并且每个配置都有一个名为AgencyName的属性。我已经证实使用标准绑定没有cvs这很好用,所以我知道一切都很好。

任何帮助都会非常感激,因为我已经找不到我的老板的借口:)。我也不想下载代码并在后面的代码中进行排序(我可以在构建ObservableCollection时使用,但是违反DRY原则的恕我直言)。

4 个答案:

答案 0 :(得分:3)

你究竟是什么意思“当其他一个盒子被改变时,所有其他盒子都改变了以反映”?你在谈论SelectedItem吗? 如果是这样,那么它可能有助于在您的ComboBox中设置IsSynchronizedWithCurrentItem = false

除此之外:我认为只要你在后面的代码中创建和排序你的ICollectionView一次,就不会违反DRY原则,因为你在XAML中不需要做更多的事情。但是我看到可能有其他理由说应该在视图中完成像排序这样的功能,用模型视图 - 视图模型来说。

答案 1 :(得分:1)

没有阅读您的整个帖子,但问题是默认情况下共享资源。因此,每个组合框都引用相同的集合视图。集合视图包括跟踪选择,因此在一个组合框中更改选择将影响其他组合。

不是将CVS移动到本地资源,而是可以阻止它被共享:

<CollectionViewSource x:Key="whatever" x:Shared="False" .../>

答案 2 :(得分:0)

虽然可能为时已晚,但我还是会为可能遇到此问题的其他人留下这个答案。 您对CollectionViewSource.Source的绑定不起作用,因为CollectionViewSource不属于可视/逻辑树,它既不继承数据上下文也不能引用ComboBox作为绑定源。我能够使用以下类以丑陋而简单的方式解决这个问题:

/// <summary>
/// Provides a way to set binding between a control
/// and an object which is not part of the visual tree.
/// </summary>
/// <remarks>
/// A bright example when you need this class is having an 
/// <see cref="ItemsControl"/> bound to a <see cref="CollectionViewSource"/>.
/// The tricky thing arises when you want the <see cref="CollectionViewSource.Source"/>
/// to be bound to some property of the <see cref="ItemsControl"/> 
/// (e.g. to its data context, and to the view model). Since 
/// <see cref="CollectionViewSource"/> doesn't belong to the visual/logical tree,
/// its not able to reference the <see cref="ItemsControl"/>. To stay in markup,
/// you do the following:
/// 1) Add an instance of the <see cref="BindingBridge"/> to the resources 
/// of some parent element;
/// 2) On the <see cref="ItemsControl"/> set the <see cref="BindingBridge.BridgeInstance"/> attached property to the
/// instance created on step 1) using <see cref="StaticResourceExtension"/>;
/// 3) Set the <see cref="CollectionViewSource.Source"/> to a binding which has 
/// source set (via <see cref="StaticResourceExtension"/>) to <see cref="BindingBridge"/>  
/// and path set to the <see cref="BindingBridge.SourceElement"/> (which will be the control 
/// on which you set the attached property on step 2) plus the property of interest
/// (e.g. <see cref="FrameworkElement.DataContext"/>):
/// <code>
///  <CollectionViewSource
///     Source="{Binding SourceElement.DataContext.Images, Source={StaticResource ImagesBindingBridge}}"/>
/// </code>.
/// 
/// So the result is that when assigning the attached property on a control, the assigned 
/// <see cref="BindingBridge"/> stores the reference to the control. And that reference can be 
/// retrieved from the <see cref="BindingBridge.SourceElement"/>.
/// </remarks>
public sealed class BindingBridge : DependencyObject
{
    #region BridgeInstance property

    public static BindingBridge GetBridgeInstance(DependencyObject obj)
    {
        Contract.Requires(obj != null);
        return (BindingBridge)obj.GetValue(BridgeInstanceProperty);
    }

    public static void SetBridgeInstance(DependencyObject obj, BindingBridge value)
    {
        Contract.Requires(obj != null);
        obj.SetValue(BridgeInstanceProperty, value);
    }

    // Using a DependencyProperty as the backing store for BridgeInstance.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty BridgeInstanceProperty =
        DependencyProperty.RegisterAttached("BridgeInstance", typeof(BindingBridge), typeof(BindingBridge),
        new PropertyMetadata(OnBridgeInstancePropertyChanged));

    #endregion BridgeInstance property

    #region SourceElement property

    public FrameworkElement SourceElement
    {
        get { return (FrameworkElement)GetValue(SourceElementProperty); }
        private set { SetValue(SourceElementPropertyKey, value); }
    }

    // Using a DependencyProperty as the backing store for SourceElement.  This enables animation, styling, binding, etc...
    private static readonly DependencyPropertyKey SourceElementPropertyKey =
        DependencyProperty.RegisterReadOnly("SourceElement", typeof(FrameworkElement), typeof(BindingBridge), new PropertyMetadata(null));

    public static readonly DependencyProperty SourceElementProperty;

    #endregion SourceElement property

    /// <summary>
    /// Initializes the <see cref="BindingBridge"/> class.
    /// </summary>
    static BindingBridge()
    {
        SourceElementProperty = SourceElementPropertyKey.DependencyProperty;
    }

    private static void OnBridgeInstancePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var sourceElement = (FrameworkElement)d;
        var bridge = (BindingBridge)e.NewValue;
        bridge.SourceElement = sourceElement;
    }
}

以下是一个使用示例(资源字典未显示):

 <ItemsControl
        infrastructure:BindingBridge.BridgeInstance="{StaticResource ImagesBindingBridge}">
        <ItemsControl.ItemsSource>
            <Binding>
                <Binding.Source>
                    <CollectionViewSource
                                Source="{Binding SourceElement.DataContext.Images, Source={StaticResource ImagesBindingBridge}, Mode=OneWay}">
                        <CollectionViewSource.SortDescriptions>
                            <componentModel:SortDescription PropertyName="Timestamp" Direction="Descending"/>
                        </CollectionViewSource.SortDescriptions>
                    </CollectionViewSource>
                </Binding.Source>
            </Binding>
        </ItemsControl.ItemsSource>
    </ItemsControl>

答案 3 :(得分:-1)

绑定取决于VisualTree,cvs不是可视化的,因此Binding不起作用。

您可以改用x:Reference。

<Border x:Name="border" />
<ComboBox>
    <ComboBox.ItemsSource>
        <Binding>
            <Binding.Source>
                <CollectionViewSource Source="{Binding Path=DataContext.Configurations, Source={x:Reference border}}">
                    <CollectionViewSource.SortDescriptions>
                        <scm:SortDescription PropertyName="AgencyName" />
                    </CollectionViewSource.SortDescriptions>
                </CollectionViewSource>
            </Binding.Source>
        </Binding>
    </ComboBox.ItemsSource>
</ComboBox>