WPF animation system

1, the basic animation.
WPF provides a more advanced model, you can only focus on the definition animation of this model, regardless of their rendering, this model is based on dependency property infrastructure, in essence, WPF animation is simply changing over time interval One way dependent property values. However, there is another limit, in order to achieve dynamic properties, the need to support the corresponding data type of animation classes, e.g. Button.Width properties using double data types, to achieve the dynamic properties necessary to use DoubleAnimation class, but Button. Padding Thickness configuration attribute is used, it is necessary to use ThicknessAnimation class. For, you can create your own animation class for the application of animation properties for the corresponding data type, you will find, System.Windows.Media.Animation name space has been provided for the animation classes you want to use the most data types.

2, Animation classes.
There are actually two types of animation, one is to change the properties of a stepwise increasing manner between the start and end values of the animation (linear interpolation process), the other is worth suddenly become animated from one value to another ( keyframe animation). All keyframe animation using the form "type name + AnimationUsingKeyFrames" were named, such as StringAnimationUsingKeyFrames and ObjectAnimationUsingKeyFrames. Some data types keyframe animation category, but there is no interpolation animation class. For example, a string of key frame animation application, the application can not be used as a string interpolation animation. However, all data types are supported keyframe animation, unless they do not support animation. All types of data having (using interpolation) conventional animation class also have a corresponding key frame animation type animation, such as the linear interpolation corresponding to DoubleAnimation DoubleAnimationUsingKyyFrames. There is also a path-based animation. Thus, WPF animation using three methods: linear interpolation, and the key frame path. System.Windows.Media.Animation found in the following namespaces:

7 "type class name + Animation" These classes use interpolation animation.

22 "type name + AnimationUsingKeyFrames" These classes use keyframe animation.

3 "type name + AnimationUsingPath" category such use path-based animation.

3, use the code to create animation.
wpf, the most common technique is linear interpolation animated movie, standard frame rate is 60 sec / frame, the easiest way is an example of the use of animation listed in the foregoing wherein an animation class, and then use the modified elements BeginAnimation () the method, all of the elements wpf, starting from UIElement base class inherits BeginAnimation () method, which is part of IAnimatable interface.

Code xaml:

<Button Width="150" Height="60" Grid.Row="0" Click="Button_Click">点击开始动画</Button>
<Button Grid.Row="1" Name="btn1" Width="150" Height="60" Content="动画按钮"></Button>

Background Code:

private void Button_Click(object sender, RoutedEventArgs e)
{
    //实例化一个DoubleAnimation类。
    DoubleAnimation doubleAnimation = new DoubleAnimation();
    //设置From属性。
    doubleAnimation.From = btn1.Width;
    //设置To属性。
    doubleAnimation.To = 250;
    //设置Duration属性。
    doubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(5));
    //为元素设置BeginAnimation方法。
    btn1.BeginAnimation(Button.WidthProperty, doubleAnimation);
}

Here Insert Picture Description
Wherein, From the beginning of the attribute value of the element, To is the end value of the attribute element attributes, Duration entire animation execution time. To property without using, By property may be used, By the value is simply added to the From value, to reach a value To. However, for non-numeric data types,, By property it is meaningless.

4, simultaneous animations.
Animation is to create multiple animation, and then set the properties for the element.

private void Button_Click(object sender, RoutedEventArgs e)
{
    //实例化一个DoubleAnimation类。
    DoubleAnimation doubleAnimation = new DoubleAnimation();
    //设置From属性。
    doubleAnimation.From = btn1.Width;
    //设置To属性。
    doubleAnimation.To = 250;
    //设置Duration属性。
    doubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(5));
    //为元素设置BeginAnimation方法。
    btn1.BeginAnimation(Button.WidthProperty, doubleAnimation);

    //实例化一个DoubleAnimation动画,用于设置元素的高。
    DoubleAnimation doubleAnimationHeight = new DoubleAnimation();
    //设置Form属性。
    doubleAnimationHeight.From = btn1.Height;
    //设置To属性的值。
    doubleAnimationHeight.By = 70;
    //设置时间。
    doubleAnimationHeight.Duration = new Duration(TimeSpan.FromSeconds(3));
    //开始动画。
    btn1.BeginAnimation(Button.HeightProperty, doubleAnimationHeight);
}

