【问题标题】:Make WPF/SL grid ignore a child element when determining size在确定大小时使 WPF/SL 网格忽略子元素
【发布时间】:2011-06-18 18:03:31
【问题描述】:

我有一个 Grid,它有几个孩子,其中一个是 ScrollViewer。我希望 Grid 根据其所有子级除了 ScrollViewer 来调整自身的大小,我只想占用通过调整 Grid 为其其余子级大小而创建的空间。 (比如 Grid 是 2x2 并且 ScrollViewer 在第 0 行第 0 列,所以其他三个 Grid 条目足以确定 Grid 的尺寸。)

有什么好的方法可以做到这一点吗?我已经考虑创建一个自定义面板来替换网格或包含 ScrollViewer,但在 MeasureOverride/ArrangeOverride 调用期间,我从来没有接到过可以告诉我网格行的最终宽度/高度的电话/我关心的列(例如,第 0 行,第 0 列)。

我的一个想法是从 Grid 派生并调用基本 MeasureOverride/ArrangeOverride 但在调用之前从 Grid 的子项中删除 ScrollViewer(然后将其放回),但在布局计算期间弄乱了可视化树似乎是个坏主意。

这是一个例子:

<Grid x:Name="LayoutRoot" Background="White">
    <Grid VerticalAlignment="Top" HorizontalAlignment="Left">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <ScrollViewer Grid.Row="0" Grid.Column="0" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto">
            <Button Height="300" Width="300"/>
        </ScrollViewer>
        <Button Grid.Row="1" Grid.Column="0" Height="100" Width="100" Content="1,0"/>
        <Button Grid.Row="0" Grid.Column="1" Height="100" Width="100" Content="0,1"/>
        <Button Grid.Row="1" Grid.Column="1" Height="100" Width="100" Content="1,1"/>
    </Grid>
</Grid>

我希望 Grid 的大小不会随着 ScrollViewer 中 Button 的大小的变化而改变 - 例如,我希望 Grid 为 ScrollViewer 提供 100x100 的正方形,该正方形由 Grid 的其余内容决定。如果我将 3 个 100x100 按钮中的每一个更改为 200x200,我希望 ScrollViewer 获得 200x200 等。

到目前为止,Pavlo 的示例让我最接近,Grid 行/列的大小适当,但 ScrollViewer 不适应在调用 Arrange 时给它的 Size。见下文:

【问题讨论】:

  • 请提供一个 XAML 的例子,它很难理解你到底需要什么。
  • 当然,好点。我刚加了一个。

标签: wpf silverlight layout grid


【解决方案1】:

如果我正确理解了您想要什么,那么您可以执行以下技巧。创建一个装饰器,它会在 Measure 阶段要求 0 个空间,并在 Arrange 阶段用所有给定的空间排列子项:

public class NoSizeDecorator : Decorator
{
    protected override Size MeasureOverride(Size constraint) {
        // Ask for no space
        Child.Measure(new Size(0,0));
        return new Size(0, 0);
    }        
}

您的 XAML 将如下所示:

<Grid x:Name="LayoutRoot" Background="White">
    <Grid VerticalAlignment="Top" HorizontalAlignment="Left">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <my:NoSizeDecorator Grid.Row="0" Grid.Column="0">
            <ScrollViewer ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto">
                <Button Height="300" Width="300" Content="0,0"/>
            </ScrollViewer>
        </my:NoSizeDecorator>

        <Button Grid.Row="1" Grid.Column="0" Height="100" Width="100" Content="1,0"/>
        <Button Grid.Row="0" Grid.Column="1" Height="100" Width="100" Content="0,1"/>
        <Button Grid.Row="1" Grid.Column="1" Height="100" Width="100" Content="1,1"/>
    </Grid>
</Grid>

【讨论】:

  • 小问题:Silverlight 中没有装饰器类。例如,边框从 Silverlight 中的 FrameworkElement 派生。 WPF 添加了装饰器类。
  • 谢谢帕夫洛。这与我尝试的很接近,但肯定更接近我想要的结果。我继续尝试在 WPF 中尝试(认为我以后可以适应 SL),问题是,虽然现在 Grid 行和列的大小适当,但 ScrollViewer 及其内容仍然占用他们想要的空间,即使他们得到使用 100x100 边界的 ArrangeOverride 命中。我在上面添加了一张图片来说明我的意思。
  • 是的,有一个错误。我们也必须用 0 空间来测量孩子。更新了代码。我已经用 WPF 对其进行了测试,它似乎可以工作。
  • 谢谢帕夫洛!这在 WPF 和 SL 中都有效(替换面板并将代码应用于所有子代而不是一个子代)。但是,当我在启动所有这些的应用程序中使用它时,当我调整窗口大小时,我最终会遇到 LayoutCycleExceptions。我还不能在简单的按钮示例中重现这一点,但是当我在本周晚些时候有空闲时间时,我会看看我是否能深入了解它。只是想我会提一下,以防有人对此有想法。
  • 为什么需要 ArrangeOverride 方法?似乎所有的魔法都发生在 Measure 步骤中。
【解决方案2】:

Pavlo Glazkov 的回答非常适合我!我已经将它用于不想指定显式大小的控件...我添加了一些逻辑和属性来击败一个维度:

public sealed class NoSizeDecorator
            : Decorator
    {
        /// <summary>
        /// Sets whether the width will be overridden.
        /// </summary>
        public static readonly DependencyProperty DisableWidthOverrideProperty
                = DependencyProperty.Register(
                        nameof(NoSizeDecorator.DisableWidthOverride),
                        typeof(bool),
                        typeof(NoSizeDecorator),
                        new FrameworkPropertyMetadata(
                                false,
                                FrameworkPropertyMetadataOptions.AffectsMeasure));

        /// <summary>
        /// Sets whether the width will be overridden.
        /// </summary>
        public bool DisableWidthOverride
        {
            get => (bool)GetValue(NoSizeDecorator.DisableWidthOverrideProperty);
            set => SetValue(NoSizeDecorator.DisableWidthOverrideProperty, value);
        }

        /// <summary>
        /// Sets whether the height will be overridden.
        /// </summary>
        public static readonly DependencyProperty DisableHeightOverrideProperty
                = DependencyProperty.Register(
                        nameof(NoSizeDecorator.DisableHeightOverride),
                        typeof(bool),
                        typeof(NoSizeDecorator),
                        new FrameworkPropertyMetadata(
                                false,
                                FrameworkPropertyMetadataOptions.AffectsMeasure));

        /// <summary>
        /// Sets whether the height will be overridden.
        /// </summary>
        public bool DisableHeightOverride
        {
            get => (bool)GetValue(NoSizeDecorator.DisableHeightOverrideProperty);
            set => SetValue(NoSizeDecorator.DisableHeightOverrideProperty, value);
        }


        protected override Size MeasureOverride(Size constraint)
        {
            UIElement child = Child;
            if (child == null)
                return new Size();
            constraint
                    = new Size(
                            DisableWidthOverride
                                    ? constraint.Width
                                    : 0D,
                            DisableHeightOverride
                                    ? constraint.Height
                                    : 0D);
            child.Measure(constraint);
            return new Size(
                    DisableWidthOverride
                            ? child.DesiredSize.Width
                            : 0D,
                    DisableHeightOverride
                            ? child.DesiredSize.Height
                            : 0D);
        }
    }

【讨论】:

    猜你喜欢
    • 2017-02-11
    • 1970-01-01
    • 2016-07-16
    • 2017-01-24
    • 2013-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多