【问题标题】:Horizontal accordion control?水平手风琴控制?
【发布时间】:2016-11-16 13:16:53
【问题描述】:

我想要一个行为如下的控件:

  • 像网格一样行事
  • 每个子控件都嵌入在水平扩展器中(其标题绑定到控件的 Tag 属性)
  • 每个扩展器都有自己的列定义
  • 一次只能扩展其中一个扩展器
  • 非扩展扩展器的 ColumnDefinition 的宽度设置为自动
  • 扩展扩展器的一个是 *(星号)

它必须使用这些精确的控件(网格/扩展器),而不是一些自定义的,所以我的应用程序的样式可以自动应用到它们。

我似乎找不到已经制作的东西,似乎不存在内置解决方案(如果只有“填充”StackPanel...),我能想出的唯一解决方案是自己制作网格实现,看起来……令人生畏。

有没有办法找到或实施这样的控制?

这就是我现在所拥有的。它不处理“单膨胀”或填充。我真的不知道 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>

【问题讨论】:

  • 您是使用 ItemsSource 来填充内容还是在普通的 StackPanel 中工作?
  • 我不明白你的问题@Joe;现在,我有一个没有 ItemsSource 的 ItemsControl,使用 StackPanel 作为它的 ControlTemplate,正如您在我的 XAML 中看到的那样。我正在寻找的解决方案应该处理普通控件或 ItemsSource。
  • 看我的回答。该解决方案仅适用于 Grid 容器,而不适用于带有 ItemsSource 的 ItemsControl,尽管它可能可以为此进行扩展。如果使用 ItemsSource 填充扩展器是一个关键要求,它可能并不理想。

标签: wpf xaml accordion horizontal-accordion


【解决方案1】:

我的第一个想法是使用 Behavior 执行这种操作。这是您可以添加到现有 XAML 控件的一些功能,可以为您提供一些额外的自定义。

我只查看了不使用 ItemsSource 的东西,因为我使用了带有 Columns 的 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 有任何更改,则必须添加一些逻辑来管理新/删除的子项!

【讨论】:

  • 这是我在发布此问题后提出的一种解决方案,但这不是我要寻找的解决方案,因为它缺少我在问题中列出的一些要求。我不想自己指明扩展器,即使没有,我也不想指明列定义。我希望容器与任意数量的子控件一起动态工作。
  • 查看编辑,与 ItemsSource 一起使用,并且应该也适用于普通网格(甚至不需要手动添加列)!
  • 这行得通,我也只需将 Expander 管理添加到行为中即可。谢谢!
  • 请注意,您应该使用 Grid.GetColum(expanderObject) 而不是 AssociatedObject.Children.IndexOf 来检索扩展器的列
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-19
相关资源
最近更新 更多