5, the life cycle of the animation.
From a technical point of view, WPF animation is only temporary, which means that they can not really change the value of the basic attributes, when the animation is active, only covers the value of the property.

Animated way, at the end of the animation operation will remain active, because the animation is necessary to maintain the width of the button to a new value, this time led to commonly asked questions, if you try to use code to modify the property value after completion, code It will not work, because the code only specifies a new local value for the property, but property values ​​will first try after animation.

To address can modify the properties of value after completion, the following methods to resolve.

A), the properties set AutoReverse If this attribute is set to true, will reverse movement returns the original value (not suitable for the animation is completed, then the last set of values ​​for the attributes, but reduced to a value before the movie).

b), change attributes FillBehavior. Usually, FillBehavior property is set to HoldEnd, which means that at the end of Dangdang animation, will continue to apply the final value of the target element. If FillBehavior property to Stop, as long as the end of the animation, the property will revert to the original value (applicable after the end of the animation, once again to set a new value, and generally do not AutoReverse combined with the use of two with one on the line ).

6, Completed event animation.
When using the Completed event, to set up the event BeginAnimation () method before, otherwise it does not work. In Completed, the animations can be rendered inactive by calling BeginAnimation () method, for which only need to specify the property, and pass a null reference to animated objects.

void doubleAnimationHeight_Completed(object sender, EventArgs e)
{
    MessageBox.Show("动画的高执行完毕了!!!");
    //设置空引用。
    btn1.BeginAnimation(Button.HeightProperty, null);
}

7, TimeLine class.

Here Insert Picture Description

7.1), AccelerationRatio and DeceleRation property.
AcclerationRation can be compressed by a time axis and a portion DecelerationRation properties, so that faster operation of the animation, and other times to compensate for the stretching, so that the total time remains unchanged. These two properties are expressed as a percentage value, e.g., the AcceleRation property to 0.3, using 30% expressed the wish that the time duration of the animation of the front acceleration. For example, in an animation of 10 seconds, three seconds before the operation is accelerated, and the remaining 7 seconds runs at a constant speed, if the DeceleRation property is set to 0.3, then the last three seconds of deceleration back.

private void Button_Click(object sender, RoutedEventArgs e)
{
    //实例化一个DoubleAnimation类。
    DoubleAnimation doubleAnimation = new DoubleAnimation();
    //设置From属性。
    doubleAnimation.From = btn1.Width;
    //前5秒加速度运行。
    doubleAnimation.AccelerationRatio = 0.5;
    //后2秒减速运行
    doubleAnimation.DecelerationRatio = 0.2;
    //设置To属性。
    doubleAnimation.To = 1000;
    //设置Duration属性。
    doubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(10));
    //为元素设置BeginAnimation方法。
    btn1.BeginAnimation(Button.WidthProperty, doubleAnimation);
}

7.2), RepeatBehavior property.
Use RepeataBehavior property to control how the animation run repeatedly, if desired fixed number of times repeated, should pass the appropriate number of times RepeatBehavior constructor.

RepeatBehavior repeat may also be provided permanently. //设置重复次数为3次。 doubleAnimation.RepeatBehavior = new RepeatBehavior(3); //设置永久重复动画。 doubleAnimation.RepeatBehavior = RepeatBehavior.Forever;

8, the story version.
WPF animation represented by a set of animation classes, using a few property-related information, such as start, end, and duration values. This obviously makes them very suitable for XAMl, it is not very clear is how, and how to trigger the animation for specific events and properties associated with animation at the right time.

Storyboard: XAML storyboard is equivalent BeginAnimation () method, the specified animation to the appropriate elements and attributes storyboards.

Trigger Event: Event Trigger response property changes or events (e.g., the Click event), and controls the storyboard.

