ฉันจะทำให้ gif แบบเคลื่อนไหวทำงานใน WPF ได้อย่างไร


218

สิ่งที่ประเภทควบคุมฉันควรใช้ - Image, MediaElementฯลฯ ?


4
นี่คือบทสรุปล่าสุดของการแก้ปัญหาด้านล่าง ฉันใช้สิ่งเหล่านี้โดยใช้ VS2015 คลาส GifImage ที่ส่งโดย Dario ทำงานได้ดี แต่ gif บางตัวของฉันถูกสร้างขึ้นมา วิธี MediaElement โดย Pradip Daunde และ nicael ดูเหมือนว่าจะทำงานในพื้นที่แสดงตัวอย่าง แต่ไม่มี gif ของฉันแสดงระหว่างรันไทม์ โซลูชัน WpfAnimatedGif โดย IgorVaschuk และ SaiyanGirl ทำงานได้ดีโดยไม่มีปัญหา แต่จำเป็นต้องติดตั้งห้องสมุดบุคคลที่สาม (อย่างชัดเจน) ฉันไม่ได้ลองที่เหลือ
Heath Carroll

คำตอบ:


214

ฉันไม่สามารถรับคำตอบที่เป็นที่นิยมที่สุดสำหรับคำถามนี้ (ด้านบนโดย Dario) เพื่อให้ทำงานได้อย่างถูกต้อง ผลที่ได้คือภาพเคลื่อนไหวที่แปลกและขาด ๆ หาย ๆ พร้อมสิ่งประดิษฐ์แปลก ๆ ทางออกที่ดีที่สุดที่ฉันพบแล้ว: https://github.com/XamlAnimatedGif/WpfAnimatedGif

คุณสามารถติดตั้งได้ด้วย NuGet

PM> Install-Package WpfAnimatedGif

และเพื่อใช้ในเนมสเปซใหม่ไปยังหน้าต่างที่คุณต้องการเพิ่มอิมเมจ gif และใช้งานดังต่อไปนี้

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:gif="http://wpfanimatedgif.codeplex.com" <!-- THIS NAMESPACE -->
    Title="MainWindow" Height="350" Width="525">

<Grid>
    <!-- EXAMPLE USAGE BELOW -->
    <Image gif:ImageBehavior.AnimatedSource="Images/animated.gif" />

แพคเกจนั้นเรียบร้อยจริงๆคุณสามารถตั้งค่าคุณลักษณะบางอย่างเช่นด้านล่าง

<Image gif:ImageBehavior.RepeatBehavior="3x"
       gif:ImageBehavior.AnimatedSource="Images/animated.gif" />

และคุณสามารถใช้มันในรหัสของคุณเช่นกัน:

var image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri(fileName);
image.EndInit();
ImageBehavior.SetAnimatedSource(img, image);

แก้ไข: การสนับสนุน Silverlight

ตามความคิดเห็นของ josh2112 หากคุณต้องการเพิ่มการสนับสนุน GIF แบบเคลื่อนไหวในโครงการ Silverlight ของคุณให้ใช้github.com/XamlAnimatedGif/XamlAnimatedGif


13
สิ่งนี้ใช้งานได้ดีมากและใช้เวลาน้อยกว่า 60 วินาทีในการติดตั้ง ขอบคุณ!
Ryan Sorensen

3
วิธีตอบที่ดีกว่า IMO ยอดนิยมใด ๆ โดยเฉพาะอย่างยิ่งเนื่องจากมันไม่ได้พึ่งพาคุณโดยใช้ C #
Jamie E

8
นี่ดีกว่าคำตอบที่ได้รับการยอมรับอย่างมาก: ใช้ข้อมูลเมตาของ gif ไม่ใช่การขาด ๆ หาย ๆ เป็นแพ็กเกจ NuGet เป็นผู้ไม่เชื่อเรื่องภาษา ฉันต้องการให้สแต็คโอเวอร์โฟลว์อนุญาตให้ลงคะแนนโดยไม่มั่นใจในคำตอบที่ยอมรับได้
John Gietzen

6
ประกาศการบริการสาธารณะ: ผู้เขียน WpfAnimatedGif ได้ 'rebooted' โครงการของเขาเป็น XamlAnimatedGif และสนับสนุน WPF, Windows Store (Win8), Windows 10 และ Silverlight: github.com/XamlAnimatedGif/XamlAnimatedGif
josh2112

2
ที่imgนี่คืออะไร
amit jha

104

ฉันโพสต์โซลูชันที่ขยายการควบคุมรูปภาพและการใช้ตัวถอดรหัส Gif ตัวถอดรหัส gif มีคุณสมบัติเฟรม ฉันทำให้FrameIndexทรัพย์สินเคลื่อนไหว เหตุการณ์ChangingFrameIndexจะเปลี่ยนคุณสมบัติต้นฉบับเป็นเฟรมที่สอดคล้องกับFrameIndex(ซึ่งอยู่ในตัวถอดรหัส) ฉันเดาว่า gif มี 10 เฟรมต่อวินาที

class GifImage : Image
{
    private bool _isInitialized;
    private GifBitmapDecoder _gifDecoder;
    private Int32Animation _animation;

    public int FrameIndex
    {
        get { return (int)GetValue(FrameIndexProperty); }
        set { SetValue(FrameIndexProperty, value); }
    }

    private void Initialize()
    {
        _gifDecoder = new GifBitmapDecoder(new Uri("pack://application:,,," + this.GifSource), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
        _animation = new Int32Animation(0, _gifDecoder.Frames.Count - 1, new Duration(new TimeSpan(0, 0, 0, _gifDecoder.Frames.Count / 10, (int)((_gifDecoder.Frames.Count / 10.0 - _gifDecoder.Frames.Count / 10) * 1000))));
        _animation.RepeatBehavior = RepeatBehavior.Forever;
        this.Source = _gifDecoder.Frames[0];

        _isInitialized = true;
    }

    static GifImage()
    {
        VisibilityProperty.OverrideMetadata(typeof (GifImage),
            new FrameworkPropertyMetadata(VisibilityPropertyChanged));
    }

    private static void VisibilityPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        if ((Visibility)e.NewValue == Visibility.Visible)
        {
            ((GifImage)sender).StartAnimation();
        }
        else
        {
            ((GifImage)sender).StopAnimation();
        }
    }

    public static readonly DependencyProperty FrameIndexProperty =
        DependencyProperty.Register("FrameIndex", typeof(int), typeof(GifImage), new UIPropertyMetadata(0, new PropertyChangedCallback(ChangingFrameIndex)));

    static void ChangingFrameIndex(DependencyObject obj, DependencyPropertyChangedEventArgs ev)
    {
        var gifImage = obj as GifImage;
        gifImage.Source = gifImage._gifDecoder.Frames[(int)ev.NewValue];
    }

