我正在尝试创建一个WrapPanel
,以便在有空间时水平扩展其子级。我使用了此处提供的解决方案:How to make WPF wrappanel child items to stretch?,尽管它不起作用,所以我想出了类似的解决方案,只是遇到了相同的效果。最后,我尝试了以下操作:A custom auto-sizing WPF Panel class,但是两步法甚至失败了。我面板的子级是3个StackPanel
,每个子级都有MinWidth和MaxWidth的两倍,但是没有它们,就没有变化。定制面板为这些面板安排了适当的值,但是奇怪的是,只有第一个面板才接受这些值(例如:面板的desireWidth是440,提供的宽度是500,它增长到了大约470),还有两个其他人仅返回其先前的DesiredSize。然后再次调用安排,情况重复,直到第一个面板占用了所有空白空间。奇怪的是,当我将窗口最小化以使最后一块面板包裹到下一行时,它会适当地增长和居中,两个上部面板也是如此,尽管第一个面板仍然更大。我不知道这是怎么回事。我尝试返回Size(0,0)和/或将Double.PositiveInfinity作为measureOverride中的宽度传递,而没有任何效果。
这是我当前的解决方案:
protected override Size MeasureOverride(Size constraint)
{
float lineU = 0;
float lineV = 0;
float panelU = 0;
float panelV = 0;
float constraintWidth = (float)constraint.Width;
var children = base.InternalChildren;
for (int i = 0; i < children.Count; i++)
{
var child = children[i];
child.Measure(constraint);
var desiredSize = child.DesiredSize;
var itemWidth = (float)desiredSize.Width;
var itemHeight = (float)desiredSize.Height;
if (lineU + itemWidth > constraintWidth - 10 * float.Epsilon)
{
panelU = Math.Max(lineU, panelU);
panelV += lineV;
lineU = itemWidth;
lineV = itemHeight;
if (itemWidth > constraintWidth - 10 * float.Epsilon)
{
panelU = Math.Max(itemWidth, panelU);
panelV += itemHeight;
lineU = 0;
lineV = 0;
}
}
else
{
lineU += itemWidth;
lineV = Math.Max(itemHeight, lineV);
}
}
panelU = Math.Max(lineU, panelU);
panelV += lineV;
return new Size(panelU, panelV);
}
protected override Size ArrangeOverride(Size finalSize)
{
var finalSizeWidth = (float)finalSize.Width;
int num = 0;
var currentHeight = 0f;
float lineU = 0;
float lineV = 0;
var children = base.InternalChildren;
for (int i = 0; i < children.Count; i++)
{
var desiredSize = children[i].DesiredSize;
float width = (float)desiredSize.Width;
float height = (float)desiredSize.Height;
// when formed line is too large with current element
if (lineU + width > finalSizeWidth - 10 * float.Epsilon)
{
arrangeLine(currentHeight, lineV, num, i, children, lineU, finalSizeWidth);
currentHeight += lineV;
lineU = width;
lineV = height;
// when current element wants to be larger than available space - put him single
if (width > finalSizeWidth - 10 * float.Epsilon)
{
arrangeLine(currentHeight, height, i, ++i, children, finalSizeWidth, finalSizeWidth);
currentHeight += height;
lineU = 0;
lineV = 0;
}
num = i;
}
else // just normal forming line
{
lineU += width;
lineV = Math.Max(height, lineV);
}
}
if (num < children.Count)
{
var lineWidth = lineU;
arrangeLine(currentHeight, lineV, num, children.Count, children, lineWidth, finalSizeWidth);
}
return finalSize;
}
private void arrangeLine(float v, float lineV, int start, int end,
UIElementCollection children, float lineWidth, float availableWidth)
{
float num = 0f;
float multiplierU = availableWidth / lineWidth;
for (int i = start; i < end; i++)
{
UIElement uIElement = children[i];
if (uIElement != null)
{
var width = (float)uIElement.DesiredSize.Width;
float itemU = width * multiplierU;
uIElement.Arrange(new Rect(num, v, itemU, lineV));
num += itemU;
}
}
}
这是两种措施的解决方案:
private List<KeyValuePair<int, float>> _lineMuls;
protected override Size MeasureOverride(Size constraint)
{
_lineMuls = new List<KeyValuePair<int, float>>();
float lineU = 0;
float lineV = 0;
float panelU = 0;
float panelV = 0;
int lineCount = 0;
//float constraintWidth = horizontal ? (float)constraint.Width : (float)constraint.Height;
float constraintWidth = (float)constraint.Width;
var children = base.InternalChildren;
Size encouragingConstraint = new Size(Double.PositiveInfinity, constraint.Height);
for (int i = 0; i < children.Count; i++)
{
UIElement child = children[i];
if (child == null)
continue;
child.Measure(constraint);
var desiredSize = child.DesiredSize;
var itemWidth = (float)desiredSize.Width;
var itemHeight = (float)desiredSize.Height;
if (lineU + itemWidth > constraintWidth - 2* float.Epsilon)
{
panelU = Math.Max(lineU, panelU);
panelV += lineV;
lineU = itemWidth;
lineV = itemHeight;
if (itemWidth > constraintWidth - 2* float.Epsilon)
{
panelU = Math.Max(itemWidth, panelU);
panelV += itemHeight;
child.Measure(new Size(constraintWidth - 1, itemHeight));
_lineMuls.Add(new KeyValuePair<int, float>(1, 1));
lineU = 0;
lineV = 0;
lineCount = 0;
}
else
{
MeasureLine(constraintWidth, lineU, i, lineCount, children);
lineCount = 1;
}
}
else
{
lineCount++;
lineU += itemWidth;
lineV = Math.Max(itemHeight, lineV);
}
}
if (lineCount > 0)
{
MeasureLine(constraintWidth, lineU, children.Count, lineCount, children);
}
panelU = Math.Max(lineU, panelU);
panelV += lineV;
return new Size(panelU, panelV);
}
private void MeasureLine(float constraintWidth, float lineU, int i, int lineCount, UIElementCollection children)
{
var mulU = (constraintWidth - 1) / (lineU + 1);
mulU = mulU < 1.02 ? 1 : mulU;
_lineMuls.Add(new KeyValuePair<int, float>(lineCount, mulU));
for (int j = i - lineCount; j < i; j++)
{
var child = children[j];
if (child == null)
continue;
var desiredSize = child.DesiredSize;
float itemWidth = (float)desiredSize.Width * mulU;
var itemHeight = (float)desiredSize.Height;
child.Measure(new Size(itemWidth, itemHeight));
}
}
protected override Size ArrangeOverride(Size finalSize)
{
var children = base.InternalChildren;
int childCount = 0;
float height = 0f;
for (int i = 0; i < _lineMuls.Count; i++)
{
var line = _lineMuls[i];
var offsetU = 0f;
var offsetV = 0f;
for (int e = childCount; e < childCount + line.Key; e++)
{
UIElement child = children[e];
if (child == null)
continue;
var desiredSize = child.DesiredSize;
var itemWidth = (float)desiredSize.Width * line.Value;
var itemHeight = (float)desiredSize.Height;
child.Arrange(new Rect(offsetU, height, itemWidth, itemHeight));
offsetV = Math.Max(offsetV, itemHeight);
offsetU += itemWidth;
}
childCount += line.Key;
height += offsetV;
}
//_lineMuls = null;
return finalSize;
}
非常感谢您的帮助。