Storyboard:
storyboard timeline is enhanced, grouping a plurality of animation can be used, but also has the ability to control animation playback - pause, stop and play position. However, the most basic function Storyboard class provides is the ability to use TargetProperty and TargetName attribute to point to a specific property and a specific element, in other words, between the storyboard animation and animated properties you want to apply a bridge. Wherein TargetProperty TargetName properties and attributes are attached property.

<!--创建一个故事板-->
<Storyboard Storyboard.TargetProperty="Width">
    <!--创建一个DoubleAnimation类。-->
    <DoubleAnimation To="350"  RepeatBehavior="Forever" Duration="0:0:3"></DoubleAnimation>
</Storyboard>

<!--由于Storyboard.TargetProperty属性是附加属性,因此还可以写出-->
<Storyboard >
    <!--创建一个DoubleAnimation类。-->
    <DoubleAnimation Storyboard.TargetProperty = "Width" To="350"  RepeatBehavior="Forever" Duration="0:0:3"></DoubleAnimation>
</Storyboard>

Event triggers:

You can define event triggers in the following four locations.

a), by style (Style.Triggers set).

b), by the data template (DataTemplate.Triggers set).

c), in the control template (ControlTemplate.Triggers collection).

d), directly defined event trigger (FrameworkElement.Triggers set) in the element.

When you create an event trigger, you need to specify the start trigger routing events and triggers one or more actions to perform. For movies, the most common action is BeginStoryboard, this action is equivalent to calling BeginAnimation () method. All event triggers can initiate action, all actions by System.Windows.TriggerAction inherited from the class represents.

Code xaml:

<Button Width="200" Height="80" Content="事件触发器" FontSize="20">
    <!--元素触发器-->
    <Button.Triggers>
        <!--定义事件触发器-->
        <EventTrigger RoutedEvent="Button.Click">
            <!--执行一个动作-->
            <EventTrigger.Actions>
                <!--开始故事板-->
                <BeginStoryboard>
                    <!--创建一个故事板-->
                    <Storyboard >
                        <!--创建一个DoubleAnimation类。-->
                        <DoubleAnimation Storyboard.TargetProperty = "Width" To="350"  RepeatBehavior="Forever" Duration="0:0:3"></DoubleAnimation>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger.Actions>
        </EventTrigger>
    </Button.Triggers>
</Button>

Here Insert Picture Description
9, using the style associated trigger.
There are three basic types of WPF Triggers: Triggers properties, data triggers and event triggers. It is the most common way to use triggers associated with animation, but not the only choice.

Code xaml:

<Window.Resources>
    <Style  TargetType="Button">
        <Setter Property="FontSize" Value="20"></Setter>            
        <Style.Triggers>
            <!--使用属性触发器-->
            <Trigger Property="IsPressed" Value="True">
                <!--在这里使用的是EnterActions-->
                <Trigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard Storyboard.TargetProperty="Width">
                            <DoubleAnimation To="300" RepeatBehavior="Forever" Duration="0:0:3"></DoubleAnimation>
                        </Storyboard>
                    </BeginStoryboard>
                </Trigger.EnterActions>
            </Trigger>
    </Style>
</Window.Resources>

<Grid>
    <Button Width="200" Height="80"  Content="使用样式关联触发器"></Button>
</Grid>

Here Insert Picture Description
10, synchronized animation.
StoryBoard indirectly class inherits from TimeLineGroup class, StoryBoard class can contain multiple movies, the movies can be managed as a group, which means they can start at the same time.

<Window.Resources>
    <Style TargetType="Button">            
        <Setter Property="FontSize" Value="20"></Setter>            
        <Style.Triggers>
            <Trigger Property="IsPressed" Value="True">
                <Trigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetProperty="Width" To="300" Duration="0:0:3"></DoubleAnimation>
                            <DoubleAnimation Storyboard.TargetProperty="Height" To="100" Duration="0:0:3"></DoubleAnimation>
                        </Storyboard>
                    </BeginStoryboard>
                </Trigger.EnterActions>
            </Trigger>
        </Style.Triggers>
    </Style>
</Window.Resources>
    
<Grid>
    <Button Width="150" Height="70" Content="同步动画"></Button>
</Grid>