    /// <summary>
    /// Defines whether the animation starts on it's own
    /// </summary>
    public bool AutoStart
    {
        get { return (bool)GetValue(AutoStartProperty); }
        set { SetValue(AutoStartProperty, value); }
    }

    public static readonly DependencyProperty AutoStartProperty =
        DependencyProperty.Register("AutoStart", typeof(bool), typeof(GifImage), new UIPropertyMetadata(false, AutoStartPropertyChanged));

    private static void AutoStartPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        if ((bool)e.NewValue)
            (sender as GifImage).StartAnimation();
    }

    public string GifSource
    {
        get { return (string)GetValue(GifSourceProperty); }
        set { SetValue(GifSourceProperty, value); }
    }

    public static readonly DependencyProperty GifSourceProperty =
        DependencyProperty.Register("GifSource", typeof(string), typeof(GifImage), new UIPropertyMetadata(string.Empty, GifSourcePropertyChanged));

    private static void GifSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        (sender as GifImage).Initialize();
    }

    /// <summary>
    /// Starts the animation
    /// </summary>
    public void StartAnimation()
    {
        if (!_isInitialized)
            this.Initialize();

        BeginAnimation(FrameIndexProperty, _animation);
    }

    /// <summary>
    /// Stops the animation
    /// </summary>
    public void StopAnimation()
    {
        BeginAnimation(FrameIndexProperty, null);
    }
}

ตัวอย่างการใช้งาน (XAML):

<controls:GifImage x:Name="gifImage" Stretch="None" GifSource="/SomeImage.gif" AutoStart="True" />

1
อันนี้ใช้ได้ดีสำหรับแอป XBAP เพราะคุณไม่ต้องการการอ้างอิงเพิ่มเติม
Max Galkin

1
มันเท่ห์มาก โดยการวางรหัสคอนสตรัคเตอร์ของคุณในเหตุการณ์ "เริ่มต้น" และแนะนำคุณสมบัติ Uri การควบคุมนี้สามารถวางในไฟล์ XAML
flq

1
+1 ดีจัง! อย่างไรก็ตามมันไม่ได้คำนึงถึงระยะเวลาเฟรมที่แท้จริงของภาพ ... หากคุณสามารถหาวิธีอ่านข้อมูลนั้นได้คุณสามารถเปลี่ยนรหัสเพื่อใช้Int32AnimationUsingKeyFrames
Thomas Levesque

7
ที่จริงแล้ว framerate นั้นคงที่สำหรับ GIF ดังนั้นคุณไม่จำเป็นต้องมีคีย์เฟรมหลังจากทั้งหมด ... คุณสามารถอ่าน framerate ด้วยgf.Frames[0].MetaData.GetQuery("/grctlext/Delay")(ส่งคืน ushort ซึ่งเป็นระยะเวลาของเฟรมเป็นร้อยวินาที)
Thomas Levesque

3
@ vidstige ใช่ฉันจำไม่ได้ว่าทำไมฉันถึงแสดงความคิดเห็นในเวลานี้ (เกือบ 2 ปีที่แล้ว) ฉันทราบว่าการหน่วงเวลาอาจแตกต่างกันสำหรับแต่ละเฟรมและไลบรารีWPF Animated GIFของฉันจะคำนึงถึงความเหมาะสม
โทมัสเลวิส

38

ฉันก็ทำการค้นหาและค้นพบวิธีแก้ปัญหาที่แตกต่างกันหลายอย่างในกระทู้ในฟอรัม MSDN เก่า (ลิงก์ไม่ทำงานอีกต่อไปดังนั้นฉันจึงลบออก)

การดำเนินการที่ง่ายที่สุดดูเหมือนจะใช้ WinForms PictureBoxควบคุมและไปเช่นนี้ (เปลี่ยนบางสิ่งจากด้ายส่วนใหญ่มันเหมือนกัน)

เพิ่มการอ้างอิงไปSystem.Windows.Forms, WindowsFormsIntegrationและSystem.Drawingโครงการของคุณครั้งแรก

<Window x:Class="GifExample.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:wfi="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
    xmlns:winForms="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
    Loaded="Window_Loaded" >
    <Grid>
        <wfi:WindowsFormsHost>
            <winForms:PictureBox x:Name="pictureBoxLoading">
            </winForms:PictureBox>
        </wfi:WindowsFormsHost>
    </Grid>
</Window >

จากนั้นในWindow_Loadedตัวจัดการคุณจะตั้งค่าpictureBoxLoading.ImageLocationคุณสมบัติเป็นเส้นทางของไฟล์รูปภาพที่คุณต้องการแสดง

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    pictureBoxLoading.ImageLocation = "../Images/mygif.gif";
}

มีMediaElementการกล่าวถึงการควบคุมในเธรดนั้น แต่ก็มีการกล่าวถึงว่าเป็นการควบคุมที่ค่อนข้างหนักดังนั้นจึงมีทางเลือกจำนวนมากรวมถึงการควบคุม homebrewed อย่างน้อย 2 ตัวตามการImageควบคุมดังนั้นจึงเป็นวิธีที่ง่ายที่สุด


คุณสามารถวางหน้าต่างหลักนี้ด้วย AllowTransparency = "True" เมื่อใช้ WindowsFormsHost ได้อย่างไร
จูเนียร์Mayhé

@Junior: AllowTransparency="True"ใช่คุณสามารถตั้งค่า หรือไม่ว่าจะสร้างผลลัพธ์ที่คุณมีในใจเป็นอีกเรื่องหนึ่ง ฉันไม่ได้ลองด้วยตัวเอง แต่ฉันจะพนันได้เลยว่าWindowsFormsHostมันจะไม่โปร่งใสเลย ส่วนที่เหลือของWindowพลัง ฉันคิดว่าคุณต้องลองดู
Joel B Fant

ฉันมีปัญหากับ pictureBoxLoading.Image เนื่องจาก winform API ฉันโพสต์รหัสด้านล่างที่แก้ไขปัญหาของฉัน ขอบคุณสำหรับวิธีการแก้ปัญหาของคุณโจเอล!
sondlerd

ดูเหมือนว่าคุณจะตายแล้ว มันเป็นหัวข้อนี้หรือไม่?
เช็ด

2
เมื่อเพิ่มการอ้างอิงการรวมชื่อใน UI ของฉันคือ WindowsFormsIntegration โดยไม่มีจุด: i.imgur.com/efMiC23.png
yu yang Jian

36

เกี่ยวกับแอพตัวเล็ก ๆ นี้: รหัสที่อยู่เบื้องหลัง:

public MainWindow()
{
  InitializeComponent();
  Files = Directory.GetFiles(@"I:\images");
  this.DataContext= this;
}
public string[] Files
{get;set;}

XAML:

<Window x:Class="PicViewer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="175" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <ListBox x:Name="lst" ItemsSource="{Binding Path=Files}"/>
        <MediaElement Grid.Column="1" LoadedBehavior="Play" Source="{Binding ElementName=lst, Path=SelectedItem}" Stretch="None"/>
    </Grid>
</Window>

1
เยี่ยมมาก! รหัสสั้น ๆ ทำงานได้ดี ฉันไม่อยากจะเชื่อเลยว่ามันไม่มี upvotes มากขึ้น
เช็ด

2
คำตอบที่ดีที่สุด ... ควรอยู่ด้านบนสุด! ฉันสามารถทำให้มันทำงานโดยไม่มีรหัสใด ๆ - แค่นี้<MediaElement LoadedBehavior="Play" Source="{Binding MyGifFile}" >- MyGifFile เป็นเพียงชื่อไฟล์ (และเส้นทาง) ของ gif เคลื่อนไหวของฉัน
Anthony Nichols

Jeez, ทำไมถึงต้องไปผูกListBoxหรือผูกเลย? ฉันพยายามโดยไม่ผูกมัดเพียงแค่ใส่เส้นทางของไฟล์ในแหล่งที่มาและปรากฏขึ้น แต่ไม่เคลื่อนไหว หากฉันใช้การเชื่อมโยงแม้ว่าListBoxจะไม่ปรากฏขึ้นเลยสำหรับฉัน - มันจะให้ข้อยกเว้นว่าเส้นทางไฟล์ของฉันไม่ถูกต้องแม้ว่าจะเป็นเส้นทางเดียวกับที่ฉันใช้เมื่อปรากฏ
vapcguy

ใช้เวลานานในการอัปเดตและจำเป็นต้องอัปเดตทุกครั้งที่เข้าสู่มุมมอง
Yola

15

มันง่ายมากถ้าคุณใช้<MediaElement>:

<MediaElement  Height="113" HorizontalAlignment="Left" Margin="12,12,0,0" 
Name="mediaElement1" VerticalAlignment="Top" Width="198" Source="C:\Users\abc.gif"
LoadedBehavior="Play" Stretch="Fill" SpeedRatio="1" IsMuted="False" />

ในกรณีที่ไฟล์ของคุณจะถูกบรรจุอยู่ในแอปของคุณคุณสามารถใช้ DataBinding public string SpinnerLogoPath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Assets\images\mso_spinninglogo_blue_2.gif");สำหรับแหล่งที่มาและหาเส้นทางในรหัส: ตรวจสอบให้แน่ใจว่าตั้งค่าไฟล์เป็น Build = Content และคัดลอกไปยังไดเร็กทอรี output
มัฟฟินแมน

ฉันใช้วิธีการนี้เนื่องจากแพ็คเกจ WpfAnimatedGif NuGet ไม่ทำงานได้ดีสำหรับฉัน - ดูเหมือนจะผิดพลาดเมื่อใช้งาน CPU มาก ฉันตั้งค่า gif ให้เป็น Build = Resource และตั้งค่า Source โดยใช้พา ธ สัมพัทธ์จากโฟลเดอร์ที่ Window อยู่เช่น Source = "../../ Images / Rotating-e.gif" ทำงานได้ดีสำหรับฉันและไม่จำเป็นต้องใช้ DLLs ของบุคคลที่สาม
Richard Moore

นี่คือทางออกที่ง่ายที่สุดโดยไกล แต่ปัญหาคือเมื่อทุกเฟรมของภาพเคลื่อนไหว gif ถูกสแกนผ่านภาพเคลื่อนไหวจะหยุดลง และไม่มีทางที่จะทำให้ gif เคลื่อนไหวจากเฟรม 0 ได้อีก ไม่มีวิธีการเริ่มการเคลื่อนไหวหรือวนซ้ำตลอดไป อย่างน้อยฉันไม่พบวิธีการใช้ <MediaElement />
BoiseBaked

<MediaElement /> ช้าอย่างไม่น่าเชื่อและเต็มไปด้วยปัญหาการแข่งด้ายระหว่างวิธีการ ฮึ่ม ...
BoiseBaked

10

นี่คือการควบคุมภาพเคลื่อนไหวในเวอร์ชันของฉัน คุณสามารถใช้แหล่งที่มาของคุณสมบัติมาตรฐานสำหรับการระบุแหล่งที่มาของรูปภาพ ฉันปรับปรุงมันต่อไป ฉันเป็นชาวรัสเซียโครงการเป็นภาษารัสเซียดังนั้นความคิดเห็นยังเป็นภาษารัสเซีย แต่อย่างไรก็ตามคุณควรเข้าใจทุกอย่างโดยปราศจากความคิดเห็น :)

/// <summary>
/// Control the "Images", which supports animated GIF.
/// </summary>
public class AnimatedImage : Image
{
    #region Public properties

    /// <summary>
    /// Gets / sets the number of the current frame.
    /// </summary>
    public int FrameIndex
    {
        get { return (int) GetValue(FrameIndexProperty); }
        set { SetValue(FrameIndexProperty, value); }
    }

    /// <summary>
    /// Gets / sets the image that will be drawn.
    /// </summary>
    public new ImageSource Source
    {
        get { return (ImageSource) GetValue(SourceProperty); }
        set { SetValue(SourceProperty, value); }
    }

    #endregion

    #region Protected interface

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the Source property.
    /// </summary>
    protected virtual void OnSourceChanged(DependencyPropertyChangedEventArgs aEventArgs)
    {
        ClearAnimation();

        BitmapImage lBitmapImage = aEventArgs.NewValue as BitmapImage;

        if (lBitmapImage == null)
        {
            ImageSource lImageSource = aEventArgs.NewValue as ImageSource;
            base.Source = lImageSource;
            return;
        }

        if (!IsAnimatedGifImage(lBitmapImage))
        {
            base.Source = lBitmapImage;
            return;
        }

        PrepareAnimation(lBitmapImage);
    }

    #endregion

    #region Private properties

    private Int32Animation Animation { get; set; }
    private GifBitmapDecoder Decoder { get; set; }
    private bool IsAnimationWorking { get; set; }

    #endregion

    #region Private methods

    private void ClearAnimation()
    {
        if (Animation != null)
        {
            BeginAnimation(FrameIndexProperty, null);
        }

        IsAnimationWorking = false;
        Animation = null;
        Decoder = null;
    }

