网格中的扩展器

时间:2011-09-07 12:53:24

标签: wpf layout

毫无疑问,这将是直截了当的,但出于任何理由,我的思绪在其上留下了空白。

我有一个小的,不可调整大小的窗口(325x450),其中有3个扩展器,垂直堆叠。每个Expander都包含一个ItemsControl,它可能包含很多项目,因此需要滚动。

我似乎无法做到的是如何布置扩展器以便扩展以填充任何可用空间而无需将其他元素从屏幕上移开。我可以通过使用网格并将每个扩展器放在一个具有*高度的行中来实现我所追求的目标,但这意味着它们总是占据窗口的1/3,这会破坏扩展器的点:)

我正在努力实现的蹩脚图表:

enter image description here

2 个答案:

答案 0 :(得分:11)

此要求有点不合时宜,因为您希望ChildrenGrid的状态决定Height所在的RowDefinition
我真的很喜欢布局的想法,我不敢相信自己从未有过类似的要求.. :)

对于可重用的解决方案,我会使用Grid的附加行为 该行为将订阅附加事件Expander.ExpandedExpander.Collapsed,并在事件处理程序中,从RowDefinition获取正确的Grid.GetRow并相应地更新Height。它像这样工作

<Grid ex:GridExpanderSizeBehavior.SizeRowsToExpanderState="True">
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Expander Grid.Row="0" ... />
    <Expander Grid.Row="1" ... />
    <Expander Grid.Row="2" ... />
    <!-- ... -->
</Grid>

这是 GridExpanderSizeBehavior

public class GridExpanderSizeBehavior
{
    public static DependencyProperty SizeRowsToExpanderStateProperty =
        DependencyProperty.RegisterAttached("SizeRowsToExpanderState",
                                            typeof(bool),
                                            typeof(GridExpanderSizeBehavior),
                                            new FrameworkPropertyMetadata(false, SizeRowsToExpanderStateChanged));
    public static void SetSizeRowsToExpanderState(Grid grid, bool value)
    {
        grid.SetValue(SizeRowsToExpanderStateProperty, value);
    }
    private static void SizeRowsToExpanderStateChanged(object target, DependencyPropertyChangedEventArgs e)
    {
        Grid grid = target as Grid;
        if (grid != null)
        {
            if ((bool)e.NewValue == true)
            {
                grid.AddHandler(Expander.ExpandedEvent, new RoutedEventHandler(Expander_Expanded));
                grid.AddHandler(Expander.CollapsedEvent, new RoutedEventHandler(Expander_Collapsed));
            }
            else if ((bool)e.OldValue == true)
            {
                grid.RemoveHandler(Expander.ExpandedEvent, new RoutedEventHandler(Expander_Expanded));
                grid.RemoveHandler(Expander.CollapsedEvent, new RoutedEventHandler(Expander_Collapsed));
            }
        }
    }
    private static void Expander_Expanded(object sender, RoutedEventArgs e)
    {
        Grid grid = sender as Grid;
        Expander expander = e.OriginalSource as Expander;
        int row = Grid.GetRow(expander);
        if (row <= grid.RowDefinitions.Count)
        {
            grid.RowDefinitions[row].Height = new GridLength(1.0, GridUnitType.Star); 
        }
    }
    private static void Expander_Collapsed(object sender, RoutedEventArgs e)
    {
        Grid grid = sender as Grid;
        Expander expander = e.OriginalSource as Expander;
        int row = Grid.GetRow(expander);
        if (row <= grid.RowDefinitions.Count)
        {
            grid.RowDefinitions[row].Height = new GridLength(1.0, GridUnitType.Auto);
        }
    }
}

答案 1 :(得分:8)

如果您不介意一点代码隐藏,您可能会挂钩Expanded / Collapsed个事件,找到父Grid,获取RowDefinition对于扩展器,如果扩展,则将值设置为*,否则设置为Auto

例如,

Expander ex = sender as Expander;
Grid parent = FindAncestor<Grid>(ex);
int rowIndex = Grid.GetRow(ex);

if (parent.RowDefinitions.Count > rowIndex && rowIndex >= 0)
    parent.RowDefinitions[rowIndex].Height = 
        (ex.IsExpanded ? new GridLength(1, GridUnitType.Star) : GridLength.Auto);

FindAncestor方法定义如下:

public static T FindAncestor<T>(DependencyObject current)
where T : DependencyObject
{
    // Need this call to avoid returning current object if it is the 
    // same type as parent we are looking for
    current = VisualTreeHelper.GetParent(current);

    while (current != null)
    {
        if (current is T)
        {
            return (T)current;
        }
        current = VisualTreeHelper.GetParent(current);
    };
    return null;
}