0

I have two custom controls for displaying collections of comments and signatures. Each of these controls has a button for changing display style - show entire collection or just first 4 elements.

When I put these controls in a default WrapPanel - they work fine: expand or collapse depending on user's actions. In my own custom WrapPanel with new ArrangeOverride function they also work fine.

Now I need WrapPanel with option "LastChildFill" (I found example here) and with custom "WrapDirection" (similar to FlowDirection). First or last element in WrapPanel have to fill all remaining space while it's more than MinWidth of the element. So I've created third WrapPanel with custom MeasureOverride and ArrangeOverride. And it works as I want, but when I change control display style a collection inside a control changes, but MeasureOverride in my WrapPanel doesn't start until I change window size.

Update: MeasureOverride doesn't start for expanding first or last element which fills remaining space. Collapsing for this element works fine.

I don't understand what thing I have broken with custom MeasureOverride?

Update 2: I fugured that my problem is similar to this: MeasureOverride not always called on children's property changes.

And attach MeasureOverride code.

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
4

1 回答 1

0

I found solution here : How does a container know when a child has called InvalidateArrange?

And it worked for me.

    public static void OnPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        UIElement panel= VisualTreeHelper.GetParent(source) as UIElement;
        if(panel != null)       
            {
                panel.InvalidateMeasure();
                panel.InvalidateArrange();
            }
    }
于 2017-05-12T15:00:07.573 回答