    private void PrepareAnimation(BitmapImage aBitmapImage)
    {
        Debug.Assert(aBitmapImage != null);

        if (aBitmapImage.UriSource != null)
        {
            Decoder = new GifBitmapDecoder(
                aBitmapImage.UriSource,
                BitmapCreateOptions.PreservePixelFormat,
                BitmapCacheOption.Default);
        }
        else
        {
            aBitmapImage.StreamSource.Position = 0;
            Decoder = new GifBitmapDecoder(
                aBitmapImage.StreamSource,
                BitmapCreateOptions.PreservePixelFormat,
                BitmapCacheOption.Default);
        }

        Animation =
            new Int32Animation(
                0,
                Decoder.Frames.Count - 1,
                new Duration(
                    new TimeSpan(
                        0,
                        0,
                        0,
                        Decoder.Frames.Count / 10,
                        (int) ((Decoder.Frames.Count / 10.0 - Decoder.Frames.Count / 10) * 1000))))
                {
                    RepeatBehavior = RepeatBehavior.Forever
                };

        base.Source = Decoder.Frames[0];
        BeginAnimation(FrameIndexProperty, Animation);
        IsAnimationWorking = true;
    }

    private bool IsAnimatedGifImage(BitmapImage aBitmapImage)
    {
        Debug.Assert(aBitmapImage != null);

        bool lResult = false;
        if (aBitmapImage.UriSource != null)
        {
            BitmapDecoder lBitmapDecoder = BitmapDecoder.Create(
                aBitmapImage.UriSource,
                BitmapCreateOptions.PreservePixelFormat,
                BitmapCacheOption.Default);
            lResult = lBitmapDecoder is GifBitmapDecoder;
        }
        else if (aBitmapImage.StreamSource != null)
        {
            try
            {
                long lStreamPosition = aBitmapImage.StreamSource.Position;
                aBitmapImage.StreamSource.Position = 0;
                GifBitmapDecoder lBitmapDecoder =
                    new GifBitmapDecoder(
                        aBitmapImage.StreamSource,
                        BitmapCreateOptions.PreservePixelFormat,
                        BitmapCacheOption.Default);
                lResult = lBitmapDecoder.Frames.Count > 1;

                aBitmapImage.StreamSource.Position = lStreamPosition;
            }
            catch
            {
                lResult = false;
            }
        }

        return lResult;
    }

    private static void ChangingFrameIndex
        (DependencyObject aObject, DependencyPropertyChangedEventArgs aEventArgs)
    {
        AnimatedImage lAnimatedImage = aObject as AnimatedImage;

        if (lAnimatedImage == null || !lAnimatedImage.IsAnimationWorking)
        {
            return;
        }

        int lFrameIndex = (int) aEventArgs.NewValue;
        ((Image) lAnimatedImage).Source = lAnimatedImage.Decoder.Frames[lFrameIndex];
        lAnimatedImage.InvalidateVisual();
    }

    /// <summary>
    /// Handles changes to the Source property.
    /// </summary>
    private static void OnSourceChanged
        (DependencyObject aObject, DependencyPropertyChangedEventArgs aEventArgs)
    {
        ((AnimatedImage) aObject).OnSourceChanged(aEventArgs);
    }

    #endregion

    #region Dependency Properties

    /// <summary>
    /// FrameIndex Dependency Property
    /// </summary>
    public static readonly DependencyProperty FrameIndexProperty =
        DependencyProperty.Register(
            "FrameIndex",
            typeof (int),
            typeof (AnimatedImage),
            new UIPropertyMetadata(0, ChangingFrameIndex));

    /// <summary>
    /// Source Dependency Property
    /// </summary>
    public new static readonly DependencyProperty SourceProperty =
        DependencyProperty.Register(
            "Source",
            typeof (ImageSource),
            typeof (AnimatedImage),
            new FrameworkPropertyMetadata(
                null,
                FrameworkPropertyMetadataOptions.AffectsRender |
                FrameworkPropertyMetadataOptions.AffectsMeasure,
                OnSourceChanged));

    #endregion
}

15
รหัสนี้เป็นส่วนหนึ่งของโครงการของฉัน ฉันเป็นนักพัฒนาชาวรัสเซียที่ทำงานในรัสเซีย ดังนั้นความคิดเห็นก็เป็นภาษารัสเซียเช่นกัน ไม่ใช่ทุกโครงการในโลกที่เป็นโครงการ "อเมริกัน - อังกฤษ" คอเรย์
Mike Eshva

2
ลองใช้รหัสของคุณด้วยมาร์กอัปต่อไปนี้: <local: AnimatedImage Source = "/ Resources / ajax-loader.gif" /> แต่ไม่มีอะไรเกิดขึ้นเลย
Sonic Soul

ถ้าฉันเปลี่ยนเป็นการใช้ jpeg มันจะแสดงภาพนิ่ง ไม่ใช่แค่ gif nice code BTW
Sonic Soul

ยอดเยี่ยมฉันต้องการโซลูชันที่สามารถทำได้ แต่เป็น GIF จากพจนานุกรมทรัพยากร -> BitmapImage -> GIF แบบเคลื่อนไหว นี่ไง!
mtbennett

9

ฉันใช้ห้องสมุดนี้: https://github.com/XamlAnimatedGif/WpfAnimatedGif

ก่อนอื่นให้ติดตั้งไลบรารีในโครงการของคุณ (โดยใช้ Package Manager Console):

    PM > Install-Package WpfAnimatedGif

จากนั้นใช้ข้อมูลโค้ดนี้เป็นไฟล์ XAML:

    <Window x:Class="WpfAnimatedGif.Demo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:gif="http://wpfanimatedgif.codeplex.com"
        Title="MainWindow" Height="350" Width="525">
        <Grid>
            <Image gif:ImageBehavior.AnimatedSource="Images/animated.gif" />
        ...

ฉันหวังว่าจะช่วย

ที่มา: https://github.com/XamlAnimatedGif/WpfAnimatedGif


3
นี่คือคำตอบเดียวกัน (รายละเอียดน้อยกว่า) เหมือนกับคำตอบหนึ่งของ @ IgorVaschuk ตั้งแต่เดือนมิถุนายน 2012 ซึ่งเป็นวิธีแก้ปัญหาอันดับสอง
Heath Carroll

5

โดยพื้นฐานแล้วโซลูชัน PictureBox เดียวกันข้างต้น แต่คราวนี้ใช้รหัส behind เพื่อใช้ทรัพยากรฝังตัวในโครงการของคุณ:

ใน XAML:

<WindowsFormsHost x:Name="_loadingHost">
  <Forms:PictureBox x:Name="_loadingPictureBox"/>
</WindowsFormsHost>

ใน Code-Behind:

