好玩的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


时间: 2024-10-14 14:02:08

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

好玩的WPF第二弹:电子表字体显示时间+多彩呼吸灯特效button

我们先来看看Quartz MS字体动态显示系统时间的效果,难度相较于上一篇也要简单很多. 首先是定义一个TextBlock例如以下. <Grid> <TextBlock Name="tBlockTime" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="68" Foreground="Green"/>

好玩的WPF第二弹:电子表字体+显示系统时间

效果呢就是这么个效果,难度相较于上一篇也要简单许多. 首先是定义一个TextBlock如下. <Grid> <TextBlock Name="tBlockTime" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="68" Foreground="Green"/> </Grid> 后台代码如

[WPF疑难] 模式窗口被隐藏后重新显示时变成了非模式窗口

原文:[WPF疑难] 模式窗口被隐藏后重新显示时变成了非模式窗口 [WPF疑难] 模式窗口被隐藏后重新显示时变成了非模式窗口 周银辉 现象: 大家可以试试下面这个很有趣但会带来Defect的现象:当我们将子窗口按照ShowDialog()的方式显示出来的时候,很明显该窗口是模式化的(即主窗口等待该窗口的返回,主窗口将不相应用户输入),但如果此时将子窗口的Visibility设置成Visibility.Hidden来隐藏窗口,然后再将Visibility设置成Visibility.Visible来

WPF window 子窗口反馈效果(抖动/阴影渐变)

原文:WPF window 子窗口反馈效果(抖动/阴影渐变) 当设置了owner的子窗口显示后,点击子窗口外部,需要一种反馈机制(反馈动画). 实现: 1.触发源 每次点击子窗口外部,即母窗口时,事件捕捉如下 HwndSource hwndSource = PresentationSource.FromVisual(this.Owner) as HwndSource;//窗口过程 hwndSource?.AddHook(WndProc); 也可以调用WindowInteropHelper,获取母

C# WPF 设置窗口无边框

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" AllowsTransparency="True" WindowStyle="None" > AllowsTransparency="True&quo

好玩的WPF第三弹:颤抖吧,地球!消失吧,地球!

我承认这一篇比较标题党,不过下面这个GIF貌似也和适合这个标题嘛. (画质比较烂是因为CSDN的博客图片限制在2M,所以我设置的是20帧,时间也很短,大家可以自己把项目拷回去慢慢看) 这个最终设计出来的样式: 中间的小圆点是一个Button,外面是一个经过切割的Grid,Grid里面还有一个Image. 其中在加上Image(地球图片)之前,Button还是很大的,所以给他设计了渐变色. <Button Padding="20" Foreground="White&qu

WPF: WpfWindowToolkit 一个窗口操作库的介绍

在 XAML 应用的开发过程中,使用MVVM 框架能够极大地提高软件的可测试性.可维护性.MVVM的核心思想是关注点分离,使得业务逻辑从 View 中分离出来到 ViewModel 以及 Model 中,从逻辑上来讲,这也是业务逻辑应该处的位置. 具体来说,借助于数据绑定 (Data Binding) 以及命令 (Command) 等这些XAML 平台自身所提供的机制,使得 MVVM 在 XAML 平台上很容易实现.但是,如果你对 XAML 应用开发以及 MVVM 的使用有一定的经验,你会发现,

你好,博客园!!第一弹~局域网下的简易聊天室,socket与多线程简结

发觉博客园里面关于这些基本知识点的详细内容真是应有尽有,所以这里的随笔就不再重复了,就积累一下简单的用法-- 1.Socket 最近学网络编程,也就是Socket,套接字,一个用来建立链接传输数据的工具. 数据传输发生在"客户端"与"服务端"之间,下面是一种建立连接传输数据的简单方法: (1)客户端 1 try{ 2 //服务端ip 3 String ip = "127.0.0.1"; 4 //服务器端口 5 int port = 5000; 6

AndroidStudio使用教程(第一弹)

AndroidStudio使用教程(第一弹) Android Studio是一套面世不久的IDE(即集成开发环境),免费向谷歌及Android的开发人员发放.Android Studio以IntelliJ IDEA为基础, 旨在取代Eclipse和ADT(Android开发者工具)为开发者提供更好的开发工具. 运行相应速度.智能提示.布局文件适时多屏预览等都比Eclipse要强,但也不能说全部都是有点现在Studio中无法在一个窗口管理多个Project, 每个Project都要打开一个窗口,或