文档章节

好玩的WPF第一弹:窗口抖动+边框阴影效果+倒计时显示文字

NoMasp
 NoMasp
发布于 2015/09/08 21:48
字数 2695
阅读 142
收藏 0

这里写图片描述

大家一进到博客就应该看到这张GIF了吧……好吧,今天不是星期一……

那么就来一起做做这个效果啦!看完记得点赞哦~

新建一个WPF项目

如果新建WPF项目应该不用我说了吧,在C#下面找找就好了。

MainWindow.xaml

在初始的Window下添加如下属性:

x:Name="mainWindow" 
WindowStartupLocation="CenterScreen"   
WindowState="Normal"
WindowStyle="None"
AllowsTransparency="True"
Loaded="Window_Loaded" 
Background="Green"
ResizeMode="NoResize"

分别是什么意思呢?

1、给窗口命名,以后后面会用到的。
2、设置窗口的出现位置,这里设置为屏幕中心,你可以设置其他位置。
3、窗口的大小,我设置为正常,你也可以设置为全屏(Maximized)。
4、窗口的样式,此处设为无标题。
5、窗口的边框,True表示无边框。
6、窗口的加载事件,稍后会补充的。
7、背景颜色,你可以再修改。
8、不可改变窗口尺寸。

然后被预设好的Grid添加一个名字:

<Grid x:Name="mainGrid">
</Grid>

那么界面部分就设好咯。

MainWindow.xaml.cs

你首先需要一个计时器:

// 创建DispatcherTimerr对象 
private DispatcherTimer dTimer = null;

还需要一个前面提到的Window_Loaded事件:

// 窗体加载事件 
private void Window_Loaded(object sender, RoutedEventArgs e)
{
     dTimer = new DispatcherTimer();
     // 时间间隔,比如每两秒刷新一次
     dTimer.Interval = TimeSpan.FromSeconds(1);            
     dTimer.Tick += new EventHandler(timer_Tick);
     dTimer.Start();
}

我们现在应该来写timer_Tick事件了,不过在此之前我们应该决定将要显示什么内容。就以“今天星期一”好了,我们可以创建一个数组,用一个整型变量作为计数器。

我接下来可能比较啰嗦,但只是因为想必看我博客的大多是学生,希望能稍微教教大家思考的方式。

在MainWindow函数前定义全局变量如下:

private int count = 5;
private string[] txt = new string[5] {"今","天","星","期","一"};

计时器方法如下:

private void timer_Tick(object sender,EventArgs e)
{
    if (count == 0)
    {
         dTimer.Stop();
         count = 5;
         dTimer.Start();
    }
    else
    {
         TextWindow textWindow = new TextWindow(this.mainGrid,this.mainWindow);
         textWindow.TxtValue = txt[count-1].ToString();
         textWindow.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
         textWindow.VerticalAlignment = System.Windows.VerticalAlignment.Center;
         this.mainGrid.Children.Add(textWindow);
         count--;
     }                 
}

如果计数器已经为0了,说明数组内的内容已经全部都显示了一遍,那么将其重置后再次开启计数器。

如果不为0,先实例化一个TextWindow窗口,至于这个窗口是做什么的,我们稍后再看。

再随后我们将数组内的字符串赋值给TextWindow类的实例的属性,并设置它的显示位置。

然后将TextWindow窗口添加到mainGrid中,最后将计数器减一。

TextWindow.xaml

新建一个XAML页面大家也会的吧?当然,也可以直接创建UserControl页面。

添加如下代码(我会逐个说明的):

<UserControl x:Class="WpfApplication1.TextWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" RenderTransformOrigin="0.5,0.5" Loaded="UserControl_Loaded" mc:Ignorable="d">

    <UserControl.RenderTransform>
        <TransformGroup>
            <ScaleTransform x:Name="scale" />
        </TransformGroup>              
    </UserControl.RenderTransform>   

    <TextBlock x:Name="textBlock" Width="200" Height="200" FontSize="180" TextAlignment="Center" FontWeight="Bold" FontFamily="宋体" Foreground="Wheat"/>