public partial class ProgressIcon
{
    public ProgressIcon()
    {
        InitializeComponent();
        var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("My.Namespace.ProgressIcon.gif");
        var image = System.Drawing.Image.FromStream(stream);
        Loaded += (s, e) => _loadingPictureBox.Image = image;
    }
}

นอกจากนี้ที่ดี เพิ่มความคล่องตัวจากสิ่งที่ฉันสามารถบอกได้ (ที่กล่าวว่าฉันไม่ได้เขียนใน WPF ในช่วงสามปีที่ผ่านมาตอนนี้)
CodeMouse92

ฉันไม่คิดว่านี่เป็นความคิดที่ดีเพราะเหตุผลหลักข้อหนึ่งที่คุณใช้กับ WPF ก็เนื่องมาจากการปรับขนาดการแสดงผล คุณจะพบกับสิ่งประดิษฐ์หนึ่ง (ภาพ) ที่ไม่ได้ปรับขนาดอย่างเหมาะสม
มัฟฟินแมน

5

ฉันแก้ไขโค้ดของ Mike Eshva และฉันทำให้มันทำงานได้ดีขึ้นคุณสามารถใช้กับ 1frame jpg png bmp หรือ mutil-frame gif หากคุณต้องการผูก uri กับตัวควบคุมให้ผูกคุณสมบัติ UriSource หรือคุณต้องการผูกอะไรก็ได้ใน สตรีมหน่วยความจำที่คุณผูกเน็คไททรัพย์สินที่เหมาะสมซึ่งเป็น BitmapImage

    /// <summary> 
/// Элемент управления "Изображения", поддерживающий анимированные GIF. 
/// </summary> 
public class AnimatedImage : Image
{
    static AnimatedImage()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(AnimatedImage), new FrameworkPropertyMetadata(typeof(AnimatedImage)));
    }

    #region Public properties

    /// <summary> 
    /// Получает/устанавливает номер текущего кадра. 
    /// </summary> 
    public int FrameIndex
    {
        get { return (int)GetValue(FrameIndexProperty); }
        set { SetValue(FrameIndexProperty, value); }
    }

    /// <summary>
    /// Get the BitmapFrame List.
    /// </summary>
    public List<BitmapFrame> Frames { get; private set; }

    /// <summary>
    /// Get or set the repeatBehavior of the animation when source is gif formart.This is a dependency object.
    /// </summary>
    public RepeatBehavior AnimationRepeatBehavior
    {
        get { return (RepeatBehavior)GetValue(AnimationRepeatBehaviorProperty); }
        set { SetValue(AnimationRepeatBehaviorProperty, value); }
    }

    public new BitmapImage Source
    {
        get { return (BitmapImage)GetValue(SourceProperty); }
        set { SetValue(SourceProperty, value); }
    }

    public Uri UriSource
    {
        get { return (Uri)GetValue(UriSourceProperty); }
        set { SetValue(UriSourceProperty, value); }
    }

    #endregion

    #region Protected interface

    /// <summary> 
    /// Provides derived classes an opportunity to handle changes to the Source property. 
    /// </summary> 
    protected virtual void OnSourceChanged(DependencyPropertyChangedEventArgs e)
    {
        ClearAnimation();
        BitmapImage source;
        if (e.NewValue is Uri)
        {
            source = new BitmapImage();
            source.BeginInit();
            source.UriSource = e.NewValue as Uri;
            source.CacheOption = BitmapCacheOption.OnLoad;
            source.EndInit();
        }
        else if (e.NewValue is BitmapImage)
        {
            source = e.NewValue as BitmapImage;
        }
        else
        {
            return;
        }
        BitmapDecoder decoder;
        if (source.StreamSource != null)
        {
            decoder = BitmapDecoder.Create(source.StreamSource, BitmapCreateOptions.DelayCreation, BitmapCacheOption.OnLoad);
        }
        else if (source.UriSource != null)
        {
            decoder = BitmapDecoder.Create(source.UriSource, BitmapCreateOptions.DelayCreation, BitmapCacheOption.OnLoad);
        }
        else
        {
            return;
        }
        if (decoder.Frames.Count == 1)
        {
            base.Source = decoder.Frames[0];
            return;
        }

        this.Frames = decoder.Frames.ToList();

        PrepareAnimation();
    }

    #endregion

    #region Private properties

    private Int32Animation Animation { get; set; }
    private bool IsAnimationWorking { get; set; }

    #endregion

    #region Private methods

    private void ClearAnimation()
    {
        if (Animation != null)
        {
            BeginAnimation(FrameIndexProperty, null);
        }

        IsAnimationWorking = false;
        Animation = null;
        this.Frames = null;
    }

    private void PrepareAnimation()
    {
        Animation =
            new Int32Animation(
                0,
                this.Frames.Count - 1,
                new Duration(
                    new TimeSpan(
                        0,
                        0,
                        0,
                        this.Frames.Count / 10,
                        (int)((this.Frames.Count / 10.0 - this.Frames.Count / 10) * 1000))))
            {
                RepeatBehavior = RepeatBehavior.Forever
            };

        base.Source = this.Frames[0];
        BeginAnimation(FrameIndexProperty, Animation);
        IsAnimationWorking = true;
    }

    private static void ChangingFrameIndex
        (DependencyObject dp, DependencyPropertyChangedEventArgs e)
    {
        AnimatedImage animatedImage = dp as AnimatedImage;

        if (animatedImage == null || !animatedImage.IsAnimationWorking)
        {
            return;
        }

        int frameIndex = (int)e.NewValue;
        ((Image)animatedImage).Source = animatedImage.Frames[frameIndex];
        animatedImage.InvalidateVisual();
    }

    /// <summary> 
    /// Handles changes to the Source property. 
    /// </summary> 
    private static void OnSourceChanged
        (DependencyObject dp, DependencyPropertyChangedEventArgs e)
    {
        ((AnimatedImage)dp).OnSourceChanged(e);
    }

    #endregion

    #region Dependency Properties

    /// <summary> 
    /// FrameIndex Dependency Property 
    /// </summary> 
    public static readonly DependencyProperty FrameIndexProperty =
        DependencyProperty.Register(
            "FrameIndex",
            typeof(int),
            typeof(AnimatedImage),
            new UIPropertyMetadata(0, ChangingFrameIndex));

    /// <summary> 
    /// Source Dependency Property 
    /// </summary> 
    public new static readonly DependencyProperty SourceProperty =
        DependencyProperty.Register(
            "Source",
            typeof(BitmapImage),
            typeof(AnimatedImage),
            new FrameworkPropertyMetadata(
                null,
                FrameworkPropertyMetadataOptions.AffectsRender |
                FrameworkPropertyMetadataOptions.AffectsMeasure,
                OnSourceChanged));

    /// <summary>
    /// AnimationRepeatBehavior Dependency Property
    /// </summary>
    public static readonly DependencyProperty AnimationRepeatBehaviorProperty =
        DependencyProperty.Register(
        "AnimationRepeatBehavior",
        typeof(RepeatBehavior),
        typeof(AnimatedImage),
        new PropertyMetadata(null));

    public static readonly DependencyProperty UriSourceProperty =
        DependencyProperty.Register(
        "UriSource",
        typeof(Uri),
        typeof(AnimatedImage),
                new FrameworkPropertyMetadata(
                null,
                FrameworkPropertyMetadataOptions.AffectsRender |
                FrameworkPropertyMetadataOptions.AffectsMeasure,
                OnSourceChanged));

    #endregion
}

