Reputation: 581
What I want to achieve: To collapse or expand all Expanders that share the same group name within a group style.
We have a ListBox and another ListBox nested inside it to display child items. The child items ItemsSource is bound to a CollectionView with Group descriptions attached.
The group item template is quite simple:
<Expander IsExpanded="{Binding Path=WHAT_TO_DO, Mode=TwoWay}">
<Expander.Header>
<TextBlock Text="{Binding Name}" />
</Expander.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" SharedSizeGroup="{Binding Name, Converter={StaticResource GroupNameToUniqueNameConverter}}" />
</Grid.RowDefinitions>
<ItemsPresenter/>
</Grid>
</Expander>
Upvotes: 0
Views: 4243
Reputation: 1089
Create your own control - GroupExpander - subclass it from Expander
Use the following helper method to find any parent in the visual hierarchy:
public static IEnumerable<T> RecurseParents<T>(this DependencyObject child)
{
if (child is T)
{
yield return Get<T>(child);
}
if (child != null)
{
foreach (var parent in RecurseParents<T>(child.GetParentObject()))
{
yield return parent;
}
}
}
public static DependencyObject GetParentObject(this DependencyObject child)
{
if (child == null) return null;
// handle content elements separately
var contentElement = child as ContentElement;
if (contentElement != null)
{
var parent = ContentOperations.GetParent(contentElement);
if (parent != null) return parent;
var fce = contentElement as FrameworkContentElement;
return fce != null ? fce.Parent : null;
}
// also try searching for parent in framework elements (such as DockPanel, etc)
var frameworkElement = child as FrameworkElement;
if (frameworkElement != null)
{
var parent = frameworkElement.Parent;
if (parent != null) return parent;
}
// if it's not a ContentElement/FrameworkElement, rely on VisualTreeHelper
return VisualTreeHelper.GetParent(child);
}
now write the logic of finding Level of GroupExpander by finding parent expander and calculating which expander this is
you can get the idea from the following code:
public class DataGridGroupExpander : GroupExpander
{
#region Class level variables
private bool mSettingIsExpanded = false;
#endregion
#region Constructors
public DataGridGroupExpander()
{
SetBinding(HeaderProperty, new Binding("Name"));
Loaded += DataGridGroupExpander_Loaded;
}
#endregion
#region Properties
#region Owner
private DataGrid mOwner = null;
public DataGrid Owner
{
get { return mOwner; }
private set
{
if (mOwner != value)
{
if (mOwner != null)
{
DetachOwner();
}
mOwner = value;
if (mOwner != null)
{
AttachOwner();
}
}
}
}
private void AttachOwner()
{
SetFieldName();
}
private void DetachOwner()
{
}
#endregion
#region ParentExpander
private DataGridGroupExpander mParentExpander = null;
public DataGridGroupExpander ParentExpander
{
get { return mParentExpander; }
private set
{
if (mParentExpander != value)
{
if (mParentExpander != null)
{
DetachParentExpander();
}
mParentExpander = value;
if (mParentExpander != null)
{
AttachParentExpander();
}
}
}
}
private void AttachParentExpander()
{
SetBinding(ParentExpanderLevelProperty, new Binding("Level") { Source = ParentExpander });
}
private void DetachParentExpander()
{
ClearValue(ParentExpanderLevelProperty);
}
#endregion
#endregion
#region Event handlers
private void DataGridGroupExpander_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
ParentExpander = this.RecurseParents<DataGridGroupExpander>().Skip(1).Take(20).FirstOrDefault();
Owner = this.RecurseParents<DataGrid>().FirstOrDefault();
LoadGroupIsExpandedState();
}
#endregion
#region Methods
private void LoadGroupIsExpandedState()
{
if (!mSettingIsExpanded && Owner != null)
{
mSettingIsExpanded = true;
try
{
IsExpanded = Owner.LoadGroupIsExpandedState(Header);
}
finally
{
mSettingIsExpanded = false;
}
}
}
private void PersistGroupIsExpandedState()
{
if (!mSettingIsExpanded && Owner != null)
{
Owner.PersistGroupIsExpandedState(Header, IsExpanded);
}
}
private void SetFieldName()
{
var fieldName = "";
if (Owner != null && Owner.Items != null && Owner.Items.GroupDescriptions.Count > Level)
{
var groupDescription = Owner.Items.GroupDescriptions[Level] as PropertyGroupDescription;
if(groupDescription!=null)
{
fieldName = groupDescription.PropertyName;
}
}
SetValue(FieldNameKey, fieldName);
}
#endregion
#region Overrides
tected override void HeaderUpdated()
{
LoadGroupIsExpandedState();
}
tected override void IsExpandedUpdated()
{
PersistGroupIsExpandedState();
}
#endregion
#region Dependency Properties
#region ParentExpanderLevel
public int ParentExpanderLevel
{
get { return (int)GetValue(ParentExpanderLevelProperty); }
set { SetValue(ParentExpanderLevelProperty, value); }
}
public static readonly System.Windows.DependencyProperty ParentExpanderLevelProperty =
System.Windows.DependencyProperty.Register(
"ParentExpanderLevel",
typeof(int),
typeof(DataGridGroupExpander),
new System.Windows.PropertyMetadata(-1, OnParentExpanderLevelPropertyChanged));
private static void OnParentExpanderLevelPropertyChanged(System.Windows.DependencyObject sender, System.Windows.DependencyPropertyChangedEventArgs e)
{
var control = sender as DataGridGroupExpander;
if (control != null)
{
control.ParentExpanderLevelUpdated();
}
}
private void ParentExpanderLevelUpdated()
{
SetValue(LevelKey, ParentExpanderLevel + 1);
}
#endregion
#region Level
public int Level
{
get { return (int)GetValue(LevelProperty); }
}
internal static readonly DependencyPropertyKey LevelKey = DependencyProperty.RegisterReadOnly(
"Level",
typeof(int),
typeof(DataGridGroupExpander),
new System.Windows.PropertyMetadata(0, OnLevelPropertyChanged));
public static readonly DependencyProperty LevelProperty = LevelKey.DependencyProperty;
private static void OnLevelPropertyChanged(System.Windows.DependencyObject sender, System.Windows.DependencyPropertyChangedEventArgs e)
{
var control = sender as DataGridGroupExpander;
if (control != null)
{
control.LevelUpdated();
}
}
private void LevelUpdated()
{
SetFieldName();
}
#endregion
#region FieldName
public string FieldName
{
get { return (string)GetValue(FieldNameProperty); }
}
internal static readonly DependencyPropertyKey FieldNameKey = DependencyProperty.RegisterReadOnly(
"FieldName",
typeof(string),
typeof(DataGridGroupExpander),
new PropertyMetadata(null, OnFieldNamePropertyChanged));
public static readonly DependencyProperty FieldNameProperty = FieldNameKey.DependencyProperty;
private static void OnFieldNamePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var control = sender as DataGridGroupExpander;
if (control != null)
{
control.FieldNameUpdated();
}
}
private void FieldNameUpdated()
{
}
#endregion
#endregion
}
Upvotes: 0
Reputation: 9677
You can in code behind iterate over all listbox items. For each listbox item you should see if it contains an expander. If it does you just expand or collapse it. See this link for how to iterate over items and finding a specific control.
Is there a way to iterate in a ListBox Items templates?
Upvotes: 0
Reputation: 7254
I don't think this can be done so easily. There are probably two options here
1) Manually - bind every expander that you need to eg. the first item in the group or anything else that is appropriate.
2) More automatic way would probably require writing a class that has a dictionary that holds states for each group. The problem here is that the controller needs to know which instance of expander is doing the binding or which group it is in. One solution that I see here is writing a custom converter that has a static instance of the class with dictionary and using the converter parameter to pass the group/reference (you can use binding here, so in xaml this is pure Ctrl+C, Ctrl+V operation)
Upvotes: 1