Связывание с CollectionViewSource - PullRequest
       65

Связывание с CollectionViewSource

5 голосов
/ 01 марта 2011

Я пытаюсь реализовать сортировку комбинированного списка с использованием 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 пользовательского элемента управления, в котором находится это поле со списком, привязан к объекту, у которого есть ObservableCollection, называемая Configurations, и каждая конфигурация имеет свойство с именем AgencyName. Я проверил, что это работает нормально, используя стандартное связывание без cvs, поэтому я знаю, что с этим все нормально.

Любая помощь будет принята с благодарностью, поскольку у меня закончились оправдания моему боссу :). Я также не хочу опускаться в код и выполнять сортировку в коде (что я мог, когда собирал ObservableCollection, но ИМХО, что нарушает принцип СУХОГО).

Ответы [ 4 ]

3 голосов
/ 02 марта 2011

Что именно вы подразумеваете под «когда один из других блоков был изменен, все другие изменились, чтобы отразить»? Вы говорите о SelectedItem? Если это так, то это может помочь установить IsSynchronizedWithCurrentItem = false в вашем ComboBox.

Кроме того: я думаю, что до тех пор, пока вы создаете и сортируете ICollectionView в коде только один раз, никакого нарушения принципа DRY не существует, потому что то, что вы делаете больше, больше не требуется в XAML. Но я вижу, что могут быть и другие причины сказать, что такая функция, как сортировка, должна выполняться в представлении, говоря в терминах Model-View-ViewModel.

1 голос
/ 01 марта 2011

Не прочитал весь ваш пост, но проблема в том, что ресурсы распределяются по умолчанию.Поэтому каждое поле со списком ссылалось на одно и то же представление коллекции.Представление коллекции включает выбор отслеживания, поэтому изменение выбора в одном поле со списком повлияет на остальные.

Вместо того, чтобы перемещать CVS на локальный ресурс, вы можете просто запретить его общий доступ:

<CollectionViewSource x:Key="whatever" x:Shared="False" .../>
0 голосов
/ 07 июля 2017

Привязка зависит от VisualTree, который не является визуальным, поэтому 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>
0 голосов
/ 14 августа 2014

Хотя, возможно, уже слишком поздно, я оставляю этот ответ для тех, кто может столкнуться с этой проблемой. Ваша привязка для 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>
...