นี่คือการควบคุมที่กำหนดเอง คุณต้องสร้างมันในโครงการแอป WPF และลบเทมเพลตแทนที่ในสไตล์


1
ฉันต้องตั้งค่า UriSource เป็น pack: // application: ,,, / Images / loader.gif การตั้งค่า UriSource หรือ Source เป็น Uri ที่เกี่ยวข้องล้มเหลวขณะใช้งานจริง
Farzan

ใช่ฉันได้ลองแล้วและฉันได้รับการยกเว้น มันไม่ทำงานกับ uris ที่เกี่ยวข้อง
SuperJMN

3

ฉันมีปัญหานี้จนกระทั่งฉันค้นพบว่าใน WPF4 คุณสามารถจำลองภาพเคลื่อนไหวภาพเฟรมหลักของคุณเองได้ ก่อนอื่นแบ่งภาพเคลื่อนไหวของคุณออกเป็นชุดภาพตั้งชื่อภาพเหล่านั้นเช่น "Image1.gif", "Image2, gif" และอื่น ๆ นำเข้ารูปภาพเหล่านั้นไปยังแหล่งโซลูชันของคุณ ฉันสมมติว่าคุณใส่ไว้ในตำแหน่งทรัพยากรเริ่มต้นสำหรับรูปภาพ

คุณกำลังจะใช้การควบคุมรูปภาพ ใช้รหัส XAML ต่อไปนี้ ฉันลบสิ่งที่ไม่จำเป็นออกแล้ว

<Image Name="Image1">
   <Image.Triggers>
      <EventTrigger RoutedEvent="Image.Loaded"
         <EventTrigger.Actions>
            <BeginStoryboard>
               <Storyboard>
                   <ObjectAnimationUsingKeyFrames Duration="0:0:1" Storyboard.TargetProperty="Source" RepeatBehavior="Forever">
                      <DiscreteObjectKeyFrames KeyTime="0:0:0">
                         <DiscreteObjectKeyFrame.Value>
                            <BitmapImage UriSource="Images/Image1.gif"/>
                         </DiscreteObjectKeyFrame.Value>
                      </DiscreteObjectKeyFrames>
                     <DiscreteObjectKeyFrames KeyTime="0:0:0.25">
                        <DiscreteObjectKeyFrame.Value>
                           <BitmapImage UriSource="Images/Image2.gif"/>
                        </DiscreteObjectKeyFrame.Value>
                     </DiscreteObjectKeyFrames>
                     <DiscreteObjectKeyFrames KeyTime="0:0:0.5">
                        <DiscreteObjectKeyFrame.Value>
                           <BitmapImage UriSource="Images/Image3.gif"/>
                        </DiscreteObjectKeyFrame.Value>
                     </DiscreteObjectKeyFrames>
                     <DiscreteObjectKeyFrames KeyTime="0:0:0.75">
                        <DiscreteObjectKeyFrame.Value>
                           <BitmapImage UriSource="Images/Image4.gif"/>
                        </DiscreteObjectKeyFrame.Value>
                     </DiscreteObjectKeyFrames>
                     <DiscreteObjectKeyFrames KeyTime="0:0:1">
                        <DiscreteObjectKeyFrame.Value>
                           <BitmapImage UriSource="Images/Image5.gif"/>
                        </DiscreteObjectKeyFrame.Value>
                     </DiscreteObjectKeyFrames>
                  </ObjectAnimationUsingKeyFrames>
               </Storyboard>
            </BeginStoryboard>
         </EventTrigger.Actions>
      </EventTrigger>
   </Image.Triggers>
</Image>

1
ปรากฏว่าข้อเสียของวิธีนี้คือโดยปกติแล้วการเคลื่อนไหวจะดำเนินต่อไปแม้ว่าจะถูกยุบแล้วซึ่งอาจทำให้ประสิทธิภาพลดลง
ลินน์

ไม่ใช่ DiscreteObjectKeyFrames แต่เป็น DiscreteObjectKeyFrame เอกพจน์.
jairhumberto

@jairhumberto ฉันคิดว่าอาจมีการเปลี่ยนแปลงระหว่างรุ่น นี่ค่อนข้างเก่า (2011) แต่ฉันใช้รหัสนี้แน่นอนในโครงการ
CodeMouse92

3

ขอบคุณสำหรับ Joel โพสต์ของคุณมันช่วยฉันแก้ปัญหา WPF ที่ขาดการสนับสนุน GIF เคลื่อนไหว เพียงแค่เพิ่มรหัสเล็กน้อยเนื่องจากฉันมีเวลากับการตั้งค่าคุณสมบัติ pictureBoxLoading.Image เนื่องจาก Winforms api

ฉันต้องตั้งค่า Build Action ของภาพเคลื่อนไหว gif เป็น "เนื้อหา" และไดเรกทอรีคัดลอกไปยังเอาต์พุตเป็น "คัดลอกถ้าใหม่กว่า" หรือ "เสมอ" จากนั้นใน MainWindow () ฉันเรียกวิธีนี้ ปัญหาเดียวก็คือเมื่อฉันพยายามที่จะกำจัดกระแสมันให้ฉันกราฟิกซองจดหมายสีแดงแทนภาพของฉัน ฉันจะต้องแก้ปัญหานั้น สิ่งนี้ลบความเจ็บปวดของการโหลด BitmapImage และเปลี่ยนเป็น Bitmap (ซึ่งเห็นได้ชัดว่าฆ่าภาพเคลื่อนไหวของฉันเพราะมันไม่ใช่ gif อีกต่อไป)

private void SetupProgressIcon()
{
   Uri uri = new Uri("pack://application:,,,/WPFTest;component/Images/animated_progress_apple.gif");
   if (uri != null)
   {
      Stream stream = Application.GetContentStream(uri).Stream;   
      imgProgressBox.Image = new System.Drawing.Bitmap(stream);
   }
}

