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

大家一進到部落格就應該看到這張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