《深入浅出WPF》学习笔记之绘图和动画

绘图

通过WPF绘制的图形都是矢量图,可以通过Design或Blend工具绘制原型图然后导出XAML再在Visual Studio中调整。绘图可以在任意布局控件中完成,常用的为Canvas和Grid,WPF会自动根据容器的不同计算图形坐标。WPF基本图像都继承自Shape类。

Line,绘制直线,通过设置起始点坐标和终止点坐标确定直线的位置,可以设置直线的样式,颜色,粗细等。通过设置Stroke属性设置图形的颜色,Stroke属性为Brush类型,Brush为抽象类。WPF系统包含丰富的Brush子类,SolidColorBrush,LinearGradientBrush(线性渐变画刷),RadialGradientBrush(径向渐变画刷),ImageBrush(图像填充),DrawingBrush(使用矢量图和位图作为填充),VisualBrush,使用可视化元素填充,可以当做可视化元素的截图。

Rectangle,绘制矩形,可以设置边框颜色,边框大小和填充,填充Fill同Stroke一样也为Brush类型。

Ellipse,绘制椭圆,使用方法与矩形没有什么区别,当width和height相同时即为正圆。

Path,路径是WPF绘图中最强大的工具,它即可以替代其他几种图形,也可以形成更复杂的图形。路径的重要属性是Data,通过设置Data来确定具体的图形。Data的类型为Geometry,同Brush一样,Geometry为抽象类,WPF已经准备了若干继承类,包括LineGeometry,RectangleGeometry,EllipseGeometry,PathGeometry,StreamGeometry(轻量级PathGeometry),CombinedGeometry,GeometryGroup。Geometry类与Shape类不同,Geometry不能作为单独元素使用,只能依附在其他图形上。PathGeometry可以绘制复杂的几何图形,其Figures可以容纳多个PathFigure对象,而PathFigure的Segments属性可以容纳多个PathSegment对象,通过PathSegment组合成复杂图形。同样的PathSegment为抽象类,继承类包括LineSegment,ArcSegment,BezierSegment,QuadraticBezierSegment,PolyBezierSegment,PolyQuadraticBezierSegment。所有这些Segment都没有起点,起点就是上一个Segment的终点,第一个Segment的起点为PathFigure的StartPoint。Path的示例代码:

        <Path Stroke="Blue">
            <Path.Data>
                <PathGeometry>
                    <PathFigure StartPoint="0,0">
                        <LineSegment Point="0,10"></LineSegment>
                        <LineSegment Point="10,50"></LineSegment>
                        <LineSegment Point="20,40"></LineSegment>
                        <ArcSegment Point="40,80" Size="25,50"></ArcSegment>
                        <BezierSegment Point1="20,20" Point2="30,30" Point3="40,40"></BezierSegment>
                    </PathFigure>
                </PathGeometry>
            </Path.Data>
        </Path>

GeometryGroup也是Geometry的一个继承类,它最大的特点是可以将一组Geometry组合到一起。

路径标记语法

Path如此强大,可以使我们随心所欲的绘制图形,但其缺点也不容忽视,就是其标签语法国语繁琐,如果图形过于复杂则会生成大量标签。通过使用路径标记语法可以极大的简化Path的描述,路径标记语法实际上就是各种线段的简记法,比如<LineSegment Point="10,50"></LineSegment>可以简记为L 10,50。使用路径标记语法一般分三步:移动至起点,绘图,闭合图形。路径标记语法不区分大小写,使用两个double表示一个点,中间用逗号或空格分隔,为避免混淆,常使用逗号分隔。

可以通过设置UIElement的Clip属性对控件进行裁剪,Clip类型即为Geometry。

图形的效果与滤镜

UIElement中BitmapEffect和Effect的两个属性用来给元素添加效果,BitmapEffect使用CPU计算资源,现在已经被遗弃。Effect属性使用GPU资源。BitmapEffect简单易用,它的类型为BitmapEffect类,是一个抽象类,WPF为我们实现了若干子类,通过设置这些子类的属性来调整效果。示例代码:

        <Button Width="120" Height="30">
            <Button.BitmapEffect>
                <DropShadowBitmapEffect ShadowDepth="5" Opacity="0.7" Direction="-45"></DropShadowBitmapEffect>
            </Button.BitmapEffect>
        </Button>