</UserControl>

1、RenderTransformOrigin的作用,通俗的讲,就是文字即将从哪个角度出来。此处是窗口的中心。

<1,0>是左下角;<0,1>是右上角
<1,1>是左上角;<0.0>是右下角

2、Loaded同样和前面是一样是加载事件

3、TransformGroup,我们稍后会见到

4、TextBlock用来显示文本

TextWindow.xaml.cs

在类TextWindow中设置以下变量,我们前面也看到了,会从MainWindow中传入相关参数:

private Grid grid = null;
private Window window=null;

因为窗口会有抖动效果,所以呢,就需要两个参数来定位它:

//记录初始位置
private double left = 0;
private double top = 0;

Storyboard以前我都是用Blend写的,这里直接刷代码有点难度。

// 创建动画
private Storyboard storyboard = null;

记得设置一个属性来传递文本参数。

// 给UserControl中的文本框赋值 
private string txtValue = string.Empty;
public string TxtValue
{
     get { return txtValue; }
     set { txtValue = value; this.textBlock.Text = txtValue; }
}

如前所述,是时候传递这两个参数了:

 public TextWindow(Grid _grid, Window _window)
{
     InitializeComponent();
     grid = _grid;
     window = _window;
     left = window.Left;
     top = window.Top;
}  

接下来就是这个项目里最重大的过程了,难度也很大,每一个参数都得多次尝试才好。

先写方法:

private void UserControl_Loaded(object sender, RoutedEventArgs e)

方法内定义动画:

// 创建动画对象实例
storyboard = new Storyboard();

文字的缩放过程:

// ScaleX缩放过程
 DoubleAnimation doubleAnimationX = new DoubleAnimation();
