2010-05-11 111 views
14

的组头我有一个相当棘手的问题:如何保存IsExpanded状态在ListView

我使用的是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属性设置为true。这意味着每当ListView刷新时,所有Expander控件都被展开。

但是我确实想保存每个扩展器的最后一个状态。我一直无法找到一种方法来保存每个ObjectType的扩展器状态列表。我正在试验一个绑定的HashTable和一个Converter,但是我没有提供ObjectType作为ConverterParameter,因为它总是作为一个字符串传递的。但这可能不是解决方案。

有人可以给我一个提示或解决方案的想法吗? :)

+0

你确定你真正的问题不是你目前的设计迫使你刷新列表视图吗? – 2010-05-11 16:05:27

+0

通过调用NotifyPropertyChanged(“CurrentListViewData”)刷新ListView。我不知道该怎么做。 – 2010-05-12 08:53:23

回答

10

您可以创建一个新的类字典(比如,对象类型列为重点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让你把索引器中的子表达式放在一个绑定路径中对我来说是新闻 - 虽然不是好事!)

+0

我还应该添加,而不是使用NotifyPropertyChanged()您可以绑定到实现INotifyCollectionChanged(http://msdn.microsoft.com/en-us/library/system.collections.specialized.inotifycollectionchanged.aspx),或使用ObservableCollection。不知道它不会导致相同的问题! – 2010-06-25 17:57:55

+0

棒极了!喜欢这个!谢谢!我将用它来做同样的事情。最好的部分是你可以存储的不仅仅是一个IsExpanded。实际上,我将使用它来存储有关基于分组的自动创建的节点的Viewmodel信息。 – MarqueIV 2011-03-29 01:08:04

+1

不错,但如果这能够奏效,不幸的是,它不会,据我所知。至少,不在.NET 4.5中。索引器中的子表达式作为字符串传递给索引器,而不是评估。 – Mark 2012-12-11 11:12:58

11

接受的答案是错误的,正如评论中所解释的那样。我写了下面的行为,达到了预期的功能:

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> 
+1

回复:我:Interaction.Behavior - 请参阅:http://stackoverflow.com/questions/18778490/i-interaction-behavior-option-is-not-即将申请beahviour和也http://stackoverflow.com/questions/3059821/the-tag-interaction-behaviors-does-not-exist-in-vs2010-blend-3 – 2014-06-29 13:45:14

+0

谢谢你!优雅的解决方案(喜欢它)! – CaptainPlanet 2016-09-07 20:34:26

+0

这个结果很好,如果碰巧GroupName为null,它会抛出一个错误。我将自己的行为修改为GroupName? string.Empty在它被使用的地方。虽然这是一个非常好的添加。谢谢! – TravisWhidden 2017-10-11 23:25:59

2

的显着回答没有在.NET 4.5的工作。

这样做的一个简单的解决方案是将

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

属性添加到您的视图模型(在这种情况下,无论对象CurrentListViewData持有)

然后执行:

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

您模板。

简单而有效就像WPF应该是

+0

我的理解是否正确:是否要将属于列表本身的“IsExpanded”属性保存到列表中的第一项?如果列表为空或第一个项目被删除会怎么样? – Woozar 2016-10-19 10:02:54