Effect属性的类型为Effect,同样为抽象类,WPF为我们实现了3个子类,BlurEffect,用来实现模糊效果。DropShadowEffect,用来实现投影效果。ShaderEffect,着色器效果,抽象类,它是留给滤镜插件开发人员的抽象接口。

元素的变形

控制变形的属性有两个RenderTransform和LayoutTransform,他们的数据类型都是Transform抽象类。Transform抽象类的派生类有如下一些,MatrixTransform,RotateTransform,ScaleTransform,SkewTransform,TranslateTransform,TransformGroup。呈现变形(RenderTransform)只改变元素的呈现位置,不会对布局系统造成影响,以减少资源占用,当使用动画时一定要使用呈现变形。注意控件呈现在哪里就在哪里提供交互。示例代码:

        <Button Width="120" Height="30" Click="Button_Click">
            <Button.RenderTransform>
                <TranslateTransform X="50" Y="50"></TranslateTransform>
            </Button.RenderTransform>
        </Button>

布局变形(LayoutTransform)会影响窗体的布局,导致布局重新计算,这会影响程序性能,因此布局变形只用在静态变形上,不会用于动画。

动画

可以用来制作动画的属性一定是依赖属性,动画的基类为Timeline。简单动画由一个元素就可以完成,简单动画使用AnimationTimeline实现。复杂动画由多个元素和多个动画协同完成,复杂动画使用StoryBoard实现。

通过AnimationTimeline可以针对一个UI元素实现简单动画,AnimationTimeline为抽象类,WPF动画系统实现了常用数据类型的子类,如DoubleAnimationBase、ByteAnimationBase等。这种数据类型并以Base做后缀的类为抽象基类,每种数据类型的抽象基类又派生出3种具体动画,分别为简单动画、关键帧动画、沿路径运动的动画。注意并不是所有的数据类型都能派生出这3种类型的动画。DoubleAnimationBase派生出DoubleAnimation(简单动画),DoubleAnimationUsingKeyFrames(关键帧动画),DoubleAnimationUsingPath(沿路径运动动画)。简单动画由变化时间Duration、变化起点From、变化幅度By、变化终点To四要素构成,如果同时设置To和By,By将被忽略。示例代码:

        <Button x:Name="button" Width="120" Height="30" Click="Button_Click">
            <Button.BitmapEffect>
                <DropShadowBitmapEffect ShadowDepth="5" Opacity="0.7" Direction="-45"></DropShadowBitmapEffect>
            </Button.BitmapEffect>
            <Button.RenderTransform>
                <TranslateTransform X="0" Y="0"></TranslateTransform>
            </Button.RenderTransform>
        </Button>

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            DoubleAnimation dax = new DoubleAnimation();
            DoubleAnimation day = new DoubleAnimation();
            dax.From = 0;
            day.From = 0;

            dax.By = 100;
            day.By = 100;

            dax.Duration = new Duration(TimeSpan.FromMilliseconds(500));
            day.Duration = new Duration(TimeSpan.FromMilliseconds(500));

            button.RenderTransform.BeginAnimation(TranslateTransform.XProperty, dax);
            button.RenderTransform.BeginAnimation(TranslateTransform.YProperty, day);
        }

通过设置动画的高级控制属性可以是动画效果更逼真,如设置加速度、减速度、循环模式,渐进式缓冲等。

关键帧动画

