2011-03-01 3 views
5

Я пытаюсь реализовать некоторую сортировку со списком с помощью CollectionViewSource. Это поле со списком фактически является частью шаблона данных и повторяется в виде списка. Мой первый подход, похоже, работал (с использованием CollectionViewSource), но все мои комбинированные поля использовали один и тот же контекст данных. Это делало это так, когда каждый другой ящик был изменен, все остальные изменились, чтобы отразить - не желаемый побочный эффект.Связывание с CollectionViewSource

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

Любая помощь будет принята с благодарностью, поскольку у меня закончились оправдания моему боссу :). Я также не хочу, чтобы нужно было сбрасывать код и выполнять сортировку позади кода (что я мог бы при создании ObservableCollection, но IMHO, который нарушает принцип DRY).

ответ

3

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

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

+0

Ah IsSynchronizedWithCurrentItem работает как шарм. Он не синхронизируется со списком и не препятствует привязке к свойству ModelView. Я только что закончил проверять это. Это решает мою проблему с элегантностью. Я не могу просить больше, спасибо. – SRM

+0

Ты, человек, мне показалось, что я столкнулся с ограничением WPF, которое требовало использования копии всего средства просмотра для каждого истребителя Template. Было бы сложно даже описать проблему. –

+0

PS. Весьма странно, что это поведение по умолчанию WPF или, по крайней мере, дефолт в шаблонах, но хорошо. –

1

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

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

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

Это выглядит перспективным. Я дам этот снимок (так как мои cvs работали совершенно иначе, чем общий аспект). Благодаря! – SRM

+0

Ну, это выглядело многообещающим, но, похоже, это не сработало. Я добавил атрибут 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> 
0

Binding зависит на 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> 
Смежные вопросы