zoukankan      html  css  js  c++  java
  • 好玩的WPF第一弹:窗口抖动+边框阴影效果+倒计时显示文字

    这里写图片描述

    大家一进到博客就应该看到这张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柯于旺 原创文章,如需转载请联系本人。

  • 相关阅读:
    ubuntu14.04 允许root用ssh登录
    MySQL(Navicat)运行.sql文件时报错:[Err] 2006
    Ubuntu14.04 安装git
    Ubuntu14.04下安装redis
    ubuntu apt-get update 失败解决
    检出商品详情中的图片并替换url
    nohup和&后台运行,进程查看及终止
    ubuntu 的chmod 和 chown
    php5.4安装fileinfo扩展
    crontab
  • 原文地址:https://www.cnblogs.com/NoMasp/p/4786004.html
Copyright © 2011-2022 走看看