通过定义关键帧控制动画的执行效果,每个帧都包含帧所在的时间和值,关键帧动画至少包含一个帧作为动画的结束位置。关键帧动画包含KeyFrames属性用来包含关键帧,可以向其中添加不同类型的关键帧,最简单的关键帧为LinearDoubleKeyFrame。示例代码:

        <Button x:Name="button" Width="120" Click="button_Click" HorizontalAlignment="Left">
            <Button.RenderTransform>
                <TranslateTransform X="0" Y="0"></TranslateTransform>
            </Button.RenderTransform>
            Button</Button>

        private void button_Click(object sender, RoutedEventArgs e)
        {
            DoubleAnimationUsingKeyFrames dakf = new DoubleAnimationUsingKeyFrames();
            dakf.Duration = new Duration(TimeSpan.FromSeconds(1));

            LinearDoubleKeyFrame kf1 = new LinearDoubleKeyFrame();
            kf1.KeyTime = KeyTime.FromPercent(0.2);
            kf1.Value = 50;

            LinearDoubleKeyFrame kf2 = new LinearDoubleKeyFrame();
            kf2.KeyTime = KeyTime.FromPercent(0.4);
            kf2.Value = 100;

            LinearDoubleKeyFrame kf3 = new LinearDoubleKeyFrame();
            kf3.KeyTime = KeyTime.FromPercent(0.8);
            kf3.Value = 150;

            dakf.KeyFrames.Add(kf1);
            dakf.KeyFrames.Add(kf2);
            dakf.KeyFrames.Add(kf3);

            button.RenderTransform.BeginAnimation(TranslateTransform.XProperty, dakf);
        }

路径动画

控制动画沿指定路径移动,路径动画的Source属性用来控制关注路径的X轴的变化还是Y轴的变化还是曲线切线方向变化。示例代码:

    <Grid x:Name="layoutRoot">
        <Grid.Resources>
            <PathGeometry x:Key="path" Figures="M 0,150 C300,-100 300,400 600,120"></PathGeometry>
        </Grid.Resources>
        <Button x:Name="button" HorizontalAlignment="Left" VerticalAlignment="Top" Height="20" Click="button_Click">
            <Button.RenderTransform>
                <TranslateTransform X="0" Y="0"></TranslateTransform>
            </Button.RenderTransform>
            Button</Button>
    </Grid>

        private void button_Click(object sender, RoutedEventArgs e)
        {
            PathGeometry path = layoutRoot.FindResource("path") as PathGeometry;
            DoubleAnimationUsingPath daupx = new DoubleAnimationUsingPath();
            DoubleAnimationUsingPath daupy = new DoubleAnimationUsingPath();

            daupx.Duration = new Duration(TimeSpan.FromSeconds(1));
            daupx.PathGeometry = path;
            daupx.Source = PathAnimationSource.X;

            daupy.Duration = new Duration(TimeSpan.FromSeconds(1));
            daupy.PathGeometry = path;
            daupy.Source = PathAnimationSource.Y;

            button.RenderTransform.BeginAnimation(TranslateTransform.XProperty, daupx);
            button.RenderTransform.BeginAnimation(TranslateTransform.YProperty, daupy);
        }

场景(Storyboard)

场景就是并行执行的一组动画,XAML代码示例:

    <Grid>
        <Button VerticalAlignment="Top" HorizontalAlignment="Left">
            <Button.RenderTransform>
                <TranslateTransform x:Name="tt" X="0" Y="0"></TranslateTransform>
            </Button.RenderTransform>
            <Button.Triggers>
                <EventTrigger RoutedEvent="Button.Loaded">
                    <BeginStoryboard>
                        <Storyboard Duration="0:0:1">
                            <DoubleAnimation Duration="0:0:1" To="200" Storyboard.TargetName="tt" Storyboard.TargetProperty="X"></DoubleAnimation>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </Button.Triggers>
            Button</Button>
    </Grid>

C#代码示例:

    <Grid>
        <Button VerticalAlignment="Top" HorizontalAlignment="Left" Click="Button_Click">
            <Button.RenderTransform>
                <TranslateTransform x:Name="tt" X="0" Y="0"></TranslateTransform>
            </Button.RenderTransform>

            Button</Button>
    </Grid>

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            DoubleAnimation da = new DoubleAnimation();
            da.To = 200;
            da.Duration = new Duration(TimeSpan.FromSeconds(1));

            Storyboard sb = new Storyboard();
            sb.Duration = new Duration(TimeSpan.FromSeconds(1));
            Storyboard.SetTargetName(da, "tt");
            Storyboard.SetTargetProperty(da, new PropertyPath(TranslateTransform.XProperty));

            sb.Children.Add(da);
            sb.RepeatBehavior = RepeatBehavior.Forever;
            sb.Begin(this);
        }
