Как сохранить состояние IsExpanded в заголовках группы представления списка - PullRequest
16 голосов
/ 11 мая 2010

У меня довольно сложная проблема:

Я использую элемент управления ListView, для которого для ItemsSource установлено значение CollectionViewSource, включая PropertyGroupDescription, для группировки элементов ListView. CollectionViewSource выглядит следующим образом:

<CollectionViewSource x:Key="ListViewObjects">
   <CollectionViewSource.Source>
      <Binding Path="CurrentListViewData"/>
   </CollectionViewSource.Source>
   <CollectionViewSource.GroupDescriptions>
      <PropertyGroupDescription PropertyName="ObjectType" />
   </CollectionViewSource.GroupDescriptions>
</CollectionViewSource>

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

<ListView.GroupStyle>
   <GroupStyle>
      <GroupStyle.ContainerStyle>
         <Style TargetType="{x:Type GroupItem}">
            <Setter Property="Margin" Value="5"/>
            <Setter Property="Template">
               <Setter.Value>
                  <ControlTemplate TargetType="{x:Type GroupItem}">
                     <Expander IsExpanded="True">
                        <Expander.Header>
                           <DockPanel>
                              <TextBlock Text="{Binding Path=Items[0].ObjectType />
                           </DockPanel>
                        </Expander.Header>
                        <Expander.Content>
                           <ItemsPresenter />
                        </Expander.Content>
                     </Expander>
                  </ControlTemplate>
               </Setter.Value>
            </Setter>
         </Style>
      </GroupStyle.ContainerStyle>
   </GroupStyle>
</ListView.GroupStyle>

Как видите, для свойства IsExpanded Expander установлено значение true. Это означает, что всякий раз, когда ListView обновляется, все элементы управления Expander раскрываются.

Однако я хочу сохранить последнее состояние каждого Расширителя. Я не смог найти способ сохранить список состояний Expander для ObjectType. Я экспериментировал со связанным HashTable и Converter, но мне не удалось предоставить ObjectType в качестве ConverterParameter, потому что он всегда передавался как строка. Но это не может быть решением в любом случае.

Может кто-нибудь дать мне подсказку или идею для решения, пожалуйста? :)

Ответы [ 3 ]

16 голосов
/ 10 апреля 2013

Принятый ответ неверен, как объяснено в комментариях. Я написал следующее поведение, которое достигает желаемой функциональности:

public class PersistGroupExpandedStateBehavior : Behavior<Expander>
{
    #region Static Fields

    public static readonly DependencyProperty GroupNameProperty = DependencyProperty.Register(
        "GroupName", 
        typeof(object), 
        typeof(PersistGroupExpandedStateBehavior), 
        new PropertyMetadata(default(object)));

    private static readonly DependencyProperty ExpandedStateStoreProperty =
        DependencyProperty.RegisterAttached(
            "ExpandedStateStore", 
            typeof(IDictionary<object, bool>), 
            typeof(PersistGroupExpandedStateBehavior), 
            new PropertyMetadata(default(IDictionary<object, bool>)));

    #endregion

    #region Public Properties

    public object GroupName
    {
        get
        {
            return (object)this.GetValue(GroupNameProperty);
        }

        set
        {
            this.SetValue(GroupNameProperty, value);
        }
    }

    #endregion

    #region Methods

    protected override void OnAttached()
    {
        base.OnAttached();

        bool? expanded = this.GetExpandedState();

        if (expanded != null)
        {
            this.AssociatedObject.IsExpanded = expanded.Value;
        }

        this.AssociatedObject.Expanded += this.OnExpanded;
        this.AssociatedObject.Collapsed += this.OnCollapsed;
    }

    protected override void OnDetaching()
    {
        this.AssociatedObject.Expanded -= this.OnExpanded;
        this.AssociatedObject.Collapsed -= this.OnCollapsed;

        base.OnDetaching();
    }

    private ItemsControl FindItemsControl()
    {
        DependencyObject current = this.AssociatedObject;

        while (current != null && !(current is ItemsControl))
        {
            current = VisualTreeHelper.GetParent(current);
        }

        if (current == null)
        {
            return null;
        }

        return current as ItemsControl;
    }

    private bool? GetExpandedState()
    {
        var dict = this.GetExpandedStateStore();

        if (!dict.ContainsKey(this.GroupName))
        {
            return null;
        }

        return dict[this.GroupName];
    }

    private IDictionary<object, bool> GetExpandedStateStore()
    {
        ItemsControl itemsControl = this.FindItemsControl();

        if (itemsControl == null)
        {
            throw new Exception(
                "Behavior needs to be attached to an Expander that is contained inside an ItemsControl");
        }

        var dict = (IDictionary<object, bool>)itemsControl.GetValue(ExpandedStateStoreProperty);

        if (dict == null)
        {
            dict = new Dictionary<object, bool>();
            itemsControl.SetValue(ExpandedStateStoreProperty, dict);
        }

        return dict;
    }

    private void OnCollapsed(object sender, RoutedEventArgs e)
    {
        this.SetExpanded(false);
    }

    private void OnExpanded(object sender, RoutedEventArgs e)
    {
        this.SetExpanded(true);
    }

    private void SetExpanded(bool expanded)
    {
        var dict = this.GetExpandedStateStore();

        dict[this.GroupName] = expanded;
    }

    #endregion
}

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

Использование:

<Expander>
    <i:Interaction.Behaviors>
        <behaviors:PersistGroupExpandedStateBehavior GroupName="{Binding Name}" />
    </i:Interaction.Behaviors>
    ...
</Expander>
9 голосов
/ 25 июня 2010

Вы можете создать новый класс со словарем (скажем, ObjectType как ключ к значениям bool) и присвоить ему индексатор:

    Dictionary<ObjectType, bool> expandStates = new Dictionary<ObjectType, bool>();

    public bool this[ObjectType key]
    {
        get
        {
            if (!expandStates.ContainsKey(key)) return false;
            return expandStates[key];
        }
        set
        {
            expandStates[key] = value;
        }
    }

Затем создайте его экземпляр в ResourceDictionary где-нибудь и привяжите IsExpanded к нему следующим образом:

<Expander IsExpanded="{Binding Source={StaticResource myExpMgr}, Path=[Items[0].ObjectType]}">

Это вполне может сделать это: хороший способ заставить WPF вызывать ваш код и передавать параметр именно тогда, когда вам это нужно. (То, что WPF позволяет размещать подвыражения в индексаторах на пути привязки, было новостью для меня - хорошо, правда, не так ли!)

2 голосов
/ 06 августа 2014

Отмеченный ответ не работает в .Net 4.5.

Простым решением для этого является добавление

    private bool _isExpanded = true;
    public bool IsExpanded
    {
        get { return _isExpanded; }
        set { _isExpanded = value; }
    }

свойство вашей ViewModel (В этом случае все объекты CurrentListViewData сохраняются)

и затем сделайте:

<Expander IsExpanded="{Binding Items[0].IsExpanded}">

в вашем шаблоне.

Простой и эффективный, как должен быть WPF

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...