我的布局中有一个可调整大小的容器控件。它应该包含任意数量的用户控件(也可以调整大小,现在总是3 * 2):
我希望你有这个主意。我试图使用StackPanel但失败了。它并没有让我的元素集中在一起。
那么,如何使用XAML获得所需的布局?希望尽可能避免编码...
我结束的代码:
我相信我必须分享结果。我从这个答案WPF - How can I center all items in a WrapPanel?中获取了代码,并添加了调整大小的子控件。
private Size elementFinalSize = new Size(0, 0);
private Size MeasureDesiredSize(Size containerSize)
{
if (base.InternalChildren.Count == 0)
{
return new Size(0, 0);
}
// NB!: We assume all the items in the panel are of the same size.
var child = base.InternalChildren[0];
double elementAspectRatio = child.DesiredSize.Height == 0 ? 2.0 / 3.0 : child.DesiredSize.Width / child.DesiredSize.Height;
Size finalElementSize = new Size(0, 0);
Size newElementSize = finalElementSize;
for (int possibleRowsNumber = 1; ; possibleRowsNumber++)
{
int numberOfElementInRow = this.GetElementsNumberInRow(base.InternalChildren.Count, possibleRowsNumber);
double maxElementHeight = containerSize.Height / possibleRowsNumber;
double maxElementWidth = containerSize.Width / numberOfElementInRow;
if (maxElementWidth / elementAspectRatio > maxElementHeight)
{
// So many elements is more in width than container size, thus use Height.
newElementSize = new Size(maxElementHeight * elementAspectRatio, maxElementHeight);
}
else
{
// The element Height is greater than container row size, thus use Width.
newElementSize = new Size(maxElementWidth, maxElementWidth / elementAspectRatio);
}
if (newElementSize.Height > finalElementSize.Height)
{
// With such number of row a single element would be bigger than with previous number of rows.
finalElementSize = newElementSize;
}
else
{
// With such number of rows a single element is less than the previous biggest one, thus stop searching.
break;
}
}
return finalElementSize;
}
private int GetElementsNumberInRow(int elementsCount, int rowsNumber)
{
int x = elementsCount % rowsNumber;
if (x == 0)
{
return elementsCount / rowsNumber;
}
int maxPossibleElementsCount = elementsCount + rowsNumber - x;
return maxPossibleElementsCount / rowsNumber;
}
protected override Size MeasureOverride(Size constraint)
{
// This MeasureOverride functions is called the first of the three overridden.
// We need to understand the best (maximum) size for future element and arrange items accordingly.
// Drop the desired size to zero in order to recalculate it as soon as necessary.
this.elementFinalSize = new Size(0, 0);
Size curLineSize = new Size();
Size panelSize = new Size();
UIElementCollection children = base.InternalChildren;
for (int i = 0; i < children.Count; i++)
{
UIElement child = children[i] as UIElement;
// Flow passes its own constraint to children
if (this.elementFinalSize.Height == 0)
{
child.Measure(constraint);
this.elementFinalSize = this.MeasureDesiredSize(constraint);
child.InvalidateMeasure();
}
if (curLineSize.Width + this.elementFinalSize.Width > constraint.Width) //need to switch to another line
{
panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width);
panelSize.Height += curLineSize.Height;
curLineSize = this.elementFinalSize;
if (this.elementFinalSize.Width > constraint.Width) // if the element is wider then the constraint - give it a separate line
{
panelSize.Width = Math.Max(this.elementFinalSize.Width, panelSize.Width);
panelSize.Height += this.elementFinalSize.Height;
curLineSize = new Size();
}
}
else //continue to accumulate a line
{
curLineSize.Width += this.elementFinalSize.Width;
curLineSize.Height = Math.Max(this.elementFinalSize.Height, curLineSize.Height);
}
}
foreach (UIElement child in children)
{
child.Measure(this.elementFinalSize);
}
// the last line size, if any need to be added
panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width);
panelSize.Height += curLineSize.Height;
return panelSize;
}
protected override Size ArrangeOverride(Size arrangeBounds)
{
int firstInLine = 0;
Size curLineSize = new Size();
double accumulatedHeight = 0;
UIElementCollection children = this.InternalChildren;
for (int i = 0; i < children.Count; i++)
{
if (curLineSize.Width + this.elementFinalSize.Width > arrangeBounds.Width) //need to switch to another line
{
ArrangeLine(accumulatedHeight, curLineSize, arrangeBounds.Width, firstInLine, i);
accumulatedHeight += curLineSize.Height;
curLineSize = this.elementFinalSize;
if (this.elementFinalSize.Width > arrangeBounds.Width) //the element is wider then the constraint - give it a separate line
{
ArrangeLine(accumulatedHeight, this.elementFinalSize, arrangeBounds.Width, i, ++i);
accumulatedHeight += this.elementFinalSize.Height;
curLineSize = new Size();
}
firstInLine = i;
}
else //continue to accumulate a line
{
curLineSize.Width += this.elementFinalSize.Width;
curLineSize.Height = Math.Max(this.elementFinalSize.Height, curLineSize.Height);
}
}
if (firstInLine < children.Count)
ArrangeLine(accumulatedHeight, curLineSize, arrangeBounds.Width, firstInLine, children.Count);
return arrangeBounds;
}
private void ArrangeLine(double y, Size lineSize, double boundsWidth, int start, int end)
{
double x = 0;
if (this.HorizontalContentAlignment == HorizontalAlignment.Center)
{
x = (boundsWidth - lineSize.Width) / 2;
}
else if (this.HorizontalContentAlignment == HorizontalAlignment.Right)
{
x = (boundsWidth - lineSize.Width);
}
UIElementCollection children = InternalChildren;
for (int i = start; i < end; i++)
{
UIElement child = children[i];
child.Arrange(new Rect(x, y, this.elementFinalSize.Width, this.elementFinalSize.Height));
x += this.elementFinalSize.Width;
}
}
答案 0 :(得分:5)
以下将产生几乎相同的效果:
<Grid>
<WrapPanel HorizontalAlignment="Center" VerticalAlignment="Center" >
<Rectangle Stroke="Red" StrokeThickness="2" Width="200" Height="200"/>
<Rectangle Stroke="Red" StrokeThickness="2" Width="200" Height="200"/>
<Rectangle Stroke="Red" StrokeThickness="2" Width="200" Height="200"/>
</WrapPanel>
</Grid>
但是这些项目是左对齐的,因此在您的情况下,您的上一个屏幕截图会显示左下方对齐的底部矩形。要解决此问题,您可以create a custom implementation of WrapPanel just for that purpose
编辑:当然这只是一个带有矩形和所有的虚拟示例,通常会创建一个ListView / ListBox / ItemContainer(取决于场景)并使ItemsPanel成为WrapPanel:
<ListView>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel .../>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>