时间: 2024-11-06 17:53:18

《深入浅出WPF》学习笔记之绘图和动画的相关文章

WPF学习笔记(2)——动画效果按钮变长

说明(2017-6-12 11:26:48): 1. 视频教程里是把一个按钮点击一下,慢慢变长: 注意几个方面: (1)RoutedEvent="Button.Click",这里面要用Button,是属性名,而不是name名button1. (2)这些东西要写在成对的标签里面,比如这个是<Button></button>,如果是单个的那种标签,可以拆分成对,比如下面我自己的<MediaElement/>本身是一个单独标签,可以手动拆分成<Med

《深入浅出WPF》笔记——绘画与动画

<深入浅出WPF>笔记——绘画与动画 本篇将记录一下如何在WPF中绘画和设计动画,这方面一直都不是VS的强项,然而它有一套利器Blend:这方面也不是我的优势,幸好我有博客园,能记录一下学习的过程.在本记录中,为了更好的理解绘画与动画,多数的例子还是在VS里面敲出来的.好了,不废话了,现在开始. 一.WPF绘画 1.1基本图形 在WPF中可以绘制矢量图,不会随窗口或图型的放大或缩小出现锯齿或变形,除此之外,XAML绘制出来的图有个好处就是便于修改,当图不符合要求的时间,通常改某些属性就可以完成

R语言学习笔记2——绘图

R语言提供了非常强大的图形绘制功能.下面来看一个例子: > dose <- c(20, 30, 40, 45, 60)> drugA <- c(16, 20, 27, 40, 60)> drugB <- c(15, 18, 25, 31, 40) > plot(dose, drugA, type="b") > plot(dose, drugB, type="b") 该例中,我们引入了R语言中第一个绘图函数plot.pl

WPF学习笔记4&mdash;&mdash;Layout之2

下面简单介绍常见的面板. 一.Grid 1.Grid关于调整行列距离有三种方法:绝对大小,自动大小,比例大小.如下: <ColumnDefinition Width="100"></ColumnDefinition> <ColumnDefinition Width="Auto"></ColumnDefinition> <ColumnDefinition Width="*"></Co

WPF学习笔记2&mdash;&mdash;XAML之2

三.事件处理程序与代码隐藏 例如,为一个Page添加一个Button控件,并为该Button添加事件名称Button_Click: <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ExampleNamespace.ExampleP

WPF学习笔记系列之一 (布局详情)

布局:StackPanel  栈布局:控件不会拐弯且多出的不再显示.DockPanel   停靠布局 吸在上边下边或左右.WrapPanel    环绕布局   一行控件会拐弯Canvas  进行基于坐标的布局 Grid中若不指定Grid.Row属性及Grid.Column则默认为:0行,0列.RowDefinitions ColumnDefinitions ShowGridLines=true <ColumnDefinition Width="100"></Colu

【WPF学习笔记】之 System.Exception 对象名 &#39;XXXX&#39; 无效。

我在运行vs时候发现项目报错,如下图: 报Exception错误,对象名"XXXXXX"无效. 经过调查得知,因为连接数据库的库名写错了,如下: 对应正确数据库的库名: 把库名改正确,问题就解决了. [WPF学习笔记]之 System.Exception 对象名 'XXXX' 无效.

【WPF学习笔记】之点登录按钮时判断用户名密码进行登录:动画系列之(二)

...... 承接动画系列之(一)的代码: 再添加登录按钮代码进行登录,验证用户名和密码在数据库是否正确. 直接上代码: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Do

【WPF学习笔记】之如何把数据库里的值读取出来然后显示在页面上:动画系列之(六)

...... 承接系列五 上一节讲了,已经把数据保存到数据库并且删除数据,本讲是把已经存在的数据从数据库里读取出来,显示在页面上. 主页面后台代码: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using Sys