自定义WrapPanel怪异行为-布局是否损坏vol.2

时间:2018-07-30 10:49:23

标签: c# wpf xaml custom-controls wrappanel

我正在尝试创建一个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;
}

非常感谢您的帮助。

0 个答案:

没有答案