2

我有一个Grid1:3 比例的两行;第一行已MinHeight设置为非零值。当我将 aListBox放入第二行时,它的大小不受窗口边框的限制:

截屏

MinHeight如果应用(即窗口很小),则会出现问题。如果我用 a 替换有问题ListBoxButton,问题就会消失(按钮始终保持在窗口边框内)。

主窗口.xaml

<Window x:Class="WpfGridLayoutMinMax.MainWindow" x:Name="self"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Height="300" Width="300" MinHeight="200" MinWidth="200">
    <Control.Resources>
        <Style TargetType="ListBox">
            <Setter Property="Margin" Value="4"/>
            <Setter Property="ItemsSource" Value="{Binding Items, ElementName=self}"/>
        </Style>
    </Control.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="1*" MinHeight="100"/>
            <RowDefinition Height="3*"/>
        </Grid.RowDefinitions>
        <ListBox Grid.Row="0"/>
        <ListBox Grid.Row="1"/>
    </Grid>
</Window>

主窗口.xaml.cs

using System.Collections.Generic;
using System.Linq;

namespace WpfGridLayoutMinMax
{
    public partial class MainWindow
    {
        public List<int> Items { get; set; }

        public MainWindow ()
        {
            Items = Enumerable.Range(0, 20).ToList();
            InitializeComponent();
        }
    }
}

问题:是什么导致了这个问题?如何使ListBox停留在窗口内容区域内?

4

3 回答 3

1

每次内容太大时都会出现此问题(如果您将高度设置为大于剩余空间,则对于 Button 也是如此)。

不知道您是否喜欢我的解决方案,但我添加了一个额外的 Grid 来测量剩余空间。不幸的是,无法直接从第二个 RowDefinition 获取 ActualHeight(它没有可用值)。这就是为什么我又添加了一个控件(虚拟对象)。现在您可以限制ListBox.MaxHeight为 Dummy.ActualHeight 并且它保持在窗口内。

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="100" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Grid x:Name="Dummy" Grid.Row="1" />

    <Grid Grid.RowSpan="2">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*" MinHeight="100"/>
            <RowDefinition Height="3*" />
        </Grid.RowDefinitions>
        <ListBox Grid.Row="0" />
        <ListBox Grid.Row="1" MaxHeight="{Binding ActualHeight, ElementName=Dummy}" />
    </Grid>
</Grid>
于 2013-10-01T12:32:51.893 回答
0

如果您将两行设置为 1* 和 3*,并说 1* 最低为 100(px),那么第二行最低为 300(px)。有道理吗?所以如果你想保持这个比例,并且想保持第一行的最小高度,你可以将窗口的最小高度设置为 400(或更多一点)。

于 2013-10-01T11:12:22.180 回答
0

问题是由越野车引起的Grid。它MeasureOverride返回比约束更大的大小,即使没有理由这样做。

我已经实现ForceCellSizes了解决问题的附加属性。

