CollectionViewSource的绑定

6
我正在尝试使用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个回答

4
你说的“其他框中任意一个发生变化时,其余框都会相应变化”的意思是什么?你是在谈论SelectedItem吗? 如果是的话,将ComboBox的IsSynchronizedWithCurrentItem设置为false可能会有所帮助。
另外:我认为只要在代码后端创建和排序你的ICollectionView一次,就不会违反DRY原则,因为你在那里做的其他工作在XAML中不再需要。但是我认为可能有其他原因说像排序这样的特性应该在视图中完成,从Model-View-ViewModel的角度来看。

啊,IsSynchronizedWithCurrentItem 真是太好用了。它不会与列表同步,也不会阻止绑定到 ModelView 上的属性。我刚刚验证过了。它以优雅的方式解决了我的问题。我不能再要求更多了,谢谢。 - SRM
你真棒,我以为我遇到了一个 WPF 的限制,需要为每个模板的生成使用整个视图源的副本。这甚至很难在谷歌上搜索/描述这个问题。 - j riv
有点奇怪,这是默认的WPF行为,或者至少是默认的模板,但没关系。 - j riv

1

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

与其将 CVS 移动到本地资源,不如防止它被共享:

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

看起来很有前途。我会试一试(因为我的cvs除了共享方面完美无缺)。谢谢! - SRM
看起来很有前途,但似乎没有起作用。我添加了x:Shared属性(由于某种原因未在intelisense中显示),组合框停止呈现任何内容。将标志更改为True,一切正常运行。 - SRM

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>

-1

绑定取决于VisualTree,而cvs不是可视化的,因此绑定无法工作。

您可以使用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>

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