天天看點

好玩的WPF第一彈:視窗抖動+邊框陰影效果+倒計時顯示文字

原文: 好玩的WPF第一彈:視窗抖動+邊框陰影效果+倒計時顯示文字 版權聲明:轉載請聯系本人,感謝配合!本站位址:http://blog.csdn.net/nomasp https://blog.csdn.net/NoMasp/article/details/46446811

好玩的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動畫後接觸所有綁定,重新整理畫面(不然上一次的文字不消失回和下一次顯示的文字重疊),然後将視窗歸位。

調試和解決問題

那麼至此就來調試一下吧~

好玩的WPF第一彈:視窗抖動+邊框陰影效果+倒計時顯示文字

呀,文字的出現順序反了哦……

想想問題出在這裡呢:

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}"           

那麼最終的效果如下:

好玩的WPF第一彈:視窗抖動+邊框陰影效果+倒計時顯示文字

那麼關于前面的小練習呢,其實解決的障礙在于,一個字元串的字數太多,原本的

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);            

好了,部落格結束啦,我也寫了好久。要源碼的話就在評論裡留郵箱吧……我一個一個發了……

好玩的WPF第一彈:視窗抖動+邊框陰影效果+倒計時顯示文字

感謝您的通路,希望對您有所幫助。 歡迎大家關注、收藏以及評論。

為使本文得到斧正和提問,轉載請注明出處:

http://blog.csdn.net/nomasp