我的布局中有一个可调整大小的容器控件。它应该包含任意数量的我的用户控件(它们也可以调整大小,并且目前始终为 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;
}
}
最佳答案
以下将产生几乎相同的效果:
<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>
关于c# - 如何进行 XAML 布局 - 堆栈面板中的居中项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8047177/