關於WPF裡面動態圖片(Animate Image)和靜態圖片(Static Image)並存問題



要在 WPF 程式上顯示一般圖片(BMP、JPG、PNG、GIF..) 都不是甚麼太大問題,大概像下面一樣用 image 標籤即可。


<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <Image Grid.Column="1" Grid.Row="0" Source="D:\MyImages\Taiwanmap.jpg"/>
</Grid>



但遇到動畫圖檔 GIFa 就是一件麻煩事了。

這個 image 標籤遇到 GIFa 就只能顯示第一張畫面,然後也不會有動畫。

<Image Grid.Column="1" Grid.Row="0" Source="D:\MyImages\cutecoupletogether.gif"/>

我的目的是需要的是在同一種圖像元件標籤下,能夠同時顯示動畫或是靜態圖片的方法。
也就是說如果我要做一個圖片展示區或清單(動態生成),就很難去依照圖片類型一直去更換樣版標籤,所以我希望一種標籤就能夠解決問題。

能夠支援的標籤類型當然不是只有 Image,還有很多種方法:
(底下標籤都會加上 Grid.Column 和 Grid.Row 屬性,因為我會放在上面 Grid 版面上的)

1、 MediaElement 標籤,他也可放置靜態圖片或動態圖片,但這個標籤早期只能支援URI方式提供來源,不過 .NET 4.0後 好像就改成可以放置路徑。

我試圖在 MediaElement 放置 gif 圖片


<MediaElement x:Name="element2" Grid.Column="1" Grid.Row="1" Source="D:\downloads\images\cutecoupletogether.gif" />


但它只播放一次(1 Loop)就停止了,所以我嘗試做成 RepeatBehavior="Forever" 但也只是播放一次而已。而且如果只是放靜態圖片來說:它的用法太複雜,因為要加入一堆 StoryBord 標籤來處理。


<MediaElement Name="myMediaElement" Grid.Column="0" Grid.Row="1">
    <MediaElement.Triggers>
        <EventTrigger RoutedEvent="MediaElement.Loaded">
            <EventTrigger.Actions>
                <BeginStoryboard>
                    <Storyboard>
                        <MediaTimeline Source="D:\MyImages\cutecoupletogether.gif" Storyboard.TargetName="myMediaElement" RepeatBehavior="Forever" />
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger.Actions>
        </EventTrigger>
    </MediaElement.Triggers>
</MediaElement>



MediaElement 標籤 還有一個處理方式就是在媒體播放結束事件 MediaEnded 加上處理方法
像是這樣:


<MediaElement x:Name="element" LoadedBehavior="Play" Source="D:\MyImages\rabbit.bmp" MediaEnded="MediaElement_MediaEnded"/>


這個 MediaElement_MediaEnded 方法就是處理撥放時間點,讓它往前多撥放1毫秒達到重播效果。


private void MediaElement_MediaEnded(object sender, RoutedEventArgs e)
{
    ((MediaElement)sender).Position = ((MediaElement)sender).Position.Add(TimeSpan.FromMilliseconds(1));
}


只是這樣做有個很大問題,當你 Source 放入的是靜態圖片時,可能會讓電腦大當機(感覺好像是計算時間造成溢位發生的)!!!

2、使用 WinForms 的 PicturxBox 控制項

這也是個解決方式,畢竟 PicturxBox 在 Window Forms 時代也是一個很強大物件

首先要把兩個參考加進來:


Syatem.Windows.Forms
WindowsFormsIntergration.dll (C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF)


先在XAML加入 NS


xmlns:WinFormControls="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"


然後使用 PicturxBox 控件:


<WindowsFormsHost Grid.Column="2" Grid.Row="0">
      <WinFormControls:PictureBox ImageLocation="D:\MyImages\cutecoupletogether.gif" SizeMode="Zoom"/>
</WindowsFormsHost>


也是可以達成,只不過.....這種用法非常不是 WPF 要的方式,而且當程式移轉到Windows RT/Windows Phone 時會無法編譯,因為Windows Forms 是 PC 的函式庫。

3、接下來就是自訂控件,就是拿 Image 標籤下去擴充功能,讓它可以處理動畫。

這部分應用來自 Thomas Levesque 的相關技術:
第一種就是使用記憶體將動畫分頁儲存後播放方式
[參考這裡]
作者將他的作品封裝成套件 WpfAnimatedGif 放在 NuGet
下載 WpfAnimatedGif 到你的專案以後

在使用的 xaml 檔案上宣告 NameSpace


xmlns:gifa="http://wpfanimatedgif.codeplex.com"



然後在顯示的 image 標籤上添加屬性 gifa:ImageBehavior.AnimatedSource


<Image Grid.Column="3" Grid.Row="0" gifa:ImageBehavior.AnimatedSource = "D:\MyImages\cutecoupletogether.gif"/>




第二種就是採用 WriteableBitmap 做為核心宣染處理方式
[參考這裡]
作者將他的作品封裝成套件 XamlAnimatedGif  放在 NuGet
下載 XamlAnimatedGif  到你的專案以後
在使用的 xaml 檔案上宣告 NameSpace


xmlns:gifb="clr-namespace:XamlAnimatedGif;assembly=XamlAnimatedGif"


然後在顯示的 image 標籤上添加屬性 gifb:AnimationBehavior.SourceUri


<Image Grid.Column="3" Grid.Row="1" gifb:AnimationBehavior.SourceUri="D:\MyImages\cutecoupletogether.gif"/>



使用上非常的簡潔便利,而且 Image 本身就是 WPF 特性的控件,蠻符合 WPF 開發風格的。


參考:
[WPF] DISPLAY AN ANIMATED GIF IMAGE
A NEW LIBRARY TO DISPLAY ANIMATED GIFS IN XAML APPS
WPF中显示GIF图片 -CSDN
[WPF疑难]在WPF中显示动态GIF


留言

這個網誌中的熱門文章

【研究】列印的條碼為什麼很難刷(掃描)

C# 使用 Process.Start 執行外部程式

統一發票列印小程式