正如上一章介紹,WPF動(dòng)畫(huà)通過(guò)一組動(dòng)畫(huà)類(Animation類)表示,。使用少數(shù)幾個(gè)熟悉設(shè)置相關(guān)信息,,如開(kāi)始值、結(jié)束值以及持續(xù)時(shí)間,。這顯然使得它們非常適合于XAML,。不是很清晰的時(shí):如何為特定的事件和屬性關(guān)聯(lián)動(dòng)畫(huà),以及如何在正確的時(shí)間觸發(fā)動(dòng)畫(huà),。 在所有聲明式動(dòng)畫(huà)中都會(huì)用到如下兩個(gè)要素:
一,、故事板 故事板是增強(qiáng)的事件線,可用來(lái)分組多個(gè)動(dòng)畫(huà),,而且具有控制動(dòng)畫(huà)播放的能力——暫停,、停止以及改變播放位置。然而,,Storyboard類提供的最基本功能是,,能夠使用TargetProperty和TargetName屬性指向某個(gè)特定屬性和特定元素。換句話說(shuō),,故事板在動(dòng)畫(huà)和希望應(yīng)用動(dòng)畫(huà)的屬性之間架起了一座橋梁,。 下面的標(biāo)記演示了如何定義用于管理DoubleAnimation的故事板: <Storyboard TargetName="cmdGrow" TargetProperty="Width"> <DoubleAnimation From="160" To="300" Duration="0:0:5"></DoubleAnimation> </Storyboard> TargetName和TargetProperty都是附加屬性。這意味著可以直接將他們應(yīng)用于動(dòng)畫(huà),,如下所示: <Storyboard > <DoubleAnimation Storyboard.TargetName="cmdGrow" Storyboard.TargetProperty="Width" From="160" To="300" Duration="0:0:5"> </DoubleAnimation> </Storyboard> 上面的語(yǔ)法更常用,,因?yàn)橥ㄟ^(guò)這種語(yǔ)法可在同一個(gè)故事板中放置幾個(gè)動(dòng)畫(huà),并且每個(gè)動(dòng)畫(huà)可用于不同的元素和屬性,。 定義故事板是創(chuàng)建動(dòng)畫(huà)的第一步,。為讓故事板實(shí)際運(yùn)行起來(lái),還需要有事件觸發(fā)器,。 二,、事件觸發(fā)器 在“【W(wǎng)PF學(xué)習(xí)】第三十七章 觸發(fā)器 ”時(shí)第一次提到事件觸發(fā)器。樣式提供了一種將事件觸發(fā)器關(guān)聯(lián)到元素的方法,。然而,,可在如下4個(gè)位置定義事件觸發(fā)器:
當(dāng)創(chuàng)建事件觸發(fā)器時(shí),,需要制定開(kāi)始出發(fā)其的路由事件和由觸發(fā)器執(zhí)行的一個(gè)或多個(gè)動(dòng)作。對(duì)于動(dòng)畫(huà),,最常用的動(dòng)作是BeginStoryboard,,該動(dòng)作相當(dāng)于調(diào)用BeginAnimation()方法。 下面的示例使用按鈕的Triggers集合為Click事件關(guān)聯(lián)某個(gè)動(dòng)畫(huà),。當(dāng)單擊按鈕時(shí),,該動(dòng)畫(huà)增長(zhǎng)按鈕: <Button Margin="10" Name="cmdGrow" Height="40" Width="160" HorizontalAlignment="Center" VerticalAlignment="Center"> <Button.Triggers> <EventTrigger RoutedEvent="Button.Click"> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Width" To="300" Duration="0:0:5"> </DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger> </Button.Triggers> <Button.Content> Click and Make Me Grow </Button.Content> </Button> Storyboard.TargetProperty屬性指定了希望改變的屬性(在這個(gè)示例中是Width屬性)。如果沒(méi)有提供類的名稱,,故事板使用其父元素,在此使用的是希望擴(kuò)展的按鈕,。如果希望設(shè)置附加屬性(如Canvas.Left或Canvas.Top),需要在括號(hào)中封裝整個(gè)屬性,,如下所示: <DoubleAnimation Storyboard.TargetName="(Canvas.Top)" .../> 在這個(gè)示例中需不需要使用Storyboard.TargetName屬性。當(dāng)忽略該屬性時(shí),,故事板使用父元素,,在此是按鈕。 在這個(gè)示例中使用的聲明式方法和前面演示的只使用代碼的方法存在如下區(qū)別:To值被硬編碼為300個(gè)單位,,而不是相對(duì)于包含按鈕的窗口的尺寸設(shè)置,。如果希望使用窗口寬度,需要使用數(shù)據(jù)綁定表達(dá)式,,如下所示: <DoubleAnimation Storyboard.TargetProperty="Width" To="{Binding ElementName=cmdGrow, Path=Width}" Duration="0:0:5"> </DoubleAnimation> 這仍不能準(zhǔn)確地得到所希望的結(jié)果,。在此,按鈕從當(dāng)前尺寸增大到窗口的完整寬度,。只使用代碼的方法使用一種簡(jiǎn)單的計(jì)算,,將按鈕擴(kuò)大到比整個(gè)窗口寬度小30個(gè)單位的值。但XAML不支持內(nèi)聯(lián)計(jì)算,。一種解決方法是構(gòu)建能夠自動(dòng)完成工作的IValueConverter接口,。如下所示的示例: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Data; namespace Animation { public class ArithmeticConverter : IValueConverter { private const string ArithmeticParseExpression = "([+\\-*/]{1,1})\\s{0,}(\\-?[\\d\\.]+)"; private Regex arithmeticRegex = new Regex(ArithmeticParseExpression); public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is double && parameter != null) { string param = parameter.ToString(); if (param.Length > 0) { Match match = arithmeticRegex.Match(param); if (match != null && match.Groups.Count == 3) { string operation = match.Groups[1].Value.Trim(); string numericValue = match.Groups[2].Value; double number = 0; if (double.TryParse(numericValue, out number)) // this should always succeed or our regex is broken { double valueAsDouble = (double)value; double returnValue = 0; switch (operation) { case "+": returnValue = valueAsDouble + number; break; case "-": returnValue = valueAsDouble - number; break; case "*": returnValue = valueAsDouble * number; break; case "/": returnValue = valueAsDouble / number; break; } return returnValue; } } } } return null; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } } <Window x:Class="Animation.XamlAnimation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Animation" Title="XamlAnimation" Height="300" Width="300"> <Window.Resources> <local:ArithmeticConverter x:Key="converter"></local:ArithmeticConverter> </Window.Resources> <Button Padding="10" Name="cmdGrow" Height="40" Width="160" HorizontalAlignment="Center" VerticalAlignment="Center"> <Button.Triggers> <EventTrigger RoutedEvent="Button.Click"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Width" To="{Binding ElementName=window,Path=Width,Converter={StaticResource converter},ConverterParameter=-30}" Duration="0:0:5"></DoubleAnimation> <DoubleAnimation Storyboard.TargetProperty="Height" To="{Binding ElementName=window,Path=Height,Converter={StaticResource converter},ConverterParameter=-50}" Duration="0:0:5"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Button.Triggers> <Button.Content> Click and Make Me Grow </Button.Content> </Button> </Window> 使用樣式關(guān)聯(lián)觸發(fā)器 FrameworkElement.Triggers集合有點(diǎn)奇怪,它僅支持事件觸發(fā)器,。其他觸發(fā)器集合(Style.Triggers,、DataTemplate.Triggers與ControlTemplate.Triggers)的功能更強(qiáng)大,他們支持三種基本類型的WPF觸發(fā)器:屬性觸發(fā)器,、數(shù)據(jù)觸發(fā)器以及事件觸發(fā)器,。 使用事件觸發(fā)器是關(guān)聯(lián)動(dòng)畫(huà)的最常用方式,但并不是唯一的選擇,。如果使用位于樣式,、數(shù)據(jù)模板或控件模板中的Triggers集合,還可創(chuàng)建當(dāng)屬性值發(fā)生變化時(shí)進(jìn)行響應(yīng)的屬性觸發(fā)器,。例如,,下面的樣式復(fù)制了前面顯示的示例,。當(dāng)IsPressed屬性為true時(shí),該樣式觸發(fā)一個(gè)故事板: <Window.Resources> <Style x:Key="GrowButtonStyle"> <Style.Triggers> <Trigger Property="Button.IsPressed" Value="True"> <Trigger.EnterActions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Width" To="250" Duration="0:0:5"></DoubleAnimation> </Storyboard> </BeginStoryboard> </Trigger.EnterActions> </Trigger> </Style.Triggers> </Style> </Window.Resources> 可使用兩種方式為屬性觸發(fā)器關(guān)聯(lián)動(dòng)作,??墒褂肨rigger.EnterActions設(shè)置當(dāng)屬性改變到指定的數(shù)值時(shí)希望執(zhí)行的動(dòng)作(在上面的示例中,當(dāng)IsPressed屬性值變?yōu)閠rue時(shí)),,也可以使用Trigger.ExitActions設(shè)置當(dāng)屬性改變回原來(lái)的數(shù)值時(shí)執(zhí)行的動(dòng)作(當(dāng)IsPressed屬性的值變回false時(shí)),。這是一種封裝一堆互補(bǔ)動(dòng)畫(huà)的簡(jiǎn)便方法。 下面的按鈕使用上面顯示的樣式: <Button Padding="10" Name="cmdGrow" Height="40" Width="160" Style="{StaticResource GrowButtonStyle}" HorizontalAlignment="Center" VerticalAlignment="Center"> Click and Make Me Grow </Button> 請(qǐng)記住,,不見(jiàn)得在樣式中使用屬性觸發(fā)器,。也可使用事件觸發(fā)器,就像在前面介紹的那樣,。最后,,不見(jiàn)得以與使用樣式的按鈕相分離的方式定義樣式(也可使用內(nèi)聯(lián)樣式設(shè)置Button.Style屬性)。但是這種兩部分相分離的方法更常用,,并且提供了為多個(gè)元素應(yīng)用相同的靈活性,。 三、重疊動(dòng)畫(huà) 故事板提供了改變處理重疊動(dòng)畫(huà)方式的能力——換句話說(shuō),,決定第二個(gè)動(dòng)畫(huà)何時(shí)被應(yīng)用到已經(jīng)具有一個(gè)正在運(yùn)行的動(dòng)畫(huà)的屬性上,。可使用BeginStoryboard.HandoffBehavior屬性改變處理重疊動(dòng)畫(huà)的方式,。 通常,,當(dāng)兩個(gè)動(dòng)畫(huà)相互重疊時(shí),第二個(gè)動(dòng)畫(huà)會(huì)立即覆蓋第一個(gè)動(dòng)畫(huà),。這種行為就是所謂的“快照并替換”(由HandoffBehavior枚舉中的SnapshotAndReplace值表示),。當(dāng)?shù)诙€(gè)動(dòng)畫(huà)開(kāi)始時(shí),第二個(gè)動(dòng)畫(huà)獲取屬性當(dāng)前值(基于第一個(gè)動(dòng)畫(huà))的快照,,停止動(dòng)畫(huà),,并用新動(dòng)畫(huà)替換第一個(gè)動(dòng)畫(huà)。 另一個(gè)HandoffBehavior選項(xiàng)是Compose,,這種方式將第二個(gè)動(dòng)畫(huà)融合到第一個(gè)動(dòng)畫(huà)的時(shí)間線中,。例如,分析ListBox示例的修改版本,,當(dāng)縮小按鈕時(shí)使用HandoffBehavior.Compose: <EventTrigger RoutedEvent="ListBoxItem.MouseLeave"> <EventTrigger.Actions> <BeginStoryboard HandoffBehavior="Compose"> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="FontSize" BeginTime="0:0:0.5" Duration="0:0:0.2"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> 現(xiàn)在,,如果將鼠標(biāo)移到ListBoxItem對(duì)象上,然后在移開(kāi),,將看到不同的行為,。當(dāng)鼠標(biāo)移開(kāi)項(xiàng)時(shí),項(xiàng)會(huì)繼續(xù)擴(kuò)張,,這種行為非常明顯,,知道第二個(gè)動(dòng)畫(huà)到達(dá)其0.5秒得開(kāi)始時(shí)間延遲,,然后,第二個(gè)動(dòng)畫(huà)會(huì)縮小按鈕,。如果不使用Compose行為,,在第二個(gè)動(dòng)畫(huà)開(kāi)始之前的0.5秒得時(shí)間間隔內(nèi),按鈕會(huì)處于等待狀態(tài),,并固定為當(dāng)前尺寸,。 使用組合的HandoffBehavior行為需要更大開(kāi)銷(xiāo)。這是因?yàn)楫?dāng)?shù)诙€(gè)動(dòng)畫(huà)開(kāi)始時(shí),,用于運(yùn)行原來(lái)動(dòng)畫(huà)的時(shí)鐘不能被釋放,。相反,這個(gè)時(shí)鐘會(huì)繼續(xù)保持存活,,知道ListBoxItem對(duì)象被垃圾回收或?yàn)橄嗤膶傩詰?yīng)用新的動(dòng)畫(huà)為止,。 四、同步的動(dòng)畫(huà) Storyboard類間接地繼承自TimelineGroup類,,所以Storyboard類能包含多個(gè)動(dòng)畫(huà),最令人高興的是,,這些動(dòng)畫(huà)可以作為一組進(jìn)行管理——這意味著他們?cè)谕粫r(shí)間開(kāi)始,。 為查看這個(gè)一個(gè)示例,分析下面的故事板,。它開(kāi)始兩個(gè)動(dòng)畫(huà),,一個(gè)動(dòng)畫(huà)用于按鈕的Width屬性,而另一個(gè)動(dòng)畫(huà)用于按鈕的Height屬性,。因?yàn)閯?dòng)畫(huà)被分組到故事板中,,它們共同增加按鈕的尺寸,所以可得到比在代碼中通過(guò)簡(jiǎn)單地多次調(diào)用BeginAnimation()方法得到的效果更趨向同步的效果,。 <EventTrigger RoutedEvent="Button.Click"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Width" To="300" Duration="0:0:5"></DoubleAnimation> <DoubleAnimation Storyboard.TargetProperty="Height" To="300" Duration="0:0:5"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> 在這個(gè)示例中,,兩個(gè)動(dòng)畫(huà)具有相同的持續(xù)時(shí)間,但這并不是必須的,,對(duì)于在不同時(shí)間結(jié)束的動(dòng)畫(huà),,唯一需要考慮的是它們的FillBehavior行為。如果一個(gè)動(dòng)畫(huà)的FillBehavior屬性被設(shè)置為HoldEnd,,它會(huì)保持值直到故事板中所有的動(dòng)畫(huà)都結(jié)束,。如果故事板的FillBehavior屬性是HoldEnd,最后那個(gè)動(dòng)畫(huà)的值將被永久保存(直到使用新的動(dòng)畫(huà)替換這個(gè)動(dòng)畫(huà)或手動(dòng)刪除了這個(gè)動(dòng)畫(huà)),。 上一章列出的Timeline類的屬性開(kāi)始變得特別有用,。例如,可通過(guò)SpeedRatio屬性使故事板中的某個(gè)動(dòng)畫(huà)比其他動(dòng)畫(huà)更快,,也可以使用BeginTime屬性相對(duì)于一個(gè)動(dòng)畫(huà)來(lái)編譯另一個(gè)動(dòng)畫(huà)的開(kāi)始時(shí)間,,使該動(dòng)畫(huà)在特定的時(shí)間點(diǎn)開(kāi)始,。 五、控制播放 到目前位置,,已在事件觸發(fā)器中使用了一個(gè)動(dòng)作——加載動(dòng)畫(huà)的BeginStoryboard動(dòng)作,。然而,一旦創(chuàng)建故事板,,就可以用在其他動(dòng)作控制故事板,。這些工作類都繼承自ControllableStoryboardAction類,下表列出了這些類,。 表 控制故事板的動(dòng)作類 幫助文檔中沒(méi)有記載會(huì)妨礙使用這些動(dòng)作的內(nèi)容,。為成功地執(zhí)行這些動(dòng)作,必須在同一個(gè)Triggers集合中定義所有觸發(fā)器,。如果將BeginStoryboard動(dòng)作的觸發(fā)器和PauseStoryboard動(dòng)作的觸發(fā)器放置到不同集合中,,PauseStoryboard動(dòng)作就無(wú)法工作。為查看需要使用的設(shè)計(jì),,分析示例是有幫助的,。 例如,分析下圖中顯示的窗口,。該窗口使用一個(gè)網(wǎng)格在完全相同的位置精確地重疊了兩個(gè)Image元素,。最初,只有最頂部的圖像可見(jiàn),。但當(dāng)動(dòng)畫(huà)運(yùn)行是,,該圖像從1到0逐漸地增加透明度,最終使夜間的場(chǎng)景完全蓋過(guò)白天場(chǎng)景,。效果就像是圖像從白天變換到黑夜,,就像連續(xù)的隨時(shí)間流逝的照片。 下面的標(biāo)記定義了包含兩個(gè)圖像的Grid控件: <Grid> <Image Source="night.jpg"></Image> <Image Source="day.jpg" Name="imgDay"></Image> </Grid> 下面是從一幅圖像淡入到另一幅圖像的動(dòng)畫(huà): <DoubleAnimation Storyboard.TargetName="imgDay" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:10"></DoubleAnimation> 為增加這個(gè)示例的趣味性,,還在底部提供了幾個(gè)用于控制動(dòng)畫(huà)播放的按鈕,。使用這些按鈕,可執(zhí)行典型的媒體播放器動(dòng)作,,如暫停,、恢復(fù)播放以及停止(可添加其他按鈕來(lái)改變速度系數(shù)以及挑選特定的時(shí)間)。 下面的標(biāo)記定義了這些按鈕: <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center"> <Button Name="cmdStart">Start</Button> <Button Name="cmdPause">Pause</Button> <Button Name="cmdResume">Resume</Button> <Button Name="cmdStop">Stop</Button> <Button Name="cmdMiddle">Move To Middle</Button> </StackPanel> 通常,,可選擇在每個(gè)按鈕的Triggers集合中放置事件觸發(fā)器,。然而,在前面已解釋過(guò),,對(duì)于動(dòng)畫(huà)這種方法不能工作,。最簡(jiǎn)單的解決方法是在一個(gè)地方定義所有事件觸發(fā)器,例如,,在包含元素的Triggers集合中,,使用EventTrigger.SourceName屬性關(guān)聯(lián)這些事件觸發(fā)器,。只要SourceName屬性和為按鈕設(shè)置的Name屬性相匹配,觸發(fā)器就會(huì)應(yīng)用到恰當(dāng)?shù)陌粹o上,。 這個(gè)示例中,,可使用包含這些按鈕的StackPanel面板的Triggers集合。然而,,使用頂級(jí)元素(在這個(gè)示例中是窗口)的Triggers集合通常最簡(jiǎn)單,。這樣,就可在用戶界面中將按鈕移到不同的位置,,而不會(huì)禁用他們的功能,。 <Window.Triggers> <EventTrigger SourceName="cmdStart" RoutedEvent="Button.Click"> <BeginStoryboard Name="fadeStoryboardBegin"> <Storyboard> <DoubleAnimation Storyboard.TargetName="imgDay" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:10"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger> <EventTrigger SourceName="cmdPause" RoutedEvent="Button.Click"> <PauseStoryboard BeginStoryboardName="fadeStoryboardBegin"> </PauseStoryboard> </EventTrigger> <EventTrigger SourceName="cmdResume" RoutedEvent="Button.Click"> <ResumeStoryboard BeginStoryboardName="fadeStoryboardBegin"></ResumeStoryboard> </EventTrigger> <EventTrigger SourceName="cmdStop" RoutedEvent="Button.Click"> <StopStoryboard BeginStoryboardName="fadeStoryboardBegin"></StopStoryboard> </EventTrigger> <EventTrigger SourceName="cmdMiddle" RoutedEvent="Button.Click"> <SeekStoryboard BeginStoryboardName="fadeStoryboardBegin" Offset="0:0:5"></SeekStoryboard> </EventTrigger> </Window.Triggers> 注意,必須為BeginStoryboard動(dòng)作指定名稱(在這個(gè)示例中,,名稱是fadeStoryboardBegin),。其他觸發(fā)器通過(guò)為BeginStoryboardName屬性指定這個(gè)名稱,連接到相同的故事板,。 當(dāng)使用故事板動(dòng)作時(shí)將遇到限制,。他們提供的屬性(如SeekStoryboard.Offset和SetStoryboardSpeedRatio.SpeedRatio屬性)不是依賴性項(xiàng)屬性,這會(huì)限制使用數(shù)據(jù)綁定表達(dá)式,。例如,,不能自動(dòng)讀取Slider.Value屬性值并將其應(yīng)用到SetStoryboardSpeedRatio.SpeedRatio動(dòng)作,因?yàn)镾peedRatio屬性不接受數(shù)據(jù)綁定表達(dá)式,??赡苷J(rèn)為通過(guò)使用Storyboard對(duì)象的SpeedRatio屬性來(lái)解決這個(gè)問(wèn)題,。但這是行不同的,,當(dāng)動(dòng)畫(huà)開(kāi)始時(shí),讀取SpeedRatio值并創(chuàng)建一個(gè)動(dòng)畫(huà)時(shí)鐘,。此后,,即使改變了SpeedRatio屬性的值,動(dòng)畫(huà)也仍會(huì)保持正常的速度,。 如果希望動(dòng)態(tài)調(diào)整速度或位置,,唯一的解決方法是使用代碼。Storyboard類中的方法提供了與故事板觸發(fā)器相同的功能,,包括Begin(),、Pause()、Resume(),、Seek(),、Stop()、SkipToFill(),、SetSpeedRatio()以及Remove()方法,。 要訪問(wèn)Storyboard對(duì)象,,必須在標(biāo)記中設(shè)置其N(xiāo)ame屬性: <Storyboard Name="fadeStoryboard"> 現(xiàn)在只需要編寫(xiě)恰當(dāng)?shù)氖录幚沓绦颍⑹褂肧toryboard對(duì)象的方法(請(qǐng)記住,,簡(jiǎn)單地改變故事板的屬性(比如SpeedRatio)是沒(méi)有任何效果的,,它們僅配置當(dāng)動(dòng)畫(huà)開(kāi)始時(shí)將要使用的設(shè)置)。 當(dāng)拖動(dòng)Slider控件上的滑塊時(shí),,下面的事件處理程序會(huì)進(jìn)行響應(yīng),。該事件處理程序獲取滑動(dòng)條的值(范圍是0~3),并使用該數(shù)值應(yīng)用新的速率: private void sldSpeed_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { fadeStoryboard.SetSpeedRatio(this, sldSpeed.Value); } 注意,,SetSpeedRatio()方法需要兩個(gè)參數(shù),。第一個(gè)參數(shù)是頂級(jí)動(dòng)畫(huà)容器(在這個(gè)示例中,是指當(dāng)前窗口),。所有故事板方法都需要這個(gè)引用,。第二個(gè)參數(shù)是新的速率。 六,、監(jiān)視動(dòng)畫(huà)進(jìn)度 上一節(jié)顯示的動(dòng)畫(huà)播放器仍缺少一個(gè)在大多數(shù)媒體播放器中都具有的功能——確定當(dāng)前位置的能力,。為使這個(gè)動(dòng)畫(huà)播放器更加精致,可添加一些文本來(lái)顯示時(shí)間的流逝,,并添加進(jìn)度條來(lái)指示動(dòng)畫(huà)只需的速度,。下圖顯示了使用這兩個(gè)細(xì)節(jié)的動(dòng)畫(huà)播放器的修改版。 添加這些細(xì)節(jié)相當(dāng)簡(jiǎn)單,。首先需要使用TextBlock元素顯示時(shí)間,,而后需要使用ProgressBar控件顯示圖形進(jìn)度條,可能認(rèn)為,,可使用數(shù)據(jù)綁定表達(dá)式設(shè)置TextBlock值和ProgressBar內(nèi)容,,但這是行不同的。因?yàn)閺墓适掳逯袡z索當(dāng)前動(dòng)畫(huà)時(shí)鐘相關(guān)的唯一方式是使用方法,,如GetCurrentTime()和GetCurrentProgress(),。無(wú)法從屬性中獲取相同的信息。 最簡(jiǎn)單的解決方法是響應(yīng)下表中列出的某個(gè)故事板事件,。 表 故事板事件
這個(gè)示例需要使用CurrentTimeInvalidated事件,,每次向前移動(dòng)動(dòng)畫(huà)時(shí)鐘都會(huì)引發(fā)該事件(通常,每秒移動(dòng)60此,,但如果執(zhí)行的代碼需要更長(zhǎng)時(shí)間,,可能會(huì)丟失時(shí)鐘刻度)。 當(dāng)引發(fā)CurrentTimeInvalidated事件時(shí),,發(fā)送者是Clock對(duì)象(Clock類位于System.Windows.Media.Animation名稱空間),。可以通過(guò)Clock對(duì)象檢索當(dāng)前時(shí)間,,當(dāng)前時(shí)間使用TimeSpan對(duì)象表示,;并且可檢索當(dāng)前進(jìn)度,當(dāng)前進(jìn)度使用0~1之間的數(shù)值表示,。 下面的代碼更新標(biāo)簽和進(jìn)度條: private void storyboard_CurrentTimeInvalidated(object sender, EventArgs e) { // Sender is the clock that was created for this storyboard. Clock storyboardClock = (Clock)sender; if (storyboardClock.CurrentProgress == null) { lblTime.Text = "[[ stopped ]]"; progressBar.Value = 0; } else { lblTime.Text = storyboardClock.CurrentTime.ToString(); progressBar.Value = (double)storyboardClock.CurrentProgress; } }
|
|