Ответ 1
Вы можете написать свой собственный класс Panel. В Интернете есть несколько учебных пособий, просто "пользовательская панель wpf".
Это сводится к переопределению методов MeasureOverride и ArrangeOverride.
public class CustomPanel : Panel
{
protected override Size MeasureOverride(Size availableSize)
{
Size panelDesiredSize = new Size();
foreach (UIElement child in InternalChildren)
{
child.Measure(availableSize);
// Use child.DesiredSize, availableSize.Width and the positions
// and sizes of the previous children to calculate the position of
// the current child. Then determine the resulting Panel height.
panelDesiredSize = ...
}
return panelDesiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
foreach (UIElement child in InternalChildren)
{
// Arrange each child according to the position calculations
// done in MeasureOverride
Point position = ...
child.Arrange(new Rect(position, child.DesiredSize));
}
return finalSize;
}
}
UPDATE. Следующая простая пользовательская панель может более или менее делать то, что вы хотите.
public class PackPanel : Panel
{
protected override Size MeasureOverride(Size availableSize)
{
foreach (UIElement child in InternalChildren)
{
child.Measure(availableSize);
}
var positions = new Point[InternalChildren.Count];
var desiredHeight = ArrangeChildren(positions, availableSize.Width);
return new Size(availableSize.Width, desiredHeight);
}
protected override Size ArrangeOverride(Size finalSize)
{
var positions = new Point[InternalChildren.Count];
ArrangeChildren(positions, finalSize.Width);
for (int i = 0; i < InternalChildren.Count; i++)
{
var child = InternalChildren[i];
child.Arrange(new Rect(positions[i], child.DesiredSize));
}
return finalSize;
}
private double ArrangeChildren(Point[] positions, double availableWidth)
{
var lastRowStartIndex = -1;
var lastRowEndIndex = 0;
var currentWidth = 0d;
var desiredHeight = 0d;
for (int childIndex = 0; childIndex < InternalChildren.Count; childIndex++)
{
var child = InternalChildren[childIndex];
var x = 0d;
var y = 0d;
if (currentWidth == 0d || currentWidth + child.DesiredSize.Width <= availableWidth)
{
x = currentWidth;
currentWidth += child.DesiredSize.Width;
}
else
{
currentWidth = child.DesiredSize.Width;
lastRowStartIndex = lastRowEndIndex;
lastRowEndIndex = childIndex;
}
if (lastRowStartIndex >= 0)
{
int i = lastRowStartIndex;
while (i < lastRowEndIndex - 1 && positions[i + 1].X < x)
{
i++;
}
while (i < lastRowEndIndex && positions[i].X < x + child.DesiredSize.Width)
{
y = Math.Max(y, positions[i].Y + InternalChildren[i].DesiredSize.Height);
i++;
}
}
positions[childIndex] = new Point(x, y);
desiredHeight = Math.Max(desiredHeight, y + child.DesiredSize.Height);
}
return desiredHeight;
}
}