public static class GridProps
{
    public static readonly DependencyProperty CalculateCellSizesProperty = DependencyProperty.RegisterAttached(
        "CalculateCellSizes", typeof(bool), typeof(GridProps),
        new PropertyMetadata(false, (o, a) => CalculateCellSizes_OnChanged((Grid)o, a)));
    public static readonly DependencyProperty ForceCellSizesProperty = DependencyProperty.RegisterAttached(
        "ForceCellSizes", typeof(bool), typeof(GridProps),
        new PropertyMetadata(false, (o, a) => ForceCellSizes_OnChanged((Grid)o, a)));
    private static readonly DependencyProperty DummyGridProperty = DependencyProperty.RegisterAttached(
        "DummyGrid", typeof(Grid), typeof(GridProps),
        new PropertyMetadata(null));
    private static readonly DependencyPropertyKey RowActualHeightPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
        "RowActualHeight", typeof(double), typeof(GridProps),
        new PropertyMetadata(0.0));
    public static readonly DependencyProperty RowActualHeightProperty = RowActualHeightPropertyKey.DependencyProperty;
    private static readonly DependencyPropertyKey ColumnActualWidthPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
        "ColumnActualWidth", typeof(double), typeof(GridProps),
        new PropertyMetadata(0.0));
    public static readonly DependencyProperty ColumnActualWidthProperty = ColumnActualWidthPropertyKey.DependencyProperty;

    public static bool GetCalculateCellSizes (Grid grid)
    {
        return (bool)grid.GetValue(CalculateCellSizesProperty);
    }

    public static void SetCalculateCellSizes (Grid grid, bool value)
    {
        grid.SetValue(CalculateCellSizesProperty, value);
    }

    public static bool GetForceCellSizes (Grid grid)
    {
        return (bool)grid.GetValue(ForceCellSizesProperty);
    }

    public static void SetForceCellSizes (Grid grid, bool value)
    {
        grid.SetValue(ForceCellSizesProperty, value);
    }

    private static Grid GetDummyGrid (Grid grid)
    {
        return (Grid)grid.GetValue(DummyGridProperty);
    }

    private static void SetDummyGrid (Grid grid, Grid value)
    {
        grid.SetValue(DummyGridProperty, value);
    }

    public static double GetRowActualHeight (RowDefinition row)
    {
        return (double)row.GetValue(RowActualHeightProperty);
    }

    private static void SetRowActualHeight (RowDefinition row, double value)
    {
        row.SetValue(RowActualHeightPropertyKey, value);
    }

    public static double GetColumnActualWidth (ColumnDefinition column)
    {
        return (double)column.GetValue(ColumnActualWidthProperty);
    }

    private static void SetColumnActualWidth (ColumnDefinition column, double value)
    {
        column.SetValue(ColumnActualWidthPropertyKey, value);
    }

    private static void CalculateCellSizes_OnChanged (Grid grid, DependencyPropertyChangedEventArgs args)
    {
        if ((bool)args.NewValue)
            grid.SizeChanged += Grid_OnSizeChanged;
        else
            grid.SizeChanged -= Grid_OnSizeChanged;
    }

    private static void Grid_OnSizeChanged (object sender, SizeChangedEventArgs args)
    {
        var grid = (Grid)sender;
        foreach (RowDefinition row in grid.RowDefinitions)
            SetRowActualHeight(row, row.ActualHeight);
        foreach (ColumnDefinition column in grid.ColumnDefinitions)
            SetColumnActualWidth(column, column.ActualWidth);
    }

    private static void ForceCellSizes_OnChanged (Grid grid, DependencyPropertyChangedEventArgs args)
    {
        if ((bool)args.NewValue) {
            Action initDummyGrid = () => {
                Grid parentGrid = (Grid)grid.Parent, dummyGrid = CreateDummyGrid(grid);
                parentGrid.Children.Add(dummyGrid);
                SetDummyGrid(grid, dummyGrid);
            };
            if (grid.IsLoaded)
                initDummyGrid();
            else
                grid.Loaded += (o, e) => initDummyGrid();
        }
        else {
            Grid parentGrid = (Grid)grid.Parent, dummyGrid = DestroyDummyGrid(grid);
            parentGrid.Children.Remove(dummyGrid);
            SetDummyGrid(grid, null);
        }
    }

    private static Grid CreateDummyGrid (Grid grid)
    {
        var dummyGrid = new Grid { Visibility = Visibility.Hidden };
        SetCalculateCellSizes(dummyGrid, true);
        foreach (RowDefinition row in grid.RowDefinitions) {
            var dummyRow = new RowDefinition { Height = row.Height, MinHeight = row.MinHeight, MaxHeight = row.MaxHeight };
            dummyGrid.RowDefinitions.Add(dummyRow);
            BindingOperations.SetBinding(row, RowDefinition.HeightProperty,
                new Binding { Source = dummyRow, Path = new PropertyPath(RowActualHeightProperty) });
        }
        foreach (ColumnDefinition column in grid.ColumnDefinitions) {
            var dummyColumn = new ColumnDefinition { Width = column.Width, MinWidth = column.MinWidth, MaxWidth = column.MaxWidth };
            dummyGrid.ColumnDefinitions.Add(dummyColumn);
            BindingOperations.SetBinding(column, ColumnDefinition.WidthProperty,
                new Binding { Source = dummyColumn, Path = new PropertyPath(ColumnActualWidthProperty) });
        }
        return dummyGrid;
    }

    private static Grid DestroyDummyGrid (Grid grid)
    {
        Grid dummyGrid = GetDummyGrid(grid);
        SetCalculateCellSizes(dummyGrid, false);
        foreach (RowDefinition row in grid.RowDefinitions)
            BindingOperations.ClearBinding(row, RowDefinition.HeightProperty);
        foreach (ColumnDefinition column in grid.ColumnDefinitions)
            BindingOperations.ClearBinding(column, ColumnDefinition.WidthProperty);
        return dummyGrid;
    }
}

附加属性

类中定义了以下附加属性GridProps

  1. Grid.CalculateCellSizes (读/写) ——分别为网格添加可绑定属性RowActualHeight和属性。ColumnActualWidthRowDefinitionsColumnDefinitions

  2. Grid.ForceCellSizes (读/写) — 修复问题中描述的问题。

  3. RowDefinition.RowActualHeight (只读) - 可绑定RowDefinition.ActualHeight属性。CalculateCellSizes在所有者网格上设置为true.

  4. ColumnDefinition.ColumnActualWidth (只读) - 可绑定ColumnDefinition.ActualWidth属性。CalculateCellSizes在所有者网格上设置为true.

如何使用

  1. 将问题包装Grid在一个空的Grid.

  2. 设置GridProps.ForceCellSizestrue。问题的示例变为:

    <Grid>
        <Grid local:GridProps.ForceCellSizes="True">
            <Grid.RowDefinitions>
                <RowDefinition Height="1*" MinHeight="100"/>
                <RowDefinition Height="3*"/>
            </Grid.RowDefinitions>
            <ListBox Grid.Row="0"/>
            <ListBox Grid.Row="1"/>
        </Grid>
    </Grid>
    

这个怎么运作

它添加了一个与原始网格具有相同行和列的空虚拟网格,然后将原始网格的高度和宽度绑定到虚拟网格的实际高度和宽度。

本质上,上面的例子变成了:

<Grid>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="{Binding RowDefinitions[0].(local:GridProps.RowActualHeight), ElementName=dummyGrid}" MinHeight="100"/>
            <RowDefinition Height="{Binding RowDefinitions[1].(local:GridProps.RowActualHeight), ElementName=dummyGrid}"/>
        </Grid.RowDefinitions>
        <ListBox Grid.Row="0"/>
        <ListBox Grid.Row="1"/>
    </Grid>
    <Grid x:Name="dummyGrid" Visibility="Hidden" local:GridProps.CalculateCellSizes="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*" MinHeight="100"/>
            <RowDefinition Height="3*"/>
        </Grid.RowDefinitions>
    </Grid>
</Grid>
于 2013-10-01T15:36:53.450 回答