防止RowHeight =自动拉伸我的网格? [英] Prevent RowHeight = Auto from Stretching my 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屋!