doubleAnimationX.Duration = TimeSpan.FromSeconds(0.8);
// 此处将用于文字出现时的缩放
doubleAnimationX.From = 20;
doubleAnimationX.To = 1;
Storyboard.SetTarget(doubleAnimationX, this);
Storyboard.SetTargetProperty(doubleAnimationX, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"));

// ScaleY缩放过程
DoubleAnimation doubleAnimationY = new DoubleAnimation();
doubleAnimationY.Duration = TimeSpan.FromSeconds(0.8);
doubleAnimationY.From = 20;
doubleAnimationY.To = 1;
Storyboard.SetTarget(doubleAnimationY, this);
Storyboard.SetTargetProperty(doubleAnimationY, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"));

试想一下,如果文字堂而皇之的冒了出来不太美观吧?如果有一个由大到小的缩放过程,那不是很赞么?

代码我觉得应该都能看懂,SetTargetProperty属性是在后台代码中设置属性的一个非常好的方式,大家如果会用“资源”,可以类比来思考一下。如果没用过资源也没关系,后面我们会见到。

前面是写的缩放部分,我们还可以添加一个透明度的过渡过程如下:

// Opacity变换动画
DoubleAnimation doubleAnimationO = new DoubleAnimation();
doubleAnimationO.Duration = TimeSpan.FromSeconds(0.8);
// 文字的透明度
doubleAnimationO.From = 0;
doubleAnimationO.To = 1; 
Storyboard.SetTarget(doubleAnimationO, this);
Storyboard.SetTargetProperty(doubleAnimationO, new PropertyPath("(Opacity)"));

最终切记将这三个实例添加到storyboard中。

storyboard.Children.Add(doubleAnimationX);
storyboard.Children.Add(doubleAnimationY);
storyboard.Children.Add(doubleAnimationO);

窗口抖动效果如下:

            // 窗口抖动效果
            DoubleAnimation doubleAnimationL1 = new DoubleAnimation();
            doubleAnimationL1.BeginTime = TimeSpan.FromSeconds(0.6);
            doubleAnimationL1.Duration = TimeSpan.FromSeconds(0.2);
            doubleAnimationL1.From = window.Left;
            doubleAnimationL1.To = window.Left - 12;
            doubleAnimationL1.EasingFunction = new BounceEase() { Bounces = 12, EasingMode = EasingMode.EaseInOut };
            Storyboard.SetTarget(doubleAnimationL1, window);
            Storyboard.SetTargetProperty(doubleAnimationL1, new PropertyPath("(Left)"));

            DoubleAnimation doubleAnimationL2 = new DoubleAnimation();
            doubleAnimationL2.BeginTime = TimeSpan.FromSeconds(0.7);
            doubleAnimationL2.Duration = TimeSpan.FromSeconds(0.2);
            doubleAnimationL2.From = window.Left;
            doubleAnimationL2.To = window.Left + 12;
            doubleAnimationL2.EasingFunction = new BounceEase() { Bounces = 12, EasingMode = EasingMode.EaseInOut };
            Storyboard.SetTarget(doubleAnimationL2, window);
            Storyboard.SetTargetProperty(doubleAnimationL2, new PropertyPath("(Left)")); 

            DoubleAnimation doubleAnimationT1 = new DoubleAnimation();
            doubleAnimationT1.BeginTime = TimeSpan.FromSeconds(0.6);
            doubleAnimationT1.Duration = TimeSpan.FromSeconds(0.2);
            doubleAnimationT1.From = window.Top;
            doubleAnimationT1.To = window.Top + 12; ;
            doubleAnimationT1.EasingFunction = new BounceEase() { Bounces = 12, EasingMode = EasingMode.EaseInOut };
            Storyboard.SetTarget(doubleAnimationT1, window);
            Storyboard.SetTargetProperty(doubleAnimationT1, new PropertyPath("(Top)"));

            DoubleAnimation doubleAnimationT2 = new DoubleAnimation();
            doubleAnimationT2.BeginTime = TimeSpan.FromSeconds(0.7);
            doubleAnimationT2.Duration = TimeSpan.FromSeconds(0.2);
            doubleAnimationT2.From = window.Top;
            doubleAnimationT2.To = window.Top - 12;
            doubleAnimationT2.EasingFunction = new BounceEase() { Bounces = 12, EasingMode = EasingMode.EaseInOut };
            Storyboard.SetTarget(doubleAnimationT2, window);
            Storyboard.SetTargetProperty(doubleAnimationT2, new PropertyPath("(Top)"));

和上面的缩放和透明度一样,添加这些属性到storyboard中。

storyboard.Children.Add(doubleAnimationL1);
storyboard.Children.Add(doubleAnimationL2);
storyboard.Children.Add(doubleAnimationT1);
storyboard.Children.Add(doubleAnimationT2);

最后就是注册事件咯:

storyboard.Completed += new EventHandler(storyboard_Completed);
storyboard.Begin(this, true);

至此该方法就完成了,然后就开始新的storyboard_Completed方法了。

 private void storyboard_Completed(object sender, EventArgs e)
{
     // 解除绑定 
     storyboard.Remove(this);
     // 解除TextWindow窗口 
     storyboard.Children.Clear();
     grid.Children.Clear();
     // 恢复窗体初始位置
     window.Left = left;
     window.Top = top;
}

这个方法所做的事情简单的说,就是在完成一个storyboard动画后接触所有绑定,刷新画面(不然上一次的文字不消失回和下一次显示的文字重叠),然后将窗口归位。

调试和解决问题

那么至此就来调试一下吧~

这里写图片描述

呀,文字的出现顺序反了哦……

想想问题出在这里呢:

private string[] txt = new string[5] {"今","天","星","期","一"};
textWindow.TxtValue = txt[count-1].ToString();

我们首先将数组最后一个打印出来了,然后依次第四个、第三个……

要么将打印顺序改变,要么定义数组的时候反向定义,但这两种方式都不人性化。比如说我们可能要让用户输入数组内容,总不好让用户反向输入吧?

所以我们中间插入一个方法,来讲数组逆序输出。

        static string[] ReverseStringArray(string[] str)
        {
            int length = str.Length;
            string[] newStr = new string[length];
            for (int i = 0; i < length; i++)
            {
                newStr[i] = str[length - i - 1];
            }
            return newStr;
        }

然后在MainWindow函数中执行该方法:

txt= ReverseStringArray(txt);

调试一下果然奏效~

不过还有一种更加简单的方式,C#的自带方法:

Array.Reverse(txt);

还可能你会遇到这个问题:不就是“今天星期一”五个字嘛,至于让每个字弄成一个字符串然后组成数组嘛,直接上字符串不行?

当然可以:

private string txt = "今天星期一";

那么逆序的问题怎么解决?

        static string ReverseString(string str)
        {
            int length = str.Length;
            StringBuilder stringB = new StringBuilder(length);
            for (int i = length - 1; i >= 0; i--)
                stringB.Append(str[i]);
            return stringB.ToString();
        }
txt = ReverseString(txt);

字符串本身也是数组,只不过是由字符组成的。

如果读者是初学者的话,我也来证明一下吧。

还记得这个么?

textWindow.TxtValue = txt[count - 1].ToString();

最后无论是字符还是字符串我都调用了ToString()方法来转换成字符,但如果txt是字符串,而把.ToString()去掉的话就会报错了,因为它不是字符串。

好了,下面来给大家看看另一种取出字符串中字符的方法:

textWindow.TxtValue = txt.ElementAt(count - 1).ToString();

这个真的非常好用。

So……到此为止了,和开篇中的GIF效果一模一样了。

那么给大家留一个小练习,如果txt是一个字符串数组呢?

private string[] txt = new string[5] { "欢迎访问我的博客", "再次欢迎访问我的博客",
 "觉得不错的话就点个赞呗", "你还可以在下面评论", "也可以给我发邮件" };

需要每次都打印出来一句话,而非一个字,该怎么做?

文章结尾我会给出思路,大家不妨在看下文前先试试。

App.xaml

好了,标题中的窗口抖动和倒计时显示文字都有了。那么边框呢?现在虽然是无边框了,但总感觉不那么精致,怎样让它有阴影效果呢?

那么,打开App.xaml,添加如下资源样式就好了。

    <Style x:Key="window_style" TargetType="{x:Type Window}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Window}">
                        <Grid Margin="10">
                            <Rectangle Fill="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"  
                               RadiusX="5" RadiusY="5">
                                <Rectangle.Effect>
                                    <DropShadowEffect BlurRadius="10" ShadowDepth="0"/>
                                </Rectangle.Effect>
                            </Rectangle>
                            <Border Background="{TemplateBinding Background}"   
                                    BorderBrush="{TemplateBinding BorderBrush}"  
                                    BorderThickness="{TemplateBinding BorderThickness}"  
                                    Padding="{TemplateBinding Margin}"  
                                    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"        
                                    CornerRadius="5">
                                <ContentPresenter />
                            </Border>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

最后在MainWindow.xaml下的Window中添加该资源:

Style="{StaticResource window_style}"

那么最终的效果如下:

这里写图片描述

那么关于前面的小练习呢,其实解决的障碍在于,一个字符串的字数太多,原本的
TextBlock的200宽度已经不能满足了,于是乎,干脆删了它:

<TextBlock x:Name="textBlock" Height="200" 
               FontSize="80" TextAlignment="Center" FontWeight="Bold" 
               FontFamily="宋体"  Foreground="Wheat"/>

当然了,字体也要调小一点。

可是这样并不完美,因为有这么多字,一秒钟的时间并不能看完吧。所以还得修改一下比较好,我将我修改过的地方贴出来……

doubleAnimationX.Duration = TimeSpan.FromSeconds(3);
doubleAnimationX.From = 15;

doubleAnimationY.Duration = TimeSpan.FromSeconds(3);
doubleAnimationY.From = 15;

doubleAnimationO.Duration = TimeSpan.FromSeconds(3);
dTimer.Interval = TimeSpan.FromSeconds(5); 

好了,博客结束啦,我也写了好久。要源码的话就在评论里留邮箱吧……我一个一个发了……

这里写图片描述



感谢您的访问,希望对您有所帮助。 欢迎大家关注、收藏以及评论。


为使本文得到斧正和提问,转载请注明出处:
http://blog.csdn.net/nomasp


版权声明:本文为 NoMasp柯于旺 原创文章,未经许可严禁转载!欢迎访问我的博客:http://blog.csdn.net/nomasp

本文转载自:http://blog.csdn.net/nomasp/article/details/46446811

NoMasp
粉丝 7
博文 334
码字总数 0
作品 0
镇江
程序员
私信 提问
使用WPF来创建 Metro UI

亲们用过 Zune 嘛? 应该吧,可是 4.7.1404.0 版本才是我的第一次Zune体验,因为这个版本有非常显著的变化: 支持Windows Phone 7 并 整合了 Windows Live Essentials 2011. 当我第一次运行它...

王振威
2012/11/27
6.1K
4
使用WPF来创建 Metro UI

当我第一次运行Zune时,我为这些美丽的UI所折服。当时就说这肯定不是用WPF做的,因为这些字体是如此的清晰而且UI反映的也非常快速。。而且我从维基百科上也了解到Zune的第一个版本是2006年发...

junwong
2012/06/10
1.6W
14
javascript范例宝典

第1章 窗口/框架与导航条设计 1 1.1 弹出窗口控制 2 实例001 打开新窗口显示广告信息 2 实例002 定时打开窗口 4 实例003 通过按钮创建窗口 5 实例004 自动关闭的广告窗口 6 实例005 控制弹出...

GZhiDao
2016/07/21
55
1
HTML+CSS 模仿Windows 7 桌面效果

前一阵在园子里看到一篇文章《使用css3仿造window7的开始菜单》,文中仅使用CSS3 实现了Windows 7 开始菜单的动态效果,很久以来一直被WPF/Silverlight 山上的风景所吸引,未成想其他的山也同...

junwong
2012/03/09
1.4K
0
用最短的CSS样式,勾勒大数据演示屏

先看案例效果,再看心得与总结 前段时间撸了一个演示平台,输入任一手机号,可追踪其行迹,类似《人民的名义》追踪丁义珍的效果,很明显涉密了,因此特意克隆了一个,并把后端阉割了,目前查...

maodayeyeye
2018/06/11
0
0

没有更多内容

加载失败,请刷新页面

加载更多

传三星二代可折叠手机2月份上市 售价1400美元

  原标题:Samsung Galaxy Z Flip will reportedly go on sale in February for $1,400   网易科技讯,1 月 28 日消息,据外媒报道,知情人透露,三星正在研发的第二代可折叠智能手机 Ga...

水果黄瓜
23分钟前
3
0
每天AC系列(六):有效的括号

1 题目 LeetCode第20题,这题比较简单,匹配括号. 2 栈 这是栈的典型应用,括号匹配,当然不需要直接使用栈,使用一个StringBuilder即可: if(s.isEmpty()) return true;char a = s.charAt(0);...

Blueeeeeee
今天
27
0
Spring AOP-06-切入点类型

切入点是匹配连接点的拦截规则。之前使用的是注解@Pointcut,该注解是AspectJ中的。除了这个注解之外,Spring也提供了其他一些切入点类型: • 静态方法切入点StaticMethodMatcherPointcut •...

moon888
昨天
90
0
Class Loaders in Java

1. Introduction to Class Loaders Class loaders are responsible for loading Java classes during runtime dynamically to the JVM (Java Virtual Machine). Also, they are part of the ......

Ciet
昨天
96
0
以Lazada为例,看电商系统架构演进

什么是Lazada? Lazada 2012年成立于新加坡,是东南亚第一电商,2016年阿里投资10亿美金,2017年完成对lazada的收购。 业务模式上Lazada更偏重自营,类似于亚马逊,自建仓储和为商家提供服务...

春哥大魔王的博客
昨天
62
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部