เรื่อง: เมื่อฉันพยายามกำจัดกระแสข้อมูลตาม MSDN บิตแมปที่ใช้สตรีมต้องมีสตรีมให้มีชีวิตตลอดชีวิตของบิตแมป การแก้ไขคือการตรึงหรือโคลนบิตแมป
Jesse Chisholm

1
เขาเพียงแค่ต้องการที่จะพูดกับชุดแทน.ImageLocation .Imageเขามีวิธีที่ผิด .ImageLocationทำงานออกรากของโครงการ Visual Studio เพื่อบอกว่าคุณมีโฟลเดอร์เส้นทางของคุณแล้วImages imgBox.ImageLocation = "/Images/my.gif";ถ้าคุณมีโฟลเดอร์ที่เรียกว่าViewsที่คุณต้องดูที่จะแสดงภาพที่จะได้รับกลับไปที่Imagesคุณจะต้องใช้ 2 imgBox.ImageLocation = "../Images/my.gif";จุด:
vapcguy

1

ฉันได้ลองทุกอย่างที่กล่าวมาแล้ว แต่ทุกคนก็มีช่วงเวลาสั้น ๆ และขอบคุณทุก ๆ คนที่ทำให้ฉันใช้ GifImage ของตัวเอง:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Controls;
    using System.Windows;
    using System.Windows.Media.Imaging;
    using System.IO;
    using System.Windows.Threading;

    namespace IEXM.Components
    {
    public class GifImage : Image
    {
            #region gif Source, such as "/IEXM;component/Images/Expression/f020.gif"
            public string GifSource
            {
                    get { return (string)GetValue(GifSourceProperty); }
                    set { SetValue(GifSourceProperty, value); }
            }

            public static readonly DependencyProperty GifSourceProperty =
                    DependencyProperty.Register("GifSource", typeof(string),
                    typeof(GifImage), new UIPropertyMetadata(null, GifSourcePropertyChanged));

            private static void GifSourcePropertyChanged(DependencyObject sender,
                    DependencyPropertyChangedEventArgs e)
            {
                    (sender as GifImage).Initialize();
            }
            #endregion

            #region control the animate
            /// <summary>
            /// Defines whether the animation starts on it's own
            /// </summary>
            public bool IsAutoStart
            {
                    get { return (bool)GetValue(AutoStartProperty); }
                    set { SetValue(AutoStartProperty, value); }
            }

            public static readonly DependencyProperty AutoStartProperty =
                    DependencyProperty.Register("IsAutoStart", typeof(bool),
                    typeof(GifImage), new UIPropertyMetadata(false, AutoStartPropertyChanged));

            private static void AutoStartPropertyChanged(DependencyObject sender,
                    DependencyPropertyChangedEventArgs e)
            {
                    if ((bool)e.NewValue)
                            (sender as GifImage).StartAnimation();
                    else
                            (sender as GifImage).StopAnimation();
            }
            #endregion

            private bool _isInitialized = false;
            private System.Drawing.Bitmap _bitmap;
            private BitmapSource _source;

            [System.Runtime.InteropServices.DllImport("gdi32.dll")]
            public static extern bool DeleteObject(IntPtr hObject);

            private BitmapSource GetSource()
            {
                    if (_bitmap == null)
                    {
                            _bitmap = new System.Drawing.Bitmap(Application.GetResourceStream(
                                     new Uri(GifSource, UriKind.RelativeOrAbsolute)).Stream);
                    }

                    IntPtr handle = IntPtr.Zero;
                    handle = _bitmap.GetHbitmap();

                    BitmapSource bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                            handle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
                    DeleteObject(handle);
                    return bs;
            }

            private void Initialize()
            {
            //        Console.WriteLine("Init: " + GifSource);
                    if (GifSource != null)
                            Source = GetSource();
                    _isInitialized = true;
            }

            private void FrameUpdatedCallback()
            {
                    System.Drawing.ImageAnimator.UpdateFrames();

                    if (_source != null)
                    {
                            _source.Freeze();
                    }

               _source = GetSource();

              //  Console.WriteLine("Working: " + GifSource);

                    Source = _source;
                    InvalidateVisual();
            }

            private void OnFrameChanged(object sender, EventArgs e)
            {
                    Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(FrameUpdatedCallback));
            }

            /// <summary>
            /// Starts the animation
            /// </summary>
            public void StartAnimation()
            {
                    if (!_isInitialized)
                            this.Initialize();


             //   Console.WriteLine("Start: " + GifSource);

                    System.Drawing.ImageAnimator.Animate(_bitmap, OnFrameChanged);
            }

            /// <summary>
            /// Stops the animation
            /// </summary>
            public void StopAnimation()
            {
                    _isInitialized = false;
                    if (_bitmap != null)
                    {
                            System.Drawing.ImageAnimator.StopAnimate(_bitmap, OnFrameChanged);
                            _bitmap.Dispose();
                            _bitmap = null;
                    }
                    _source = null;
                    Initialize();
                    GC.Collect();
                    GC.WaitForFullGCComplete();

             //   Console.WriteLine("Stop: " + GifSource);
            }

            public void Dispose()
            {
                    _isInitialized = false;
                    if (_bitmap != null)
                    {
                            System.Drawing.ImageAnimator.StopAnimate(_bitmap, OnFrameChanged);
                            _bitmap.Dispose();
                            _bitmap = null;
                    }
                    _source = null;
                    GC.Collect();
                    GC.WaitForFullGCComplete();
               // Console.WriteLine("Dispose: " + GifSource);
            }
    }
}

การใช้งาน:

<localComponents:GifImage x:Name="gifImage" IsAutoStart="True" GifSource="{Binding Path=value}" />

เนื่องจากมันจะไม่ทำให้หน่วยความจำรั่วและเคลื่อนไหวตามเส้นเวลาของภาพ gif คุณสามารถลองได้


ตัวอย่างที่ยอดเยี่ยม ต้องการเริ่มต้นอัปเดตเพื่อตรวจสอบIsAutoStartแต่อย่างอื่นทำงานเหมือนแชมป์!
Steve Danner

1
การเรียก GC.Collect () อย่างชัดเจนมีผลกระทบต่อประสิทธิภาพการทำงานที่น่ากลัว
Kędrzu

0

ก่อนหน้านี้ฉันประสบปัญหาคล้ายกันฉันต้องเล่น.gifไฟล์ในโครงการของคุณ ฉันมีสองทางเลือก:

  • ใช้ PictureBox จาก WinForms

  • ใช้ไลบรารีบุคคลที่สามเช่น WPFAnimatedGif จากcodeplex.com