Here Insert Picture Description
11, control playback.
So far, it has been used in the event trigger an action, load BeginStoryboard action animation, however, once created the story version, you can use other motion control storyboards, these actions classes inherit from ControllableStoryboardAction class, control version of the story The main categories are as follows:

Here Insert Picture Description
For the successful implementation of these actions, all the triggers must be defined in the same Triggers collection, if the action triggers and PauseStoryboard BeginStoryboard action trigger placed into a different collection, PauseStoryboard action will not work.

Code xaml:

<Window x:Class="控制播放.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Triggers>
        <!--开始事件-->
        <EventTrigger RoutedEvent="Button.Click" SourceName="btn_start">
            <BeginStoryboard Name="beginstoryboard1">
                <Storyboard>
                    <DoubleAnimation Storyboard.TargetName="img" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:6"></DoubleAnimation>
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>

        <!--停止动画-->
        <EventTrigger RoutedEvent="Button.Click" SourceName="btn_pause">
            <PauseStoryboard BeginStoryboardName="beginstoryboard1"></PauseStoryboard>
        </EventTrigger> 
        
        <!--恢复动画-->
        <EventTrigger RoutedEvent="Button.Click" SourceName="btn_resume">
            <ResumeStoryboard BeginStoryboardName="beginstoryboard1"></ResumeStoryboard>
        </EventTrigger>
        
        <!--停止动画-->
        <EventTrigger RoutedEvent="Button.Click" SourceName="btn_stop">
            <StopStoryboard BeginStoryboardName="beginstoryboard1"></StopStoryboard>
        </EventTrigger>

        <!--移除动画-->
        <EventTrigger RoutedEvent="Button.Click" SourceName="btn_remove">
            <RemoveStoryboard BeginStoryboardName="beginstoryboard1"></RemoveStoryboard>
        </EventTrigger>        
    </Window.Triggers>
    
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="3*"></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>   
        
        <Image  Name="img" Source="1.jpg"></Image>
        
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Button Name="btn_start" Content="开始" Margin="10" FontSize="20" Grid.Column="0"></Button>
            <Button Name="btn_pause" Content="暂停" Margin="10" FontSize="20" Grid.Column="1"></Button>
            <Button Name="btn_resume" Content="恢复" Margin="10" FontSize="20" Grid.Column="2"></Button>
            <Button Name="btn_stop" Content="停止" Margin="10" FontSize="20" Grid.Column="3"></Button>
            <Button Name="btn_remove" Content="移除" Margin="10" FontSize="20" Grid.Column="4"></Button>
        </Grid>
    </Grid>
</Window>

Here Insert Picture Description
Analysis: Triggers collection contains the element's (here Window.Triggers collection), using EventTrigger.SourceName associated properties of these event triggers, and as long as the properties SourceName Name property matches to the button, the appropriate triggers will be used on a button. BeginStoryboard action must also be asked to specify the name, so that other triggers BeginStoryboardName attribute specifies the name, connected to the same storyboard, then be controlled.

12, storyboards event.
Here Insert Picture Description
Storyboard event
Completed animation has reached the end
CurrentGlobalSpeedInvalidated rate changes, or animation is suspended, resumed, stopped or moved to a new location.
CurrentStateInvalidated motion has started or ended.
CurrentTimeInvalidated animated clock has moved a step forward, it is to change the animation. When the animation start, stop, or it will lead to the end of the event.
RemoveRequested animation is being removed.

Monitoring the progress of the animation:

If you want to monitor a movie, to use some of the events of the Storyboard. As used herein, is CurrentTimeInvalidated event, each animated clock moves forward will lead the event. When triggered CurrentTimeInvalidated event, the sender is the object Clock (Color class located System.Windows.Media.Animation namespace), you can retrieve the current time through the Clock object. Using the current time TimeSpan object representation, and may retrieve the current schedule, using the current progress value between 0 and 1. FIG.

Background Code:

In the above example, is added as a storyboard CurrentTimeInvalidated event, then put a label control interface (for displaying time) and the ProgressBar (for displaying the progress of a maximum value of 1, the minimum value is 0) control.

