首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

WPF 顯示動態 GIF 完整解決方案:從幀解析到自訂 GifImage 控制項

     編輯:關於.NET

痛點:WPF 原生 Image 控制項不支援動畫 GIF

WPF 強大的 Image 控制項僅能顯示 GIF 第一幀。雖可用 WebBrowser 或 Frame 載入,但過於笨重且難以抑制右鍵選單。MediaElement 亦不支援 GIF 動畫。

核心思路:幀解碼 + 時間解析 + DispatcherTimer 驅動

第一步:GifBitmapDecoder 解碼所有幀

var decoder = new GifBitmapDecoder(
    new Uri("OH.gif", UriKind.Relative),
    BitmapCreateOptions.PreservePixelFormat,
    BitmapCacheOption.Default);

foreach (BitmapFrame frame in decoder.Frames)
{
    var img = new Image { Source = frame };
    panel.Children.Add(img); // 僅靜態顯示所有幀
}

第二步:手動解析 GIF 檔案結構獲取幀延遲與處置方式

GIF 格式規範參考:動態 GIF 檔案儲存結構

private int ParseGraphicControlExtension(byte[] gifData, int offset)
{
    int length = gifData[offset + 2];
    int returnOffset = offset + length + 3;
    byte packedField = gifData[offset + 3];
    int disposalMethod = (packedField & 0x1C) >> 2;
    int delay = BitConverter.ToUInt16(gifData, offset + 4); // 單位:10ms
    // 略過應用程式擴展區塊...
    return returnOffset;
}

第三步:封裝 GifImage 控制項

支援多種來源:本地相對/絕對路徑、HTTP/FTP、Pack URI、內嵌資源

if (source.EndsWith(".GIF", StringComparison.OrdinalIgnoreCase) || ForceGifAnim)
{
    if (!uri.IsAbsoluteUri) GetGifStreamFromPack(uri);
    else if (uri.Scheme == "http" || uri.Scheme == "https") GetGifStreamFromHttp(uri);
    else if (uri.Scheme == "file") GetGifStreamFromFile(uri);
    else CreateNonGifAnimationImage();
}
else CreateNonGifAnimationImage();

第四步:DispatcherTimer 依幀延遲切換顯示

public void CreateGifAnimation(MemoryStream memoryStream)
{
    Reset();
    byte[] gifData = memoryStream.GetBuffer();
    var decoder = new GifBitmapDecoder(memoryStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
    numberOfFrames = decoder.Frames.Count;
    ParseGif(gifData); // 解析延遲、處置方式

    for (int i = 0; i < decoder.Frames.Count; i++)
    {
        frameList[i].Source = decoder.Frames[i];
        frameList[i].Visibility = Visibility.Hidden;
        canvas.Children.Add(frameList[i]);
        // 設定位置、ZIndex...
    }

    frameList[0].Visibility = Visibility.Visible;
    frameTimer = new DispatcherTimer();
    frameTimer.Tick += NextFrame;
    frameTimer.Interval = TimeSpan.FromMilliseconds(frameList[0].delayTime * 10);
    frameTimer.Start();
}

使用方式:如同原生 Image 控制項般簡單

<local:GifImage Source="pack://application:,,,/Images/loading.gif" />

關鍵技術細節補充

  • 記憶體管理:大型 GIF 建議使用 BitmapCacheOption.OnLoad 釋放檔案鎖定
  • 迴圈控制:解析 Netscape 2.0 迴圈擴展區塊決定播放次數
  • 處置方式:依 disposalMethod 決定幀切換時是否還原背景或保留前一幀
  • 效能優化:預先建立所有 Image 元素,切換 Visibility 而非重建
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved