防止RowHeight =自动拉伸我的网格? [英] Prevent RowHeight = Auto from Stretching my Grid?

查看:107
本文介绍了防止RowHeight =自动拉伸我的网格?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的场景中,最终用户通过将用户界面拆分成行并定义这些行的高度规则(固定,填充空间,适合内容)来定制他或她的用户界面。我使用WPF Grid来实现它。



网格开始填充整个屏幕,不应该变得更大 - 用户必须能够看到整个网格(滚动条WITHIN行是好的,但不是整个网格的滚动条)。



我的问题的症结所在:当用户创建一个或多个自动这些行中的内容可以强制扩展整个网格的大小,即使当我将网格的最大高度设置为固定数字时,也会引入滚动条。



当涉及一个星形大小的行时,情况会变得更糟,因为一旦网格被拉伸一点,那个星形大小的行就会填充可用空间,所以即使稍后自动调整大小的行,网格也会被永久拉伸缩小。

我需要找到一种方法来限制auto行,以便它们根据需要展开和缩小,但是所有行的总实际高度永远不会大于整个电网。



为了说明,我有这个网格w固定的最大高度,以及代表所有尺寸模式的行。

 < Grid.RowDefinitions> 
< RowDefinition Height =Auto/>
< RowDefinition Height =Auto/>
< RowDefinition Height =200/>
< RowDefinition Height =*/>
< /Grid.RowDefinitions>

< ScrollViewer VerticalScrollBarVisibility =Auto>
< TextBlock>
abc< LineBreak />
abc< LineBreak />
abc< LineBreak />
abc< LineBreak />
abc< LineBreak />
< / TextBlock>
< / ScrollViewer>



在这个例子中, abc文本块扩展后,网格的总高度延伸超过固定的300最大高度。我怎样才能防止这种行为,以保证网格的最大高度,同时保持自动尺寸行的灵活性? 解决方案

好吧,我发现我必须对Grid进行子类化,以便覆盖Measure()和Arrange()布局步骤。

我不认为这是一个伟大的通用解决方案,但它适用于我的方案。特别要注意的是,我没有处理列,因为在我的情况下,只有一列。我也没有在单元格中定位元素(我将它们留在左上角)。



如果您需要更通用的解决方案,我认为这是一个非常好的开始。

  class NoStretchGrid:Grid 
{
//这个覆盖决定了我们要求的大小
//确保我们永远不会要求超过最大高度
保护覆盖System.Windows.Size MeasureOverride(System.Windows。大小约束)
{
//基本Grid将做什么?
System.Windows.Size desiredSize = base.MeasureOverride(constraint);

if(desiredSize.Height> constraint.Height)
desiredSize.Height = constraint.Height;

//如果定义了最大高度并且期望高度过大,则将其减小
if(this.MaxHeight!= double.NaN&& desiredSize.Height> this.MaxHeight )
{
desiredSize.Height = this.MaxHeight;
}

return desiredSize;
}

//这个覆盖告诉子控件它们可以有多大以及它们的位置
保护覆盖System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize )
{
//必须决定每行的高度是多少
double [] desiredHeights = new double [this.RowDefinitions.Count];
double [] minimumHeights = new double [this.RowDefinitions.Count];
double [] finalHeights = new double [this.RowDefinitions.Count];

//首先,找出每行想要的高度是多少

//检查固定大小的行
for(int i = 0; i< ; desiredHeights.Length; i ++)
{
if(this.RowDefinitions [i] .Height.IsAbsolute)
{
desiredHeights [i] = this.RowDefinitions [i]。 Height.Value;
}
else
{
desiredHeights [i] = 0;
}

minimumHeights [i] = this.RowDefinitions [i] .MinHeight;
}

//然后询问孩子他们想要多大
foreach(UIElement child in this.InternalChildren)
{
int row = Grid .GetRow(孩子);
if(!this.RowDefinitions [row] .Height.IsAbsolute&& child.DesiredSize.Height> desiredHeights [row])
{
desiredHeights [row] = child.DesiredSize 。高度; (作为FrameworkElement的子元素).MinHeight> minimumHeights [row])
{
minimumHeights [row] =(子元素为FrameworkElement).MinHeight(


;
}
}

double availableHeight = arrangeSize.Height;

//保留最小高度
(int i = 0; i< minimumHeights.Length; i ++)
{
finalHeights [i] = minimumHeights [i ]。
availableHeight - = finalHeights [i];
}

//允许固定高度的行的高度 - 如果一些无知使固定高度太大,我们不能帮助他
for(int i = 0; i< desiredHeights.Length; i ++)
{
if(this.RowDefinitions [i] .Height.IsAbsolute)
{
finalHeights [i] = this.RowDefinitions [i ] .Height.Value;
availableHeight = availableHeight + minimumHeights [i] - finalHeights [i];
}
}

//允许自动调整大小的行的期望高度,只要有高度剩下
for(int i = 0; i < desiredHeights.Length; i ++)
{
if(this.RowDefinitions [i] .Height.IsAuto)
{
double desiredHeightIncrease = desiredHeights [i] - minimumHeights [i ]。

if(desiredHeightIncrease <= availableHeight)
{
finalHeights [i] + = desiredHeightIncrease;
availableHeight - = desiredHeightIncrease;
}
else
{
finalHeights [i] = minimumHeights [i] + availableHeight;
availableHeight = 0;
}
}
}

//现在可以防止自动大小的行失控,使任何星形行的最小高度(int i = 0; i< desiredHeights.Length; i ++)
{
if(this.RowDefinitions [i] .Height.IsStar)
{
availableHeight + = minimumHeights [i];
}
}

//在星形行之间按比例分配剩余的可用高度,剩下的高度为
double totalStarValues = 0;
for(int i = 0; i< desiredHeights.Length; i ++)
{
if(this.RowDefinitions [i] .Height.IsStar)
{
totalStarValues + = this.RowDefinitions [i] .Height.Value; (int i = 0; i< desiredHeights.Length; i ++)
{
}

if(this.RowDefinitions [i ] .Height.IsStar)
{
finalHeights [i] = availableHeight *(this.RowDefinitions [i] .Height.Value / totalStarValues);
}
}

//决定每一行的垂直位置
double [] rowPositions = new double [desiredHeights.Length];
rowPositions [0] = 0;
for(int i = 1; i< rowPositions.Length; i ++)
{
rowPositions [i] = rowPositions [i - 1] + finalHeights [i - 1];
}

//根据这些结果告诉孩子们自己出来
foreach(UIElement child in this.InternalChildren)
{
int row = Grid.GetRow(孩子);

// scrollviewer的特殊情况,它不适合自己的尺寸
if(child是ScrollViewer)
{
ScrollViewer scrollViewer = child as ScrollViewer;

//暂时更新其高度值,JUST为Arrange()调用
double oldHeight = scrollViewer.Height;
scrollViewer.Height = finalHeights [row];
child.Arrange(new Rect(0,rowPositions [row],arrangeSize.Width,finalHeights [row]));

//恢复原始值
scrollViewer.Height = oldHeight;
}

//非滚动查看器的典型情况
else
{
child.Arrange(new Rect(0,rowPositions [row] ,arrangeSize.Width,finalHeights [row]));
}
}

return arrangeSize;
}
}

这是一个测试用例。

 < local:NoStretchGrid VerticalAlignment =Stretch> 

< Grid.RowDefinitions>
< RowDefinition Height =Auto/>
< RowDefinition Height =Auto/>
< RowDefinition Height =2 */>
< RowDefinition Height =*/>
< RowDefinition Height =50/>
< /Grid.RowDefinitions>

< ScrollViewer VerticalScrollBarVisibility =VisibleMinHeight =50>
< Rectangle Fill =OrangeHeight =250/>
< / ScrollViewer>

< ScrollViewer VerticalScrollBarVisibility =VisibleGrid.Row =1MinHeight =50>
< Rectangle Fill =BlueHeight =200/>
< / ScrollViewer>

< Grid Background =PinkGrid.Row =2MinHeight =30/>
< Grid Background =GreenGrid.Row =3MinHeight =30/>
< Grid Background =RedGrid.Row =4/>

< / local:NoStretchGrid>


In my scenario, the end user customizes his or her user interface by breaking it into rows and defining the height rule for those rows (fixed, fill space, fit content). I implement this using the WPF Grid.

The Grid starts filling the entire screen, and should not get any bigger - the user MUST be able to see the entire Grid at all times (scroll bars WITHIN rows are okay, but not scroll bars for the entire grid).

The crux of my problem: When the user creates one or more "auto" sized rows, the content in those rows can force the size of the entire grid to expand, introducing scroll bars, even when I've set the max height of the grid to a fixed number.

It gets worse when a star-sized row is involved, because once the grid is stretched a little, that star-sized row fills the available space, so the grid is PERMANENTLY stretched even when the auto-size row later shrinks.

I need to find a way to restrict "auto" rows so that they expand and shrink as needed, BUT the total actual height of all rows is never larger than the entire grid.

To illustrate, I have this grid with fixed max height, and rows representing all size modes.

<Grid.RowDefinitions>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="200"/>
    <RowDefinition Height="*"/>
</Grid.RowDefinitions>

<ScrollViewer VerticalScrollBarVisibility="Auto">
    <TextBlock>
        abc<LineBreak/>
        abc<LineBreak/>
        abc<LineBreak/>
        abc<LineBreak/>
        abc<LineBreak/>                    
    </TextBlock>
</ScrollViewer>

In this example, as the "abc" text block expands, the total height of the grid stretches beyond the fixed "300" maximum height. How can I prevent this behavior to guarantee the maximum height of the grid, while keeping the flexibility of the auto-size rows?

解决方案

Okay, I discovered that I had to subclass Grid so that I could override Measure() and Arrange() layout steps.

I don't claim that this is a great general purpose solution, but it works for my scenario. Note especially that I'm not dealing with columns, since in my case, there's only one column. I'm also not positioning elements within the cells (I'm leaving them anchored at top-left).

If you need a more general solution, I think this is a very good start. The column problem is the same as the row problem, just in the other direction.

class NoStretchGrid:Grid
{
    //this override determines what size we ask to be
    //gotta make sure we never ask for more than the max height
    protected override System.Windows.Size MeasureOverride(System.Windows.Size constraint)
    {
        //what would a basic Grid do?
        System.Windows.Size desiredSize = base.MeasureOverride(constraint);

        if (desiredSize.Height > constraint.Height)
            desiredSize.Height = constraint.Height;

        //if max height is defined and desired height is too big, reduce it
        if (this.MaxHeight != double.NaN && desiredSize.Height > this.MaxHeight)
        {
            desiredSize.Height = this.MaxHeight;
        }

        return desiredSize;
    }

    //this override tells child controls how big they can be and where they're positioned
    protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize)
    {
        //must decide how tall each row will be
        double[] desiredHeights = new double[this.RowDefinitions.Count];
        double[] minimumHeights = new double[this.RowDefinitions.Count];
        double[] finalHeights = new double[this.RowDefinitions.Count];

        //first, find out how tall each row wants to be

        //check for fixed-size rows
        for (int i = 0; i < desiredHeights.Length; i++)
        {
            if (this.RowDefinitions[i].Height.IsAbsolute)
            {
                desiredHeights[i] = this.RowDefinitions[i].Height.Value;
            }
            else
            {
                desiredHeights[i] = 0;
            }

            minimumHeights[i] = this.RowDefinitions[i].MinHeight;
        }

        //then ask children how big they want to be
        foreach (UIElement child in this.InternalChildren)
        {
            int row = Grid.GetRow(child);
            if (!this.RowDefinitions[row].Height.IsAbsolute && child.DesiredSize.Height > desiredHeights[row])
            {
                desiredHeights[row] = child.DesiredSize.Height;
            }

            if ((child as FrameworkElement).MinHeight > minimumHeights[row])
            {
                minimumHeights[row] = (child as FrameworkElement).MinHeight;
            }
        }

        double availableHeight = arrangeSize.Height;

        //reserve minimum heights
        for (int i = 0; i < minimumHeights.Length; i++)
        {
            finalHeights[i] = minimumHeights[i];
            availableHeight -= finalHeights[i];
        }            

        //allow fixed-height rows their height - if some ignoramus made fixed-heights too big, we can't help him
        for (int i = 0; i < desiredHeights.Length; i++)
        {
            if (this.RowDefinitions[i].Height.IsAbsolute)
            {
                finalHeights[i] = this.RowDefinitions[i].Height.Value;
                availableHeight = availableHeight + minimumHeights[i] - finalHeights[i];
            }
        }

        //allow auto-size rows their desired heights, so long as there's height left to be had
        for (int i = 0; i < desiredHeights.Length; i++)
        {                
            if (this.RowDefinitions[i].Height.IsAuto)
            {
                double desiredHeightIncrease = desiredHeights[i] - minimumHeights[i];

                if (desiredHeightIncrease <= availableHeight)
                {
                    finalHeights[i] += desiredHeightIncrease;
                    availableHeight -= desiredHeightIncrease;
                }
                else
                {
                    finalHeights[i] = minimumHeights[i] + availableHeight;
                    availableHeight = 0;
                }
            }
        }

        //now that auto-size rows have been prevented from getting out of control, make the min heights of any star-size rows available again
        for (int i = 0; i < desiredHeights.Length; i++)
        {
            if (this.RowDefinitions[i].Height.IsStar)
            {
                availableHeight += minimumHeights[i];
            }
        }

        //divide any leftover available height proportionally amongst the star-sized rows, while there's height left to be had
        double totalStarValues = 0;
        for (int i = 0; i < desiredHeights.Length; i++)
        {
            if (this.RowDefinitions[i].Height.IsStar)
            {
                totalStarValues += this.RowDefinitions[i].Height.Value;
            }
        }

        for (int i = 0; i < desiredHeights.Length; i++)
        {
            if (this.RowDefinitions[i].Height.IsStar)
            {
                finalHeights[i] = availableHeight * (this.RowDefinitions[i].Height.Value / totalStarValues);
            }
        }

        //decide the vertical position of each row
        double[] rowPositions = new double[desiredHeights.Length];
        rowPositions[0] = 0;
        for (int i = 1; i < rowPositions.Length; i++)
        {
            rowPositions[i] = rowPositions[i - 1] + finalHeights[i - 1];
        }

        //tell children to lay themselves out based on these results
        foreach (UIElement child in this.InternalChildren)
        {
            int row = Grid.GetRow(child);

            //special case for scrollviewer, which doesn't size itself appropriately
            if (child is ScrollViewer)
            {
                ScrollViewer scrollViewer = child as ScrollViewer;

                //temporarily update its height value, JUST for the Arrange() call
                double oldHeight = scrollViewer.Height;
                scrollViewer.Height = finalHeights[row];
                child.Arrange(new Rect(0, rowPositions[row], arrangeSize.Width, finalHeights[row]));

                //restore the original value
                scrollViewer.Height = oldHeight;
            }

            //typical case for non-scroll-viewers
            else
            {
                child.Arrange(new Rect(0, rowPositions[row], arrangeSize.Width, finalHeights[row]));
            }
        }

        return arrangeSize;
    }
}

Here's a test case. Drop this in a Window and resize the window to see it working.

<local:NoStretchGrid VerticalAlignment="Stretch">

    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="2*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="50"/>
    </Grid.RowDefinitions>

    <ScrollViewer VerticalScrollBarVisibility="Visible" MinHeight="50">
        <Rectangle Fill="Orange" Height="250"/>
    </ScrollViewer>

    <ScrollViewer VerticalScrollBarVisibility="Visible" Grid.Row="1" MinHeight="50">
        <Rectangle Fill="Blue" Height="200"/>
    </ScrollViewer>

    <Grid Background="Pink" Grid.Row="2" MinHeight="30"/>
    <Grid Background="Green" Grid.Row="3" MinHeight="30"/>
    <Grid Background="Red" Grid.Row="4"/>

</local:NoStretchGrid>

这篇关于防止RowHeight =自动拉伸我的网格?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