private void Storyboard_CurrentTimeInvalidated(object sender, EventArgs e)
{
    Clock storyboardClock = (Clock)sender;
    if (storyboardClock.CurrentProgress == null)
    {
        lblTime.Content = "";
        progressBar1.Value = 0;
    }
    else
    {
        lblTime.Content = storyboardClock.CurrentTime.ToString();
        progressBar1.Value = (double)storyboardClock.CurrentProgress;
    }
}

Here Insert Picture Description
13, animation easing.
One drawback of linear animation, mechanical and generally make people feel unnatural. The secret to improve and create animations become more natural motion is to change the rate of change. Instead of creating change the properties of animation at fixed rates, but in some way be designed according to acceleration or deceleration of the animation, the easiest way to achieve become more natural motion is to use preset easing function (EasingFunction). EasyingFunction property can only accept a single easing function object can not be a combination of different easing function for the same animation.

Code xaml

<Window.Resources>
    <Style TargetType="Button">
        <Style.Triggers>
            <EventTrigger RoutedEvent="Click">
                <EventTrigger.Actions>
                    <BeginStoryboard>
                        <Storyboard Storyboard.TargetProperty="Width">
                            <DoubleAnimation To="300" Duration="0:0:5">
                                <!--使用缓动函数-->
                                <DoubleAnimation.EasingFunction>
                                    <!--设置缓动模式和振荡次数-->
                                    <ElasticEase EasingMode="EaseOut" Oscillations="5"></ElasticEase>
                                </DoubleAnimation.EasingFunction>
                            </DoubleAnimation>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger.Actions>
            </EventTrigger>
        </Style.Triggers>
    </Style>
</Window.Resources>   
<Grid>
     <Button Width="150" Height="50" Content="缓动动画" FontSize="20"></Button>
</Grid>

Here Insert Picture Description
14, easing function class.
Before continuing the analysis of different easing classes, understand the function of easing the application timing is very important. All easing function classes inherit from EasingFunctionBase class and inherits EasingMode property, EasingMode there are three values, namely: EasyIn (in animation began to apply the easing effect), EasyOut (at the end of the animation application easing effect) and EasyInOut (at the beginning and end of the application easing animation). When applying easing function does not change the duration of the animation.
Here Insert Picture Description
15, the custom easing function.
Create custom easing functions generally requires the following steps:

A), a new category, let EasingFunctionBase class inherits from.

b), rewriting EaseInCore () method and CreateInstanceCore When () method.

c), defined dependency properties.

d), by reference.

Background Code (custom class):

public class RandomJitterEase : EasingFunctionBase
{
    //声明一个Random类,用于声明随机数。
    Random rand = new Random();
    /// <summary>
    /// 重写EaseCore方法。 
    /// </summary>
    /// <param name="normalizedTime"></param>
    /// <returns></returns>
    protected override double EaseInCore(double normalizedTime)
    {
        //几乎所有逻辑代码都在EaseInCore方法中运行。该方法接受一个规范化的时间值,本质上是表示动画进度从
        //0到1之间的值,当动画开始时,规范化的时间值是0,它从该点开始增加,直到在动画结束点达到1.
        //在动画运行期间,每次更新动画的值时,WPF都会调用EaseInCore方法,确切的调用频率取决于动画的帧率。
        if (normalizedTime == 1)
        {
            return 1;
        }
        else
        {
            return Math.Abs(normalizedTime - (double)rand.Next(0, 10) / (2010 - Jitter));
        }
    }

    protected override System.Windows.Freezable CreateInstanceCore()
    {
        return new RandomJitterEase();
    }

    //定义一个依赖属性。
    public static readonly DependencyProperty JitterProperty;

    //在静态方法中注册依赖属性。
    static RandomJitterEase()
    {
        JitterProperty = DependencyProperty.Register("Jitter", typeof(int), typeof(RandomJitterEase), new UIPropertyMetadata(1000), new ValidateValueCallback(ValidateJitter));
    }

    public int Jitter
    {
        get { return (int)GetValue(JitterProperty); }
        set { SetValue(JitterProperty, value); }
    }

