为什么不启动 MeasureOverride?

Why doesn't start MeasureOverride?

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

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

现在我需要带选项 "LastChildFill" 的 WrapPanel(我在此处找到示例)和自定义 "WrapDirection"(类似于 FlowDirection)。当 WrapPanel 中的第一个或最后一个元素超过元素的 MinWidth 时,必须填充所有剩余的 space。 所以我用自定义 MeasureOverride 和 ArrangeOverride 创建了第三个 WrapPanel。它按我的意愿工作,但是当我更改控件显示样式时,控件内的集合会更改,但我的 WrapPanel 中的 MeasureOverride 不会启动,直到我更改 window 大小。

更新:MeasureOverride 不会开始扩展填充剩余 space 的第一个或最后一个元素。折叠此元素效果很好。

我不明白自定义 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

我在这里找到了解决方案: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();
            }
    }