【UWP通用应用开发】编辑文本、绘制图形、3D透视效果及绘制时钟实战

  1. 云栖社区>
  2. 博客>
  3. 正文

【UWP通用应用开发】编辑文本、绘制图形、3D透视效果及绘制时钟实战

nomasp 2015-12-14 11:19:00 浏览2272
展开阅读全文

编辑文本及键盘输入

相信大家都会使用TextBox,但如果要让文本在TextBox中换行该怎么做呢?将TextWrapping属性设置为Wrap,将AcceptsReturn属性设置为True就好咯。

PasswordBox很明显就是一个密码框了,和其他的控件相比其有2个特殊之处,一个是其可以用MaxLength来控制最大的长度,一个是用PasswordChanged来捕捉密码的改名。显然比如QQ密码的MaxLength就是16位了,而PasswordChanged可以用来监测比如用户设置的密码和用户名是否相同。

大家在用电脑或者手机输入时偶尔键盘是出来的26字母拼音或是26字母英文亦或是10个数字对吧,那这个是怎么实现的呢?同样也是很简单的噢!直接在TextBox上用InputScope属性就好啦,比如有Default、TelephoneNumber、EmailSmtpAddress、Url、Search、Chat等可以设置。

除了在XAML中设置InputScope属性外,也可以在后台C#文件中设置。

InputScope inputScope = new InputScope();
InputScopeName inputScopeName= new InputScopeName();
inputScopeName.NameValue = InputScopeNameValue.TelephoneNumber;
inputScope.Names.Add(scopeName);
phoneNumberTtBox.InputScope = scope;

在这段代码中,phoneNumberTtBox是TextBox的名字哟,或者也可以简写这段代码的:

phoneNumberTtBox.InputScope = new InputScope() 
{
    Names = {new InputScopeName(InputScopeNameValue.TelephoneNumber)}
};

除此之外,我们还可以给RichEditBox控件设置IsSpellCheckEnabled属性让这个文本控件启用拼写检查。另外值得注意的是TextBox控件的拼写检查只在Windows Phone上启用,在Windows上市禁用的。而文本预测属性在TextBox和RichEditBox以及在Windows和Windows Phone上都是可用的哦,也就是IsTextPredictionEnabled。

情节提要动画与关键帧动画

简单动画示例

因为下面这些Rectangle都是在ItemsControl中的,因为在容器控件中应用主题样式时,其所有的子对象也都会继承下来。

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <ItemsControl Grid.Row="1" x:Name="itemsControlRectangle">             
        <ItemsControl.ItemContainerTransitions>
            <TransitionCollection>
                <EntranceThemeTransition/>
            </TransitionCollection>
        </ItemsControl.ItemContainerTransitions>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapGrid Height="400"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <ItemsControl.Items>
            <Rectangle Fill="Red" Width="100" Height="100" Margin="12"/>
            <Rectangle Fill="Wheat" Width="100" Height="100" Margin="12"/>
            <Rectangle Fill="Yellow" Width="100" Height="100" Margin="12"/>
            <Rectangle Fill="Blue" Width="100" Height="100" Margin="12"/>
            <Rectangle Fill="Green" Width="100" Height="100" Margin="12"/>
            <Rectangle Fill="Gray" Width="100" Height="100" Margin="12"/>
            <Rectangle Fill="White" Width="100" Height="100" Margin="12"/>
            <Rectangle Fill="Gainsboro" Width="100" Height="100" Margin="12"/>
            <Rectangle Fill="Magenta" Width="100" Height="100" Margin="12"/>
            <Rectangle Fill="CadetBlue" Width="100" Height="100" Margin="12"/>
            <Rectangle Fill="NavajoWhite" Width="100" Height="100" Margin="12"/>
            <Rectangle Fill="Khaki" Width="100" Height="100" Margin="12"/>
        </ItemsControl.Items>
    </ItemsControl>
</Grid>

情节提要动画

就像我们前面介绍的定义样式资源一样,我们也可以将动画设为资源。