    //此方法用于判断值。
    private static bool ValidateJitter(object value)
    {
        int jitterValue = (int)value;
        if (jitterValue <= 2000 && jitterValue >= 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

Code xaml:

<Window x:Class="自定义缓动函数.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:自定义缓动函数"
        Title="MainWindow" Height="350" Width="525">
    
    <Window.Triggers>
        <!--事件触发器,窗体加载的Loaded事件。-->
        <EventTrigger RoutedEvent="Window.Loaded">
            <EventTrigger.Actions>
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetName="ellipse1" Storyboard.TargetProperty="(Canvas.Left)" From="0" To="480" Duration="0:0:5"></DoubleAnimation>
                        <DoubleAnimation Storyboard.TargetName="ellipse2" Storyboard.TargetProperty="(Canvas.Left)" From="0" To="480" Duration="0:0:5">
                            <DoubleAnimation.EasingFunction>
                                <!--调用自定义缓动函数类-->
                                <local:RandomJitterEase EasingMode="EaseIn" Jitter="1500"></local:RandomJitterEase>
                            </DoubleAnimation.EasingFunction>
                        </DoubleAnimation>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger.Actions>
        </EventTrigger>
    </Window.Triggers>        
        
    <Canvas ClipToBounds="True">
        <Ellipse Name="ellipse1" Width="25" Height="25" Fill="Red"></Ellipse>
        <Ellipse Margin="0,70,0,0" Name="ellipse2" Width="25" Height="25" Fill="Green"></Ellipse>
    </Canvas>    
</Window>

Here Insert Picture Description
16, WPF animation performance and frame rate.
Typically, the user interface application animation, simply create and configure the correct version of the story of animations and objects. But in other cases, especially when several simultaneous animations, may be more concerned about the performance required. WPF attempts to keep the animation at 60 frames / second, to ensure a smooth smooth animation from start to finish. The lower the frame rate, jitter phenomenon occurs. The higher the frame rate, the higher the occupancy of CPU. Adjusted by TimeLine.DesiredFrameRate property.

Code xaml:

<Window x:Class="动画性能之帧率.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:动画性能之帧率"
        Title="MainWindow" Height="500" Width="525">    
    <Window.Triggers>
        <!--定义一个事件触发器,通过SourceName属性关联button-->
        <EventTrigger RoutedEvent="Button.Click" SourceName="btn_start">
            <EventTrigger.Actions>
                <BeginStoryboard>
                    <!--通过Timeline.DesiredFrameRate属性设置帧速率-->
                    <Storyboard Storyboard.TargetName="ellipse" Timeline.DesiredFrameRate="{Binding ElementName=txtBox1, Path=Text}">
                        <DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" From="0"  To="300" Duration="0:0:10"></DoubleAnimation>
                        <DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" From="0" To="250" Duration="0:0:10"></DoubleAnimation>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger.Actions>
        </EventTrigger>
    </Window.Triggers>    
    
    <Grid ShowGridLines="False">
        <Grid.RowDefinitions>
            <RowDefinition Height="5*"></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>            
        </Grid.RowDefinitions>
        
        <Grid.ColumnDefinitions >
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>  
        
        <Canvas ClipToBounds="True" Grid.Row="0" Grid.ColumnSpan="2" Height="320" Background="Beige">
            <Ellipse Name="ellipse" Fill="Red" Width="10" Height="10"></Ellipse>
        </Canvas>
        
        <Label Grid.Row="1" Content="帧速率:" FontSize="20" HorizontalAlignment="Right" VerticalAlignment="Center"></Label>
        <TextBox Name="txtBox1" Text="1" Width="60" Height="30" FontSize="20" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center"></TextBox>
        <Button  Name="btn_start" Grid.Row="2" Grid.ColumnSpan="2" Width="200" Height="60" Content="点击动画" FontSize="20"></Button>
    </Grid>
</Window>

Here Insert Picture Description

Reprinted from: https://www.cnblogs.com/xiezunxu/articles/8965848.html:

Released four original articles · won praise 1 · views 1333

Guess you like

Origin blog.csdn.net/obf2018/article/details/88944120