สิ่งที่ประเภทควบคุมฉันควรใช้ - Image
, MediaElement
ฯลฯ ?
สิ่งที่ประเภทควบคุมฉันควรใช้ - Image
, MediaElement
ฯลฯ ?
คำตอบ:
ฉันไม่สามารถรับคำตอบที่เป็นที่นิยมที่สุดสำหรับคำถามนี้ (ด้านบนโดย 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
img
นี่คืออะไร
ฉันโพสต์โซลูชันที่ขยายการควบคุมรูปภาพและการใช้ตัวถอดรหัส 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" />
Int32AnimationUsingKeyFrames
gf.Frames[0].MetaData.GetQuery("/grctlext/Delay")
(ส่งคืน ushort ซึ่งเป็นระยะเวลาของเฟรมเป็นร้อยวินาที)
ฉันก็ทำการค้นหาและค้นพบวิธีแก้ปัญหาที่แตกต่างกันหลายอย่างในกระทู้ในฟอรัม 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
มันจะไม่โปร่งใสเลย ส่วนที่เหลือของWindow
พลัง ฉันคิดว่าคุณต้องลองดู
เกี่ยวกับแอพตัวเล็ก ๆ นี้: รหัสที่อยู่เบื้องหลัง:
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>
<MediaElement LoadedBehavior="Play" Source="{Binding MyGifFile}" >
- MyGifFile เป็นเพียงชื่อไฟล์ (และเส้นทาง) ของ gif เคลื่อนไหวของฉัน
ListBox
หรือผูกเลย? ฉันพยายามโดยไม่ผูกมัดเพียงแค่ใส่เส้นทางของไฟล์ในแหล่งที่มาและปรากฏขึ้น แต่ไม่เคลื่อนไหว หากฉันใช้การเชื่อมโยงแม้ว่าListBox
จะไม่ปรากฏขึ้นเลยสำหรับฉัน - มันจะให้ข้อยกเว้นว่าเส้นทางไฟล์ของฉันไม่ถูกต้องแม้ว่าจะเป็นเส้นทางเดียวกับที่ฉันใช้เมื่อปรากฏ
มันง่ายมากถ้าคุณใช้<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" />
public string SpinnerLogoPath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Assets\images\mso_spinninglogo_blue_2.gif");
สำหรับแหล่งที่มาและหาเส้นทางในรหัส: ตรวจสอบให้แน่ใจว่าตั้งค่าไฟล์เป็น Build = Content และคัดลอกไปยังไดเร็กทอรี output
นี่คือการควบคุมภาพเคลื่อนไหวในเวอร์ชันของฉัน คุณสามารถใช้แหล่งที่มาของคุณสมบัติมาตรฐานสำหรับการระบุแหล่งที่มาของรูปภาพ ฉันปรับปรุงมันต่อไป ฉันเป็นชาวรัสเซียโครงการเป็นภาษารัสเซียดังนั้นความคิดเห็นยังเป็นภาษารัสเซีย แต่อย่างไรก็ตามคุณควรเข้าใจทุกอย่างโดยปราศจากความคิดเห็น :)
/// <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
}
ฉันใช้ห้องสมุดนี้: 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" />
...
ฉันหวังว่าจะช่วย
โดยพื้นฐานแล้วโซลูชัน 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;
}
}
ฉันแก้ไขโค้ดของ 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 และลบเทมเพลตแทนที่ในสไตล์
ฉันมีปัญหานี้จนกระทั่งฉันค้นพบว่าใน 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>
ขอบคุณสำหรับ 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);
}
}
.ImageLocation
.Image
เขามีวิธีที่ผิด .ImageLocation
ทำงานออกรากของโครงการ Visual Studio เพื่อบอกว่าคุณมีโฟลเดอร์เส้นทางของคุณแล้วImages
imgBox.ImageLocation = "/Images/my.gif";
ถ้าคุณมีโฟลเดอร์ที่เรียกว่าViews
ที่คุณต้องดูที่จะแสดงภาพที่จะได้รับกลับไปที่Images
คุณจะต้องใช้ 2 imgBox.ImageLocation = "../Images/my.gif";
จุด:
ฉันได้ลองทุกอย่างที่กล่าวมาแล้ว แต่ทุกคนก็มีช่วงเวลาสั้น ๆ และขอบคุณทุก ๆ คนที่ทำให้ฉันใช้ 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
แต่อย่างอื่นทำงานเหมือนแชมป์!
ก่อนหน้านี้ฉันประสบปัญหาคล้ายกันฉันต้องเล่น.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
ไฟล์จากไดเรกทอรีปัจจุบัน
การปรับปรุง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;
}
ฉันไม่แน่ใจว่าสิ่งนี้ได้รับการแก้ไขหรือไม่ แต่วิธีที่ดีที่สุดคือการใช้ไลบรารี WpfAnimatedGidห้องสมุดมันง่ายมากง่ายและตรงไปตรงมาเพื่อใช้ ต้องใช้รหัส XAML 2 บรรทัดและรหัส C # ประมาณ 5 บรรทัดในรหัสที่อยู่ด้านหลัง
คุณจะเห็นรายละเอียดที่จำเป็นทั้งหมดเกี่ยวกับวิธีการใช้สิ่งนี้ นี่คือสิ่งที่ฉันใช้แทนการประดิษฐ์ล้ออีกครั้ง
การเพิ่มไปยังการตอบกลับหลักที่แนะนำให้ใช้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);
ตรวจสอบรหัสของฉันฉันหวังว่านี่จะช่วยคุณได้ :)
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{}
}
});
}
ทางเลือกสำหรับการรอภาพเคลื่อนไหวใน WPF คือ:
<ProgressBar Height="20" Width="100" IsIndeterminate="True"/>
มันจะแสดงแถบความคืบหน้าเคลื่อนไหว