<Page.Resources>
    <Storyboard x:Name="storyboardRectangle" >
        <DoubleAnimation
            Storyboard.TargetName="rectangle"  
            Storyboard.TargetProperty="Opacity"                
            From="1.0" To="0" Duration="0:0:1"
            AutoReverse="True"
            RepeatBehavior="Forever"/>          
    </Storyboard>
</Page.Resources>

<Grid>
    <Rectangle x:Name="rectangle"       
        Width="200" Height="130" Fill="Blue"/>
</Grid>

在理解这些代码意思之前,还是先让动画跑起来,你可以加上一个Button并设置其Click事件,也可以在MainPage方法下直接写如下代码:

storyboardRectangle.Begin();

运行应用后,Rectangle的透明度就会渐渐的消失而后出现。

在上面这个示例中,我们为Rectangle的Opacity(透明度)属性设置了动画,Storyboard通常存放在

Storyboard.TargetProperty="(rectangle.Fill).(SolidColorBrush.Color)"

如果你已经定义了TargetName属性为rectangle,那么Fill前的rectangle和点都可以去掉。

左右两个括号都是必要的,它表示一个属性的名称。中间的点意味着要先获取第一个括号的属性,也就是设置动画的对应对象,然后进入到其对象模型中,此处是Color。官网上还给出了其它示例:

(UIElement.RenderTransform).(TranslateTransform.X) 应用到RenderTransform上,并创建TranslateTransform的X值的动画

(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color) 应用到Fill上,并在LinearGradientBrush的GradientStop内创建Color的动画,这里方括号内的数字表示索引,表示集合中的一项,索引从0开始

(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X) 应用到RenderTransform上,并创建TranslateTransform

我们还注意到,动画中还有From和To属性,顾名思义,From表示动画的开始值,To表示结束值。

如果没有定义From值,那么动画起始值为该对象属性的当前值。

如果想要设置一个和起始值相对的结束值,建议使用By属性。

动画在这3个属性中至少应该设置一个,否则动画便不会更改值,且这3个属性也无法同时存在。

我们还可以用设置AutoReverse属性为真以使动画才结束后自动进行反向播放,但反向播放完后不会再继续播放。

设置RepeatBehavior属性为“1x”表示动画的播放次数,或者也可以直接设为“Forever”,让其永远播放。

如果动画较多的情况下,我们哈可以设置BeginTime来使不同的动画错开播放。

关键帧动画

什么是关键帧动画?

关键帧动画建立在上文的情节提要动画概念智商,它令动画沿着一条时间线来逐步达到多个目标值,也就是说如果要让上文的Fill属性从Blue变化到Lime之间还可以令其先变化到Red或Orange等。

更为巧妙的是,你可以同时指定不同的属性来制作复杂的动画。

如果稍微会一点Flash,对于关键帧的概念肯定没有问题。

1.线性关键帧

我们为动画设置一个KeyTime来表示间隔的时间戳,例如我们可以设置4个时间戳为:KeyTime=”0:0:0”、”0:0:2”、”0:0:8”、”0:0:9”,可以看到动画在中间部分时跳跃性非常之大。但其动画都是缓慢变化的,因为这是线性的,还有一种另外一种关键帧它会让动画在时间戳上产生突变而不是渐变,这就是离散式关键帧(就像概率论中的离散型和连续型一样)。

2.样条关键帧

其主要通过KeySpline属性来建立过渡,例如KeySpline=”0.1,0.1 0.7.0.8”,这里有两个点,分别对应贝塞尔曲线的第一个控制点和第二个控制点,描述了动画的加速情况。关于贝塞尔曲线,建议大家看看维基百科,在图形化编程中非常常用。

3.缓动关键帧

这种模式就更加高级了,它由多个预定义好的数学公式来控制。以下是的缓动函数列表来源于网络:

BackEase:动画开始在指定路径上运动前稍微收缩动画的运行。
BounceEase:创建回弹效果。
CircleEase:使用圆函数创建加速或减速的动画。
CubicEase:使用函数 f(t) = t3 创建加速或减速的动画。
ElasticEase:创建一个动画,模拟弹簧的来回振荡运动,直到它达到停止状态。
ExponentialEase:使用指数公式创建加速或减速的动画。
PowerEase:使用公式 f(t) = tp 创建加速或减速的动画,其中 p 等于 Power 属性。
QuadraticEase:使用函数 f(t) = t2 创建加速或减速的动画。
QuarticEase:使用函数 f(t) = t4 创建加速或减速的动画。
QuinticEase:使用函数 f(t) = t5 创建加速或减速的动画。
SineEase:使用正弦公式创建加速或减速的动画。

绘制图形

Rectangle

我们开篇先介绍一个之前用过,也是比较简单的Rectangle。简单的矩形就只用定义长和宽了,但如果要有圆角的话呢,用RadiusX和RadiusY就好。那么RadiusX和RadiusY到底是什么呢?看看下图就知道了。

<Rectangle Fill="Yellow" Width="300" Height="200" Stroke="Blue"
                   StrokeThickness="10" RadiusX="80" RadiusY="40"/>

这里写图片描述

和Rectangle类似,Border也可以创建矩形,而且后者还可以有自对象以及会自动调整大小,前者只能有固定的大小哦。

Ellipse

看到这个名字大家应该都知道是什么意思吧,如果要定义成圆的话让Height和Width属性相等即可。

那童鞋们都知道ProgressRing是由6个Ellipse组成的吗,RadioButton也是由2个同心的Ellipse组成的哦。

<Ellipse Fill="Blue" Height="200" Width="350"/>

Polygon

Polygon则显得比较自由,只需要定义出各个顶点,它就会将这些点连接起来。那么我们可能会有疑问,需不需要确定图形的起始点和终点呢?答案是不用的,因为Polygon会自动将终点和起始点连接起来(它会假设图形是闭合的)。

<Polygon Fill="Green" Points="0,0,100,0,100,100,0,100  "/>

这里写图片描述

如果要在后台C#文件中来写的话呢,原本的Point则由PointCollection来定义所有点后添加到一起。

Line

Line的使用也比较简单,但有一点要注意,必须设置好Stroke和StrokeThickness的属性值,否则Line就不会显示出来。原因很简单,因为它是直线。

<Line Stroke="Red" StrokeThickness="10" X1="100" Y1="0" Y2="400" X2="400"/>

这里写图片描述

Path

最后上台的自然是最厉害的啦,先上图。

这里写图片描述

<Path Stroke="Gold"  StrokeThickness="7" 
              Data="M 0,0 C 100,200 50,200 40,150 H 200 V 100 "/>

前两个属性用过多次了,Data却还挺复杂的。这里有3个命令,M、C、H和V。如果按英文来记可能会容易些吧,分别是:Move、Control、Horizontal和Vertical。

那么,重头戏来了,先看图^_^

这里写图片描述

接着上代码。

  <Path Stroke="Black" StrokeThickness="1" Fill="red">
                <Path.Data>
                    <GeometryGroup>           
                        <RectangleGeometry Rect="5,5 180,10" />
                        <RectangleGeometry Rect="5,5 95,180" />  
                        <RectangleGeometry Rect="90,175 95,180"/>   
                        <RectangleGeometry Rect="5,345 180,10" />    
                        <EllipseGeometry            
                            Center="95, 180" RadiusX="20"   
                            RadiusY="30"/>                         
                        <PathGeometry>
                            <PathGeometry.Figures>
                                <PathFigureCollection>
                                    <PathFigure IsClosed="true" StartPoint="50,50">
                                        <PathFigure.Segments>
                                            <PathSegmentCollection>
                                                <BezierSegment Point1="100,180" 
                                                               Point2="125,100" Point3="150,50"/>                                                
                                            </PathSegmentCollection>
                                        </PathFigure.Segments>
                                    </PathFigure>                                     
                                    <PathFigure IsClosed="true" StartPoint="40,310">
                                        <PathFigure.Segments>
                                            <PathSegmentCollection>
                                                <BezierSegment Point1="90,180"
                                                               Point2="115,250" Point3="140,310"/>
                                            </PathSegmentCollection>
                                        </PathFigure.Segments>
                                    </PathFigure>
                                </PathFigureCollection>
                            </PathGeometry.Figures>
                        </PathGeometry>                  
                    </GeometryGroup>
                </Path.Data>
            </Path>  

