[WPF Custom Control Library] understand the process layout WPF, and use Measure to add animation Expander

Original: [WPF Custom Control Library] understand the process layout WPF, and use Measure to add animation Expander

1 Introduction

This article describes a two-step process layout WPF UI elements, and introduction by using only Measure Resizer control what content can be achieved.

I do not recommend beginners too much work to do animation, but animation can direct users to the appropriate line of sight, improve the user experience. For example, the figure of this animation, the animation is quite common, dynamically change their height when changing the height of the content, in addition to good-looking, is also useful to improve the user experience. Unfortunately, WPF itself does not support this by default in this regard, even Expander expand / collapse are no animation. For this reason I can achieve a animated way to change their size Resizer control when changing the size of the content ( think of what a good name, request recommended ). In fact, honestly Toolkit transplant AccordionItem from Silverlight like, but I think some of the concepts introduced by this control layout (and animated) of. XAML shown Resizer used as follows:

lt;StackPanelgt;
    lt;kino:KinoResizer HorizontalContentAlignment=quot;Stretchquot;gt;
        lt;Expander Header=quot;Expander1quot;gt;
            lt;Rectangle Height=quot;100quot;
                       Fill=quot;Redquot; /gt;
        lt;/Expandergt;
    lt;/kino:KinoResizergt;
    lt;kino:KinoResizer HorizontalContentAlignment=quot;Stretchquot;gt;
        lt;Expander Header=quot;Expander2quot;gt;
            lt;Rectangle Height=quot;100quot;
                       Fill=quot;Bluequot; /gt;
        lt;/Expandergt;
    lt;/kino:KinoResizergt;
lt;/StackPanelgt;

2. The need to understand the concept of

To achieve this we must first understand the process control layout WPF UI elements.

2.1 two-step process layout

WPF layout substantially Measure and Arrange into two steps, first the layout elements recursively calculate the required size with all child elements Measure, then Arrange implement the layout.

To StackPanel, for example, when the need StackPanel layout, it will first have to know how much space is available, and then use the available space to ask all children of Children's how much space they need, this is the Measure; all child elements that need space after layout logic of its own sub-element to determine the actual size and location of installation, which is Arrange.

When re StackPanel layout (e.g., size change StackPanel), this time the two-step process is repeated StackPanel layout. If a child element of the StackPanel need to re-layout, it will need to re-notify the StackPanel layout.

2.2 MeasureOverride

MeasureOverride in a derived class override, the sub-elements required for measuring the size in the layout. The parent element is simply tell yourself how much space is available again after themselves and their child elements to discuss, we need to tell the size of the parent element.

2.3 DesiredSize

DesiredSize refers to the size determined after looking Measure. The following code demonstrates how to use MeasureOverride and DesiredSize:

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

    // In our example, we just have one child. 
    // Report that our panel requires just the size of its only child.
    foreach (UIElement child in InternalChildren)
    {
        child.Measure(availableSize);
        panelDesiredSize = child.DesiredSize;
    }

    return panelDesiredSize ;
}

2.4 InvalidateMeasure

InvalidateMeasure the current measure invalid layout elements, and asynchronously triggered remeasurement.

2.5 IsMeasureValid

IsMeasureValid indicating the current size distribution measuring return valid, so this value may be used InvalidateMeasure becomes False.

3. To achieve

Resizer not need to use Arrange, so understanding these concepts above is enough. Resizer principle is simple, Reszier the ControlTemplate contains a ContentControl (InnerContentControl), when the request for re-layout Resizer change the size of the InnerContentControl, Resizer start a Storyboard, InnerContentControl.DesiredSize final value to gradually change the ContentHeight Resizer and ContentWidth properties:

DoubleAnimation heightAnimation;
DoubleAnimation widthAnimation;
if (Animation != null)
{
    heightAnimation = Animation.Clone();
    Storyboard.SetTarget(heightAnimation, this);
    Storyboard.SetTargetProperty(heightAnimation, new PropertyPath(ContentHeightProperty));

    widthAnimation = Animation.Clone();
    Storyboard.SetTarget(widthAnimation, this);
    Storyboard.SetTargetProperty(widthAnimation, new PropertyPath(ContentWidthProperty));
}
else
{
    heightAnimation = _defaultHeightAnimation;
    widthAnimation = _defaultWidthAnimation;
}

heightAnimation.From = ActualHeight;
heightAnimation.To = InnerContentControl.DesiredSize.Height;
widthAnimation.From = ActualWidth;
widthAnimation.To = InnerContentControl.DesiredSize.Width;

_resizingStoryboard.Children.Clear();
_resizingStoryboard.Children.Add(heightAnimation);
_resizingStoryboard.Children.Add(widthAnimation);

And changing ContentHeight call ContentWidth InvalidateMeasure () request re-layout, MeasureOverride ContentWidth and the return value ContentHeight. Resizer this size would vary depending on the Storyboard gradually progress to achieve the animation.

protected override Size MeasureOverride(Size constraint)
{
    if (_isResizing)
        return new Size(ContentWidth, ContentHeight);

    if (_isInnerContentMeasuring)
    {
        _isInnerContentMeasuring = false;
        ChangeSize(true);
    }

    return base.MeasureOverride(constraint);
}

private void ChangeSize(bool useAnimation)
{
    if (InnerContentControl == null)
    {
        return;
    }

    if (useAnimation == false)
    {
        ContentHeight = InnerContentControl.ActualHeight;
        ContentWidth = InnerContentControl.ActualWidth;
    }
    else
    {
        if (_isResizing)
        {
            ResizingStoryboard.Stop();
        }

        _isResizing = true;
        ResizingStoryboard.Begin();
    }
}

Can simply add animation Expander use Resizer control, the effect is as follows:

Finally, Resizer also provides DoubleAnimation Animationproperty is used to modify the animation is used as follows:

lt;kino:KinoResizer HorizontalContentAlignment=quot;Stretchquot;gt;
    lt;kino:KinoResizer.Animationgt;
        lt;DoubleAnimation BeginTime=quot;0:0:0quot;
                         Duration=quot;0:0:3quot;gt;
            lt;DoubleAnimation.EasingFunctiongt;
                lt;QuinticEase EasingMode=quot;EaseOutquot; /gt;
            lt;/DoubleAnimation.EasingFunctiongt;
        lt;/DoubleAnimationgt;
    lt;/kino:KinoResizer.Animationgt;
    lt;TextBox AcceptsReturn=quot;Truequot;
             VerticalScrollBarVisibility=quot;Disabledquot; /gt;
lt;/kino:KinoResizergt;

4. Conclusion

Resizer control I usually will not be used alone, but on the other controls inside, such as Button:

Because of this control performance is not high, the future may also improve the API, then was placed Primitives namespace.

Once upon a time often encounter the "layout cycle" this error, which often appear in the code that handles layout. Recently a long time did not encounter this error, perhaps the WPF become robust, and perhaps my code becomes an advance. But once bitten twice shy, so I rarely touch the Measure and Arrange code, I also recommend the use of Measure and Arrange be careful.

5. Reference

FrameworkElement.MeasureOverride(Size) Method (System.Windows) Microsoft Docs.html

UIElement.DesiredSize Property (System.Windows) Microsoft Docs.html

UIElement.InvalidateMeasure Method (System.Windows) Microsoft Docs

UIElement.IsMeasureValid Property (System.Windows) Microsoft Docs

6. Source

Kino.Toolkit.Wpf_Resizer at master

Guess you like

Origin www.cnblogs.com/lonelyxmas/p/11242108.html