เวอร์ชันที่มีPictureBoxไม่ทำงานสำหรับฉันและโครงการไม่สามารถใช้ไลบรารีภายนอกได้ ดังนั้นผมจึงทำเพื่อตัวเองผ่านด้วยความช่วยเหลือBitmap ImageAnimatorเนื่องจากมาตรฐานBitmapImageไม่รองรับการเล่น.gifไฟล์

ตัวอย่างเต็มรูปแบบ:

XAML

<Window x:Class="PlayGifHelp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" Loaded="MainWindow_Loaded">

    <Grid>
        <Image x:Name="SampleImage" />
    </Grid>
</Window>

Code behind

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    Bitmap _bitmap;
    BitmapSource _source;

    private BitmapSource GetSource()
    {
        if (_bitmap == null)
        {
            string path = Directory.GetCurrentDirectory();

            // Check the path to the .gif file
            _bitmap = new Bitmap(path + @"\anim.gif");
        }

        IntPtr handle = IntPtr.Zero;
        handle = _bitmap.GetHbitmap();

        return Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
    }

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        _source = GetSource();
        SampleImage.Source = _source;
        ImageAnimator.Animate(_bitmap, OnFrameChanged);
    }

    private void FrameUpdatedCallback()
    {
        ImageAnimator.UpdateFrames();

        if (_source != null)
        {
            _source.Freeze();
        }

        _source = GetSource();

        SampleImage.Source = _source;
        InvalidateVisual();
    }

    private void OnFrameChanged(object sender, EventArgs e)
    {
        Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(FrameUpdatedCallback));
    }
}

Bitmapไม่รองรับคำสั่งURIดังนั้นฉันโหลด.gifไฟล์จากไดเรกทอรีปัจจุบัน


0

การปรับปรุงGifImage.Initialize()วิธีการเล็กน้อยซึ่งอ่านเวลาเฟรมที่เหมาะสมจากข้อมูลเมตาของ GIF

    private void Initialize()
    {
        _gifDecoder = new GifBitmapDecoder(new Uri("pack://application:,,," + this.GifSource), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);

        int duration=0;
        _animation = new Int32AnimationUsingKeyFrames();
        _animation.KeyFrames.Add(new DiscreteInt32KeyFrame(0, KeyTime.FromTimeSpan(new TimeSpan(0))));
        foreach (BitmapFrame frame in _gifDecoder.Frames)
        {
            BitmapMetadata btmd = (BitmapMetadata)frame.Metadata;
            duration += (ushort)btmd.GetQuery("/grctlext/Delay");
            _animation.KeyFrames.Add(new DiscreteInt32KeyFrame(_gifDecoder.Frames.IndexOf(frame)+1, KeyTime.FromTimeSpan(new TimeSpan(duration*100000))));
        }            
         _animation.RepeatBehavior = RepeatBehavior.Forever;
        this.Source = _gifDecoder.Frames[0];            
        _isInitialized = true;
    }

0

ฉันไม่แน่ใจว่าสิ่งนี้ได้รับการแก้ไขหรือไม่ แต่วิธีที่ดีที่สุดคือการใช้ไลบรารี WpfAnimatedGidห้องสมุดมันง่ายมากง่ายและตรงไปตรงมาเพื่อใช้ ต้องใช้รหัส XAML 2 บรรทัดและรหัส C # ประมาณ 5 บรรทัดในรหัสที่อยู่ด้านหลัง

คุณจะเห็นรายละเอียดที่จำเป็นทั้งหมดเกี่ยวกับวิธีการใช้สิ่งนี้ นี่คือสิ่งที่ฉันใช้แทนการประดิษฐ์ล้ออีกครั้ง


0

การเพิ่มไปยังการตอบกลับหลักที่แนะนำให้ใช้WpfAnimatedGifคุณต้องเพิ่มบรรทัดต่อไปนี้ในท้ายที่สุดหากคุณสลับภาพกับ Gifเพื่อให้แน่ใจว่าภาพเคลื่อนไหวทำงานจริง:

ImageBehavior.SetRepeatBehavior(img, new RepeatBehavior(0));
ImageBehavior.SetRepeatBehavior(img, RepeatBehavior.Forever);

ดังนั้นรหัสของคุณจะมีลักษณะดังนี้:

var image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri(fileName);
image.EndInit();
ImageBehavior.SetAnimatedSource(img, image);
ImageBehavior.SetRepeatBehavior(img, new RepeatBehavior(0));
ImageBehavior.SetRepeatBehavior(img, RepeatBehavior.Forever);

0

ตรวจสอบรหัสของฉันฉันหวังว่านี่จะช่วยคุณได้ :)

         public async Task GIF_Animation_Pro(string FileName,int speed,bool _Repeat)
                    {
    int ab=0;
                        var gif = GifBitmapDecoder.Create(new Uri(FileName), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
                        var getFrames = gif.Frames;
                        BitmapFrame[] frames = getFrames.ToArray();
                        await Task.Run(() =>
                        {


                            while (ab < getFrames.Count())
                            {
                                Thread.Sleep(speed);
try
{
                                Dispatcher.Invoke(() =>
                                {
                                    gifImage.Source = frames[ab];
                                });
                                if (ab == getFrames.Count - 1&&_Repeat)
                                {
                                    ab = 0;

                                }
                                ab++;
            }
 catch
{
}

                            }
                        });
                    }

หรือ

     public async Task GIF_Animation_Pro(Stream stream, int speed,bool _Repeat)
            {
 int ab = 0;   
                var gif = GifBitmapDecoder.Create(stream , BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
                var getFrames = gif.Frames;
                BitmapFrame[] frames = getFrames.ToArray();
                await Task.Run(() =>
                {


                    while (ab < getFrames.Count())
                    {
                        Thread.Sleep(speed);
    try
    {


                     Dispatcher.Invoke(() =>
                        {
                            gifImage.Source = frames[ab];
                        });
                        if (ab == getFrames.Count - 1&&_Repeat)
                        {
                            ab = 0;

                        }
                        ab++;
    }
     catch{} 



                    }
                });
            }

0

ทางเลือกสำหรับการรอภาพเคลื่อนไหวใน WPF คือ:

 <ProgressBar Height="20" Width="100" IsIndeterminate="True"/>

มันจะแสดงแถบความคืบหน้าเคลื่อนไหว


1
คำถามไม่จำเป็นต้องถามเกี่ยวกับภาพเคลื่อนไหวที่รอ - แต่เป็นการถามเกี่ยวกับภาพเคลื่อนไหว GIF โดยทั่วไป เห็นได้ชัดว่าอาจเป็นภาพเคลื่อนไหวที่รออยู่ซึ่งในกรณีนี้อาจเป็นทางเลือกที่เหมาะสม แต่ก็สามารถทำได้อย่างง่ายดายสำหรับความต้องการสื่ออื่น ๆ
Jeremy Caney
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.