为什么不启动MeasureOverride?

时间:2017-05-11 13:26:21

标签: wpf custom-controls wrappanel measureoverride

我有两个用于显示评论和签名集合的自定义控件。每个控件都有一个用于更改显示样式的按钮 - 显示整个集合或仅显示前4个元素。

当我将这些控件放在默认的WrapPanel中时 - 它们可以正常工作:根据用户的操作进行扩展或折叠。 在我自己的带有新ArrangeOverride功能的自定义WrapPanel中,它们也可以正常工作。

现在我需要WrapPanel选项" LastChildFill" (我在这里找到了例子)并使用自定义" WrapDirection" (类似于FlowDirection)。 WrapPanel中的第一个或最后一个元素必须填充所有剩余空间,同时它超过元素的MinWidth。 所以我用自定义的MeasureOverride和ArrangeOverride创建了第三个WrapPanel。并且它可以按照我的需要工作,但是当我更改控件显示样式时,控件内的集合会发生变化,但是我的WrapPanel中的MeasureOverride在我更改窗口大小之前不会启动。

更新:MeasureOverride不会开始扩展填充剩余空间的第一个或最后一个元素。折叠此元素可以正常工作。

我不明白我用自定义MeasureOverride打破了什么?

更新2: 我觉得我的问题与此类似:MeasureOverride not always called on children's property changes

并附上MeasureOverride代码。

public class WrapPanelFlowDirection : WrapPanel
{
    public bool LastChildFill
    {
        get { return (bool)GetValue(LastChildFillProperty); }
        set { SetValue(LastChildFillProperty, value); }
    }

    public static readonly DependencyProperty LastChildFillProperty =
        DependencyProperty.Register("LastChildFill", typeof(bool), typeof(WrapPanelFlowDirection), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsMeasure));

    public WrapDirection WrapDirection
    {
        get { return (WrapDirection)GetValue(WrapDirectionProperty); }
        set { SetValue(WrapDirectionProperty, value); }
    }

    public static readonly DependencyProperty WrapDirectionProperty =
        DependencyProperty.Register("WrapDirection", typeof(WrapDirection), typeof(WrapPanelFlowDirection), new FrameworkPropertyMetadata(WrapDirection.LeftToRight, FrameworkPropertyMetadataOptions.AffectsMeasure));

    #region Measure Override

    protected override Size MeasureOverride(Size availableSize)
    {
        var panelSize = new Size(availableSize.Width, 0);
        double minWidth = 0;

        foreach (FrameworkElement child in this.InternalChildren)
        {
            child.Measure(availableSize);
            minWidth += child.DesiredSize.Width;
        }

        int firstChildInLine = 0;
        double currLineHeight = 0;
        double currLineWidth = 0;
        UIElementCollection children = this.InternalChildren;

        for (int i = 0; i < children.Count; i++)
        {
            FrameworkElement child = children[i] as FrameworkElement;
            if (child == null)
                continue;

            double w = 0;
            if (child.MinWidth == 0)
                w = child.DesiredSize.Width;
            else
                w = child.MinWidth;

            if (currLineWidth + w <= availableSize.Width)
            {
                currLineWidth += w;
                currLineHeight = Math.Max(currLineHeight, child.DesiredSize.Height);
            }
            else
            {
                MeasureOneLine(firstChildInLine, i - 1, ref currLineHeight, availableSize.Width);
                panelSize.Height += currLineHeight;
                currLineWidth = w;
                currLineHeight = child.DesiredSize.Height;
                if (w > availableSize.Width)
                {
                    MeasureOneLine(i, i, ref currLineHeight, availableSize.Width);
                    panelSize.Height += currLineHeight;
                    currLineHeight = currLineWidth = 0;
                    firstChildInLine = i + 1;
                }
                else
                {
                    firstChildInLine = i;
                }
            }

        }

        if (firstChildInLine < children.Count)
        {
            MeasureOneLine(firstChildInLine, children.Count - 1, ref currLineHeight, availableSize.Width);
            panelSize.Height += currLineHeight;
        }

        if (double.IsInfinity(panelSize.Width))
            panelSize.Width = minWidth;
        return panelSize;
    }

    private void MeasureOneLine(int start, int end, ref double height, double totalWidth)
    {
        UIElementCollection children = this.InternalChildren;

        if (WrapDirection == WrapDirection.LeftToRight)
        {
            for (int i = start; i < end; i++)
            {
                FrameworkElement child = children[i] as FrameworkElement;
                if (child == null)
                    continue;

                double w = 0;
                if (child.MinWidth == 0)
                    w = child.DesiredSize.Width;
                else
                    w = child.MinWidth;
                if (i < end)
                {
                    child.Measure(new Size(w, height));
                    totalWidth -= w;
                }
                else
                {
                    child.Measure(new Size(totalWidth, double.PositiveInfinity));
                    height = Math.Max(height, child.DesiredSize.Height);
                }
            }
        }
        else
        {
            for (int i = end; i >= start; i--)
            {
                FrameworkElement child = children[i] as FrameworkElement;
                if (child == null)
                    continue;

                double w = 0;
                if (child.MinWidth == 0)
                    w = child.DesiredSize.Width;
                else
                    w = child.MinWidth;
                if (i > start)
                {
                    child.Measure(new Size(w, height));
                    totalWidth -= w;
                }
                else
                {
                    child.Measure(new Size(totalWidth, double.PositiveInfinity));
                    height = Math.Max(height, child.DesiredSize.Height);
                }
            }
        }
    }

    #endregion

1 个答案:

答案 0 :(得分:0)

我在这里找到了解决方案:How does a container know when a child has called InvalidateArrange?

它对我有用。

    public static void OnPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        UIElement panel= VisualTreeHelper.GetParent(source) as UIElement;
        if(panel != null)       
            {
                panel.InvalidateMeasure();
                panel.InvalidateArrange();
            }
    }