Stackpanel: Height vs ActualHeight vs ExtentHeight

2019-03-10 00:26发布

问题:

i want to know the height of all items my StackPanel.

What is the difference between:

  • Height - Gets or sets the suggested height of the element.
  • ActualHeight - Gets the rendered height of this element. (readonly)
  • ExtentHeight - Gets a value that contains the vertical size of the extent. (readonly)
  • ViewportHeight - Gets a value that contains the vertical size of the content's viewport. (readonly)
  • DesiredSize - Gets the size that this element computed during the measure pass of the layout process. (readonly)
  • RenderSize - Gets (or sets, but see Remarks) the final render size of this element.

From MSDN:

Height
Gets or sets the suggested height of the element.

Property value: Double - The height of the element, in device-independent units (1/96th inch per unit).

The height of the element, in device-independent units (1/96th inch per unit).

 

ActualHeight (readonly)
Gets the rendered height of this element.

Property value: Double - The element's height, as a value in device-independent units (1/96th inch per unit).

This property is a calculated value based on other height inputs, and the layout system. The value is set by the layout system itself, based on an actual rendering pass, and may therefore lag slightly behind the set value of properties such as Height that are the basis of the input change.

Because ActualHeight is a calculated value, you should be aware that there could be multiple or incremental reported changes to it as a result of various operations by the layout system. The layout system may be calculating required measure space for child elements, constraints by the parent element, and so on.

 

ExtentHeight (readonly)
Gets a value that contains the vertical size of the extent.

Property height: Double - A Double that represents the vertical size of the extent.

The returned value is described in Device Independent Pixels.

 

ViewportHeight (readonly)
Gets a value that contains the vertical size of the content's viewport.

Property value: Double - The Double that represents the vertical size of the content's viewport.

The returned value is described in Device Independent Pixels.

 

DesiredSize (readonly)
Gets the size that this element computed during the measure pass of the layout process.

Property value: Size - The computed size, which becomes the desired size for the arrange pass.

The value returned by this property will only be a valid measurement if the value of the IsMeasureValid property is true.

DesiredSize is typically checked as one of the measurement factors when you implement layout behavior overrides such as ArrangeOverride, MeasureOverride, or OnRender (in the OnRender case, you might check RenderSize instead, but this depends on your implementation). Depending on the scenario, DesiredSize might be fully respected by your implementation logic, constraints on DesiredSize might be applied, and such constraints might also change other characteristics of either the parent element or child element. For example, a control that supports scrollable regions (but chooses not to derive from the WPF framework-level controls that already enable scrollable regions) could compare available size to DesiredSize. The control could then set an internal state that enabled scrollbars in the UI for that control. Or, DesiredSize could potentially also be ignored in certain scenarios.

 

RenderSize Gets the final render size of this element.

Property value: Size - The rendered size for this element.

This property can be used for checking the applicable render size within layout system overrides such as OnRender or GetLayoutClip.

A more common scenario is handling the SizeChanged event with the class handler override or the OnRenderSizeChanged event.


In my case i want know the desired height of all items in the StackPanel.

In other words: i want to know height of all items in the StackPanel (before drawing), and if they were to overflow the panel, i will

  • delete
  • shrink
  • scale
  • adjust

items to ensure they fit in the StackPanel.

Which means i probably want to get the desired height (ExtentHeight? DesiredSize?) during a resize event (SizeChanged? LayoutUpdated? ) - before any drawing happens (so it's faster).

Most of these properties return zero; so obviously there's some understanding of what these properties mean that i don't know and are not explained in the documentation.

回答1:

As you know the StackPanel is a [Panel] object. Each panel communicates with its children by two methods to determine the final sizes and positions. The first method is MeasureOverride and the second is ArrangeOverride.

The MeasureOveride asks each child for desired size with a given amount of space available. ArrangeOverride arranges the children after measurement has been completed.

Let's create a stackpanel:

public class AnotherStackPanel : Panel
{
    public static readonly DependencyProperty OrientationProperty =
        DependencyProperty.Register(“Orientation”, typeof(Orientation),
        typeof(SimpleStackPanel), new FrameworkPropertyMetadata(
        Orientation.Vertical, FrameworkPropertyMetadataOptions.AffectsMeasure));

    public Orientation Orientation
    {
        get { return (Orientation)GetValue(OrientationProperty); }
        set { SetValue(OrientationProperty, value); }
    }

    protected override Size MeasureOverride(Size availableSize)
    {
        Size desiredSize = new Size();

        if (Orientation == Orientation.Vertical)
            availableSize.Height = Double.PositiveInfinity;
        else
            availableSize.Width = Double.PositiveInfinity;
        foreach (UIElement child in this.Children)
        {
            if (child != null)
            {
                child.Measure(availableSize);

                if (Orientation == Orientation.Vertical)
                {
                    desiredSize.Width = Math.Max(desiredSize.Width,
                    child.DesiredSize.Width);
                    desiredSize.Height += child.DesiredSize.Height;
                }
                else
                {
                    desiredSize.Height = Math.Max(desiredSize.Height,
                    child.DesiredSize.Height);
                    desiredSize.Width += child.DesiredSize.Width;
                }
            }
        }
        return desiredSize;
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        double offset = 0;
        foreach (UIElement child in this.Children)
        {
            if (child != null)
            {
                if (Orientation == Orientation.Vertical)
                {               
                    child.Arrange(new Rect(0, offset, finalSize.Width,
                    child.DesiredSize.Height));                 
                    offset += child.DesiredSize.Height;
                }
                else
                {                   
                    child.Arrange(new Rect(offset, 0, child.DesiredSize.Width,
                    finalSize.Height));
                    offset += child.DesiredSize.Width;
                }
            }
        }
        return finalSize;
    }
}
  • The DesiredSize (the size returned by MeasureOverride) is sum of child sizes in the direction of StackPanel and the size of largest child in the other direction.

  • RenderSize represents the final size of the StackPanel after layout is complete.

  • ActualHeight is exactly same as RenderSize.Height.

For rely these properties you should access them only within an event handler for LayoutUpdated event.

  • for ViewPortHeight look here
  • for ExtentHeight look here


回答2:

Above answer is correct, except that RenderSize and ActualHeight can have temporarily different values. RenderSize gets set before OnRender, whereas ActualHeight gets set once WPF has finished the layout and render processing for that control. At the very end, LayoutUpdated gets raised.

Therefore, RenderSize can be used within OnRender, but ActualHeight will still have the old value from before the layout started.

The sequence looks like this:

MeasureOverride() => sets DesiredSize
ArrangeOverride() => sets RenderSize
OnRender()

WPF might execute this sequence several times (recursion). Once everything is settled, the following gets executed:

ActualHeight = RenderSize.Height

ActualHeight can be accessed any time (!) after the first layout is done, except during the layout process itself of measure, arrange and render. WPF ensures that any code gets completed before layout processing runs.