How to save the IsExpanded state in group headers of a listview

前端 未结 3 839
不思量自难忘°
不思量自难忘° 2020-12-03 03:18

I have quite a tricky problem:

I am using a ListView control with the ItemsSource set to a CollectionViewSource including a PropertyGroupDescription to group the Lis

相关标签:
3条回答
  • 2020-12-03 04:08

    You could create a new class with a Dictionary (say, ObjectType as a key to bool values), and give it an indexer:

        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;
            }
        }
    

    Then, instantiate it in a ResourceDictionary somewhere and bind the IsExpanded to it like this:

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

    That might well do it: a nice way of getting WPF to call your code and pass a parameter just when you need it. (That WPF lets you put subexpressions in indexers in a binding path was news to me - good though isn't it!)

    0 讨论(0)
  • 2020-12-03 04:13

    The accepted answer is wrong as explained in the comments. I wrote the following behavior which achieves the desired functionality:

    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
    }
    

    It attaches a dictionary to the containing ItemsControl which saves the expanded state for every group item. This will be peristent, even if the items in the control changes.

    Usage:

    <Expander>
        <i:Interaction.Behaviors>
            <behaviors:PersistGroupExpandedStateBehavior GroupName="{Binding Name}" />
        </i:Interaction.Behaviors>
        ...
    </Expander>
    
    0 讨论(0)
  • 2020-12-03 04:20

    The marked Answer Doesn't work in .Net 4.5.

    A simple solution for this is to add a

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

    property to your ViewModel (In this case Whatever objects CurrentListViewData holds)

    and then do:

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

    on your template.

    Simple and effective just like WPF should be

    0 讨论(0)
提交回复
热议问题