Как свернуть / развернуть несколько элементов управления Expander в зависимости от имени группы в WPF?

Чего я хочу добиться: свернуть или развернуть все расширители, которые имеют одно и то же имя группы в стиле группы.

У нас есть ListBox и еще один ListBox, вложенный в него для отображения дочерних элементов. Дочерние элементы ItemsSource привязаны к CollectionView с вложенными описаниями групп.

Шаблон группового элемента довольно прост:

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

3 ответа

Я не думаю, что это можно сделать так легко. Здесь возможно два варианта

1) Вручную - свяжите каждый расширитель, который вам нужен, например. первый элемент в группе или что-либо еще, что подходит.

2) Более автоматический способ, вероятно, потребует написания класса, в котором есть словарь, содержащий состояния для каждой группы. Проблема здесь в том, что контроллеру необходимо знать, какой экземпляр экспандера выполняет связывание или в какую группу он входит. Одно из решений, которое я вижу здесь, - это написание пользовательского конвертера со статическим экземпляром класса со словарем и использование конвертера параметр для передачи группы / ссылки (здесь можно использовать привязку, поэтому в xaml это чистая операция Ctrl+C, Ctrl+V)

Создайте свой собственный элемент управления - GroupExpander - подкласс его из Expander

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

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

Теперь напишите логику нахождения уровня GroupExpander, найдя родительский расширитель и вычислив, какой это расширитель

Вы можете получить идею из следующего кода:

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
}

Вы можете в коде позади перебирать все элементы списка. Для каждого элемента списка вы должны увидеть, содержит ли он расширитель. Если это произойдет, вы просто развернете или свернете это. Смотрите эту ссылку, чтобы узнать, как перебирать элементы и находить определенный элемент управления.

Есть ли способ перебирать шаблоны элементов ListBox?

Другие вопросы по тегам