这张图花了我好久时间呢,希望大家也都会画,虽然作用不大,不过花着玩玩也不错。

这里写图片描述

我在图上大概加了一些标注啦,另外RectangleGeometry的Rect属性有2个值,后者是相对于前者增加的长度哦。

最难的部分是BezierSegment,也就是贝赛斯曲线,其中StartPoint和Point3分别为起点和终点,而Point1和Point2不是路径哟,只是给曲线的一个参考偏移方向。具体大家可以上维基百科看看。

画笔与图像

画笔想必大家都不陌生,这里系统的介绍一下好了。先来介绍纯色画笔。

纯色画笔

最简单的纯色画笔就是已经定义好名字的啦,比如Red和Green这种,据说一共有256种已命名的,所以基本已经够用啦。XAML解析器会自动将这些颜色名称链接到Color结构。

还有就是传说中的十六进制颜色值,它可以定义精确的24位颜色值,其中有8位用于SolidColorBrush。如下代码所示的,alpha=”FF”,红色=”55”,绿色=”00”,蓝色=”88”。

<Rectangle Width="200" Height="100" Fill="#FF550088" />

还有一种称为属性元素语法。具体用法如下,其中Opacity就是透明度咯。

  <Rectangle Width="200" Height="100">
     <Rectangle.Fill>
        <SolidColorBrush Color="Yellow" Opacity="0.3" />
     </Rectangle.Fill>
  </Rectangle>

渐变画笔

除了纯色画笔外,还有渐变画笔。小时候学PhotoShop的时候最喜欢渐变画笔了。

LinearGradientBrush会沿着一条称为渐变轴直线来进行渐变以绘制一个区域。我们还是拿Rectangle来做示例。

   <Rectangle Width="200" Height="100">
            <Rectangle.Fill>
                <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
                    <GradientStop Color="Green" Offset="0.0" x:Name="GradientStop1"/>
                    <GradientStop Color="Blue" Offset="0.25" x:Name="GradientStop2"/>
                    <GradientStop Color="Wheat"  Offset="0.7" x:Name="GradientStop3"/>
                    <GradientStop Color="Yellow" Offset="0.75" x:Name="GradientStop4"/>
                    <GradientStop Color="Gold" Offset="1.0" x:Name="GradientStop5"/>
                </LinearGradientBrush>
            </Rectangle.Fill>
        </Rectangle>

这里写图片描述

通过改变StartPoint和EndPoint的属性值可以创建各种渐变哦,比如垂直和水平方向的渐变,还可以颠倒渐变方向,甚至还可以加快渐变速度呢。

直接添加图片

除了用着两种画笔外,还可以直接将图片添加进来呢。

 <Ellipse Height="100" Width="200">
    <Ellipse.Fill>
        <ImageBrush ImageSource="9327.jpg"/>
    </Ellipse.Fill>
</Ellipse>

效果如下咯,主要是有一张合适的图片啦。

这里写图片描述

既然用到了ImageBrush,那就来看看Image和ImageBrush的区别好了。前者主要用来呈现图像,后者则为其他对象绘制为一个图像。

Stretch属性

对于Image,我们可以来拉伸图像,也就是Stretch属性:

None:图像不经过拉伸。如果源图像比所留给Image的区域大,那么就会被剪切。
Uniform:按照纵横比来缩放图像。
UniformToFill:按照纵横比来填满所有区域,这意味着可能会有一部分不可见。
Fill。因为不保留纵横比而填满屏幕,所以图像部分全部可见,但会产生画面变形(失真)。
具体效果见下图(来源于网络)。

这里写图片描述

Clip属性

用Clip属性可以对图像进行剪裁,Rect属性在上一篇博客中用过许多次,前2个值为起始点的X轴和Y轴坐标,后2个值为终点的X轴和Y轴坐标。

