2010-05-11 85 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