Как да свиете/разширите множество контроли на Expander в зависимост от името на групата в WPF?

Какво искам да постигна: Да свия или разширя всички Expanders, които споделят едно и също име на група в стил на група.

Имаме 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>

person David    schedule 02.07.2010    source източник
comment
Опитвате ли се да направите това в WPF стил?   -  person Gishu    schedule 02.07.2010
comment
Да, това е GroupStyle на ListBox, който има Style Trigger, който задава свойството Template. Така че това е ControlTemplate с TargetType={x:Type GroupItem}   -  person David    schedule 02.07.2010


Отговори (3)


Не мисля, че това може да стане толкова лесно. Тук вероятно има два варианта

1) Ръчно - свържете всеки експандер, от който се нуждаете, напр. първият елемент в групата или нещо друго, което е подходящо.

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

person kubal5003    schedule 02.07.2010
comment
Всъщност опитвахме решение, което беше нещо като вашата втора опция, но не мога да свържа името на групата с нормално {Binding}, тъй като според мен е динамично на контекста на данни? Опитахме се да предадем името на групата и речника (в PM) на конвертор в мултибиндинг, но имаше някои проблеми в метода за обратно преобразуване, тъй като не знаем името на групата. - person David; 02.07.2010
comment
Обвързването работи и за динамични неща, в момента не виждам проблема. Ще го разгледам по-отблизо по-късно и ще се опитам да създам работещ пример. - person kubal5003; 02.07.2010
comment
Съжалявам, беше малко неясно. Не е възможно да се използва Binding за предаване на динамичен ConverterParameter към Converter. - person David; 02.07.2010

Можете в кода отзад да итерирате всички елементи от списъка. За всеки елемент от списъка трябва да видите дали съдържа разширител. Ако го направи, просто го разгънете или свийте. Вижте тази връзка за това как да итерирате елементи и да намерите конкретна контрола.

Има ли начин за повторение в елементите на ListBox шаблони?

person Wallstreet Programmer    schedule 02.07.2010
comment
Най-лошото работещо решение, защото е общо решение, което може да се използва повторно? Най-лошото работещо решение, защото обработва свързаните с изгледа въпроси в изгледа, без да обърква вашия контролер с конкретен за изглед код? - person Wallstreet Programmer; 02.07.2010

Създайте свой собствен контрол - 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
}
person Kirill Chilingarashvili    schedule 08.09.2011