<Image Source="9327.jpg">
    <Image.Clip>
        <RectangleGeometry Rect="10,10,100,100"/>
    </Image.Clip>
</Image>

Image和ImageBrush能处理的图像格式有如下几种:

JPEG XR
图标(ICO)
位图(BMP)
图像交换格式(GIF)
联合图像专家组(JPEG)
可移植网络图像(PNG)
标记图像文件格式(TIEF)

3D透视效果

原图如下:

这里写图片描述

首先是布局代码:

<Grid>
        <Image Name="Image1" Source="ms.png" >            
        </Image>
        <Button Height="50" BorderThickness="2" Click="btnClick1"   Width="100"
             Content="Add X" Margin="34,0,226,40" VerticalAlignment="Bottom" 
                HorizontalAlignment="Stretch"/>
        <Button Height="50" BorderThickness="2" Click="btnClick2"        Width="100"
             Content="Add Y" Margin="146,0,128,37" VerticalAlignment="Bottom" 
                HorizontalAlignment="Stretch" />
        <Button Height="50" BorderThickness="2" Click="btnClick3"            Width="100"
            Content="Add Z" Margin="243,0,31,37" VerticalAlignment="Bottom" 
            HorizontalAlignment="Stretch" />
</Grid>

这里我通过点击按钮让X、Y、Z旋转,当然你也可以设定一个定时器,这样就可以让他一直旋转了。

  public sealed partial class MainPage : Page
    {
        private double x = 0;
        private double y = 0;
        PlaneProjection p = new PlaneProjection();
        public MainPage()
        {
            this.InitializeComponent();
        }

        private void btnClick1(object sender, RoutedEventArgs e)
        {                
            p.RotationX += 9.0;    
            Image1.Projection = p;                                                             
        }
        private void btnClick2(object sender, RoutedEventArgs e)
        {
            p.RotationY += 4.0;
            Image1.Projection = p;
        }
        private void btnClick3(object sender, RoutedEventArgs e)
        {
            p.RotationZ += 2.0;
            Image1.Projection = p;
        }
    }

效果图:

这里写图片描述

这里写图片描述

实战:用UserControl来绘制时钟

首先你需要添加一个User Control文件,如图:

这里写图片描述

整个文件我都列了出来:

<UserControl
    x:Class="App86.MyUserControl1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App86"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" Height="389.396" Width="403.221">

    <UserControl.Resources>
        <Storyboard x:Name="clockStoryboard">
            <DoubleAnimation x:Name="hourAnimation"
            Storyboard.TargetName="hourHandTransform"
            Storyboard.TargetProperty="Angle"
            Duration="12:0:0" RepeatBehavior="Forever" To="360"/>
            <DoubleAnimation x:Name="minuteAnimation"
          Storyboard.TargetName="minuteHandTransform" 
          Storyboard.TargetProperty="Angle"
          Duration="1:0:0" RepeatBehavior="Forever" To="360" />
            <DoubleAnimation x:Name="secondAnimation"
          Storyboard.TargetName="secondHandTransform" 
          Storyboard.TargetProperty="Angle"
          Duration="0:1:0" RepeatBehavior="Forever" To="360" />
        </Storyboard>
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" Background="White" Loaded="SetAndStartClock">
        <Ellipse x:Name="shadowEllipse" Fill="#FF00000A" HorizontalAlignment="Left" Height="330" Margin="36,37,0,0" Stroke="Black" VerticalAlignment="Top" Width="330" Opacity="0.3"/>
        <Ellipse x:Name="outerRimEllipse" HorizontalAlignment="Left" Height="330" Margin="29,31,0,0" Stroke="Black" VerticalAlignment="Top" Width="330">
            <Ellipse.Fill>
                <LinearGradientBrush EndPoint="0.816,0.888" MappingMode="RelativeToBoundingBox" StartPoint="0.184,0.112">
                    <GradientStop Color="Black" Offset="0"/>
                    <GradientStop Color="Silver" Offset="0.731"/>
                </LinearGradientBrush>
            </Ellipse.Fill>
        </Ellipse>
        <Ellipse x:Name="bevelEllipse" HorizontalAlignment="Left" Height="290" Margin="50,51,0,0" Stroke="Black" VerticalAlignment="Top" Width="290">
            <Ellipse.Fill>
                <LinearGradientBrush EndPoint="0.816,0.888" MappingMode="RelativeToBoundingBox" StartPoint="0.184,0.112">
                    <GradientStop Color="#FF2F2F32" Offset="0"/>
                    <GradientStop Color="#FFB6B6B6" Offset="0.731"/>
                    <GradientStop Color="#FFE4E5F4" Offset="1"/>
                </LinearGradientBrush>
            </Ellipse.Fill>
        </Ellipse>
        <Ellipse x:Name="faceEllipse" HorizontalAlignment="Left" Height="270" Margin="60,62,0,0" Stroke="Black" VerticalAlignment="Top" Width="270" Fill="Black"/>
        <Ellipse x:Name="centerEllipse" Fill="Black" HorizontalAlignment="Left" Height="30" Margin="180,182,0,0" Stroke="#FF57F010" VerticalAlignment="Top" Width="30" StrokeThickness="8"/>
        <Rectangle x:Name="secondHand" Fill="#FFF11600" HorizontalAlignment="Left" Height="80" Margin="193,93,0,0" Stroke="Black" VerticalAlignment="Top" Width="5" RenderTransformOrigin="0.45,1.3">
            <Rectangle.RenderTransform>
                <RotateTransform x:Name="secondHandTransform"/>
            </Rectangle.RenderTransform>
        </Rectangle>
        <Rectangle x:Name="minuteHand" Fill="#FF3BF006" HorizontalAlignment="Left" Height="80" Margin="191,92,0,0" Stroke="Black" VerticalAlignment="Top" Width="9" RenderTransformOrigin="0.45,1.3">
            <Rectangle.RenderTransform>
                <RotateTransform x:Name="minuteHandTransform"/>
            </Rectangle.RenderTransform>
        </Rectangle>
        <Rectangle x:Name="hourHand" Fill="#FF20F017" HorizontalAlignment="Left" Height="66" Margin="190,105,0,0" Stroke="Black" VerticalAlignment="Top" Width="11" RenderTransformOrigin="0.45,1.4">
            <Rectangle.RenderTransform>
                <RotateTransform x:Name="hourHandTransform"/>
            </Rectangle.RenderTransform>
        </Rectangle>
    </Grid>
</UserControl>

如你看到的那样,一开始是样式资源,它是一个Storyboard,这里设置了3个指针的走法,都是旋转360度且一直循环。

后面则是相关的Rectangle等,当然了,这些不可能一次性写完,你得慢慢测试才可以。

VS的优势在于你可以拖动控件,以后有机会我再写一篇介绍如何使用Blend的博客,这里就不详细说明了。

在MyUserControl1.xaml.cs文件中,写一个方法:

  private void SetAndStartClock(object sender, RoutedEventArgs routedEventArgs)     
        {                                                           
            System.DateTime currentDate = DateTime.Now;

            double hourangle = (((float)currentDate.Hour) / 12) * 360 + currentDate.Minute / 2;

            double minangle = (((float)currentDate.Minute) / 60) * 360;

            double secangle = (((float)currentDate.Second) / 60) * 360;

            hourAnimation.From = hourangle;

            hourAnimation.To = hourangle + 360;

            minuteAnimation.From = minangle;
            minuteAnimation.To = minangle + 360;

            secondAnimation.From = secangle;
            secondAnimation.To = secangle + 360;

            clockStoryboard.Begin();
        }

首先获取到当前的系统时间,然后根据这个获得各个指针该有的角度,最后让其在动画中旋转,最后开启这个动画就好。因为动画资源已经设定好了,所以也无需再手动控制这些指针如何选择。

好了,这篇博客到此为止,欢迎大家常来我的博客,谢谢。

网友评论

登录后评论
0/500
评论
nomasp
+ 关注