Горизонтальное управление аккордеоном?
Я хочу элемент управления, поведение которого выглядит следующим образом:
- Действовать как сетка
- Каждый дочерний элемент управления встроен в горизонтальный Expander (заголовок которого связан со свойством Tag элемента управления)
- Каждый из этих Expander имеет свое собственное ColumnDefinition
- Только один из этих расширителей может быть расширен за один раз
- ColumnDefinition нерасширенных расширителей имеет ширину, установленную на Auto
- Один из расширенных расширителей - * (Звезда)
Он должен использовать именно эти элементы управления (Grid/Expander), а не некоторые пользовательские, поэтому стиль моего приложения может автоматически применяться к ним.
Кажется, я не могу найти что-то уже сделанное, кажется, что не существует встроенного решения (если бы только была "заполняющая" StackPanel...), и единственное решение, которое я могу придумать, - это сделать мою собственную реализацию Grid, что кажется... пугающим
Есть ли решение, чтобы найти или реализовать такой контроль?
Вот что у меня сейчас. Он не обрабатывает ни "однораспакованный", ни наполнитель. Я действительно не знаю, виноваты ли в этом StackPanel или Expander.
<ItemsControl>
<ItemsControl.Resources>
<DataTemplate x:Key="verticalHeader">
<ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type Expander}}, Path=Header}" />
</DataTemplate>
<Style TargetType="{x:Type Expander}"
BasedOn="{StaticResource {x:Type Expander}}">
<Setter Property="HeaderTemplate"
Value="{StaticResource verticalHeader}" />
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="ExpandDirection"
Value="Right" />
</Style>
</ItemsControl.Resources>
<ItemsControl.Template>
<ControlTemplate>
<!-- Damn you, StackPanel! -->
<StackPanel Orientation="Horizontal" IsItemsHost="True"/>
</ControlTemplate>
</ItemsControl.Template>
<Expander Header="Exp1">
<TextBlock Text="111111111" Background="Red"/>
</Expander>
<Expander Header="Exp2">
<TextBlock Text="222222222" Background="Blue"/>
</Expander>
<Expander Header="Exp3">
<TextBlock Text="333333333" Background="Green"/>
</Expander>
</ItemsControl>
1 ответ
Моя первая мысль - выполнить такое действие с поведением. Это некоторые функциональные возможности, которые вы можете добавить к существующим элементам управления XAML, что дает вам некоторые дополнительные настройки.
Я смотрел на это только для чего-то, что не использует ItemsSource, так как я использовал Grid со столбцами и т. Д. Но в простой сетке вы можете добавить поведение, которое прослушивает его события Expanded и Collapsed для детей, например:
public class ExpanderBehavior : Behavior<Grid>
{
private List<Expander> childExpanders = new List<Expander>();
protected override void OnAttached()
{
//since we are accessing it's children, we have to wait until initialise is complete for it's children to be added
AssociatedObject.Initialized += (gridOvject, e) =>
{
foreach (Expander expander in AssociatedObject.Children)
{
//store this so we can quickly contract other expanders (though we could just access Children again)
childExpanders.Add(expander);
//track expanded events
expander.Expanded += (expanderObject, e2) =>
{
//contract all other expanders
foreach (Expander otherExpander in childExpanders)
{
if (expander != otherExpander && otherExpander.IsExpanded)
{
otherExpander.IsExpanded = false;
}
}
//set width to star for the correct column
int index = Grid.GetColum(expanderObject as Expander);
AssociatedObject.ColumnDefinitions[index].Width = new GridLength(1, GridUnitType.Star);
};
//track Collapsed events
expander.Collapsed += (o2, e2) =>
{
//reset all to auto
foreach (ColumnDefinition colDef in AssociatedObject.ColumnDefinitions)
{
colDef.Width = GridLength.Auto;
}
};
}
};
}
}
Используйте это следующим образом, обратите внимание, что вы должны добавить System.Windows.Interactivity в качестве ссылки на ваш проект:
<Window ...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:local="...">
<Window.Resources>
<DataTemplate x:Key="verticalHeader">
<ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type Expander}}, Path=Header}" />
</DataTemplate>
<Style TargetType="{x:Type Expander}"
BasedOn="{StaticResource {x:Type Expander}}">
<Setter Property="HeaderTemplate"
Value="{StaticResource verticalHeader}" />
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="ExpandDirection"
Value="Right" />
</Style>
<local:ExpanderBehavior x:Key="ExpanderBehavor"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<i:Interaction.Behaviors>
<local:ExpanderBehavior/>
</i:Interaction.Behaviors>
<Expander Header="Exp1">
<TextBlock Text="111111111" Background="Red"/>
</Expander>
<Expander Header="Exp2" Grid.Column="1">
<TextBlock Text="222222222" Background="Blue"/>
</Expander>
<Expander Header="Exp3" Grid.Column="2">
<TextBlock Text="333333333" Background="Green"/>
</Expander>
</Grid>
</Window>
Конечный результат:
Изменить: Работа с ItemsControl - добавьте его в сетку, в которой размещены элементы, и добавьте немного для управления отображением столбцов
public class ItemsSourceExpanderBehavior : Behavior<Grid>
{
private List<Expander> childExpanders = new List<Expander>();
protected override void OnAttached()
{
AssociatedObject.Initialized += (gridOvject, e) =>
{
//since we are accessing it's children, we have to wait until initialise is complete for it's children to be added
for (int i = 0; i < AssociatedObject.Children.Count; i++)
{
Expander expander = AssociatedObject.Children[i] as Expander;
//sort out the grid columns
AssociatedObject.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
Grid.SetColumn(expander, i);
childExpanders.Add(expander);
//track expanded events
expander.Expanded += (expanderObject, e2) =>
{
foreach (Expander otherExpander in childExpanders)
{
if (expander != otherExpander && otherExpander.IsExpanded)
{
otherExpander.IsExpanded = false;
}
}
//set width to auto
int index = AssociatedObject.Children.IndexOf(expanderObject as Expander);
AssociatedObject.ColumnDefinitions[index].Width = new GridLength(1, GridUnitType.Star);
};
//track Collapsed events
expander.Collapsed += (o2, e2) =>
{
foreach (ColumnDefinition colDef in AssociatedObject.ColumnDefinitions)
{
colDef.Width = GridLength.Auto;
}
};
}
};
}
}
Используемый:
<ItemsControl>
<ItemsControl.Template>
<ControlTemplate>
<Grid IsItemsHost="True">
<i:Interaction.Behaviors>
<local:ItemsSourceExpanderBehavior/>
</i:Interaction.Behaviors>
</Grid>
</ControlTemplate>
</ItemsControl.Template>
<Expander Header="Exp1">
<TextBlock Text="111111111" Background="Red"/>
</Expander>
<Expander Header="Exp2">
<TextBlock Text="222222222" Background="Blue"/>
</Expander>
<Expander Header="Exp3">
<TextBlock Text="333333333" Background="Green"/>
</Expander>
</ItemsControl>
Обратите внимание, что вам придется добавить некоторую логику для управления новыми / удаленными дочерними элементами, если у вас есть какие-либо изменения в вашем ItemsSource!