Quel type de contrôle dois - je utiliser - Image
, MediaElement
etc.?
Quel type de contrôle dois - je utiliser - Image
, MediaElement
etc.?
Réponses:
Je n'ai pas pu obtenir la réponse la plus populaire à cette question (ci-dessus par Dario) pour fonctionner correctement. Le résultat a été une animation étrange et saccadée avec des artefacts étranges. Meilleure solution que j'ai trouvée jusqu'à présent: https://github.com/XamlAnimatedGif/WpfAnimatedGif
Vous pouvez l'installer avec NuGet
PM> Install-Package WpfAnimatedGif
et pour l'utiliser, dans un nouvel espace de noms dans la fenêtre où vous souhaitez ajouter l'image gif et l'utiliser comme ci-dessous
<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" />
Le package est vraiment soigné, vous pouvez définir certains attributs comme ci-dessous
<Image gif:ImageBehavior.RepeatBehavior="3x"
gif:ImageBehavior.AnimatedSource="Images/animated.gif" />
et vous pouvez également l'utiliser dans votre code:
var image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri(fileName);
image.EndInit();
ImageBehavior.SetAnimatedSource(img, image);
EDIT: prise en charge de Silverlight
Selon le commentaire de josh2112 si vous souhaitez ajouter un support GIF animé à votre projet Silverlight, utilisez github.com/XamlAnimatedGif/XamlAnimatedGif
img
ici?
Je poste une solution étendant le contrôle d'image et utilisant le décodeur Gif. Le décodeur gif a une propriété frames. J'anime la FrameIndex
propriété. L'événement ChangingFrameIndex
change la propriété source en la trame correspondant à FrameIndex
(c'est-à-dire dans le décodeur). Je suppose que le gif a 10 images par seconde.
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);
}
}
Exemple d'utilisation (XAML):
<controls:GifImage x:Name="gifImage" Stretch="None" GifSource="/SomeImage.gif" AutoStart="True" />
Int32AnimationUsingKeyFrames
gf.Frames[0].MetaData.GetQuery("/grctlext/Delay")
(retourne une courte durée qui est la durée de la trame en centaines de secondes)
Moi aussi, j'ai fait une recherche et trouvé plusieurs solutions différentes dans un seul fil sur les anciens forums MSDN. (le lien ne fonctionnait plus, je l'ai donc supprimé)
Le plus simple à exécuter semble être d'utiliser un PictureBox
contrôle WinForms , et est allé comme ça (a changé quelques choses du fil, la plupart du même).
Ajoutez d'abord une référence à System.Windows.Forms
, WindowsFormsIntegration
et System.Drawing
à votre projet.
<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 >
Ensuite, dans le Window_Loaded
gestionnaire, vous devez définir la pictureBoxLoading.ImageLocation
propriété sur le chemin du fichier image que vous souhaitez afficher.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
pictureBoxLoading.ImageLocation = "../Images/mygif.gif";
}
Le MediaElement
contrôle a été mentionné dans ce fil, mais il est également mentionné qu'il s'agit d'un contrôle plutôt lourd, il y avait donc un certain nombre d'alternatives, y compris au moins 2 contrôles homebrewed basés sur le Image
contrôle, c'est donc le plus simple.
AllowTransparency="True"
. Que cela produise ou non les résultats que vous avez en tête est une autre question. Je ne l'ai pas essayé moi-même, mais je parierais que WindowsFormsHost
cela ne deviendrait pas transparent du tout. Le reste de la Window
puissance. Vous devrez simplement l'essayer, je pense.
Que diriez-vous de cette petite application: code derrière:
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}" >
- Le MyGifFile est juste le nom de fichier (et le chemin) de mon gif animé.
ListBox
, ou de se lier du tout? Je l'ai essayé sans liaison, mettez simplement le chemin du fichier dans la source et il apparaît, mais ne s'anime pas. Si j'utilise la liaison, même avec le ListBox
, cela ne se produit pas du tout, pour moi - cela me donnera une exception que mon chemin de fichier est incorrect, même s'il est le même que celui que j'utilise lorsqu'il apparaît.
C'est très simple si vous utilisez <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");
. Assurez-vous de définir le fichier sur Build = Content et de le copier dans le répertoire de sortie.
Voici ma version du contrôle d'image animée. Vous pouvez utiliser la propriété standard Source pour spécifier la source d'image. Je l'ai encore amélioré. Je suis russe, le projet est russe donc les commentaires sont également en russe. Mais de toute façon, vous devriez pouvoir tout comprendre sans commentaires. :)
/// <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
}
J'utilise cette bibliothèque: https://github.com/XamlAnimatedGif/WpfAnimatedGif
Tout d'abord, installez la bibliothèque dans votre projet (à l'aide de la console du gestionnaire de packages):
PM > Install-Package WpfAnimatedGif
Ensuite, utilisez cet extrait dans le fichier 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" />
...
J'espère que ça aide.
Fondamentalement, la même solution PictureBox ci-dessus, mais cette fois avec le code-behind pour utiliser une ressource intégrée dans votre projet:
En XAML:
<WindowsFormsHost x:Name="_loadingHost">
<Forms:PictureBox x:Name="_loadingPictureBox"/>
</WindowsFormsHost>
Dans 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;
}
}
J'ai modifié le code de Mike Eshva, et je l'ai fait mieux fonctionner. Vous pouvez l'utiliser avec 1frame jpg png bmp ou mutil-frame gif.Si vous voulez lier un uri au contrôle, liez les propriétés UriSource ou vous voulez lier n'importe quel in- flux de mémoire que vous liez à la propriété Source qui est un 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
}
Il s'agit d'un contrôle personnalisé. Vous devez le créer dans WPF App Project et supprimer le remplacement de modèle avec style.
J'ai eu ce problème, jusqu'à ce que je découvre que dans WPF4, vous pouvez simuler vos propres animations d'images clés. Tout d'abord, divisez votre animation en une série d'images, nommez-les quelque chose comme "Image1.gif", "Image2, gif", etc. Importez ces images dans les ressources de votre solution. Je suppose que vous les avez placés dans l'emplacement de ressource par défaut pour les images.
Vous allez utiliser le contrôle Image. Utilisez le code XAML suivant. J'ai supprimé les éléments non essentiels.
<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>
Merci pour votre message Joel, cela m'a aidé à résoudre l'absence de prise en charge de WPF pour les GIF animés. Je viens d'ajouter un peu de code car j'ai eu beaucoup de mal à définir la propriété pictureBoxLoading.Image en raison de l'API Winforms.
J'ai dû définir l'action de construction de mon image gif animée comme "Contenu" et le répertoire Copier vers la sortie à "Copier si plus récent" ou "toujours". Ensuite, dans MainWindow (), j'ai appelé cette méthode. Le seul problème est que lorsque j'ai essayé de supprimer le flux, cela m'a donné un graphique d'enveloppe rouge au lieu de mon image. Je vais devoir résoudre ce problème. Cela a supprimé la douleur de charger une BitmapImage et de la transformer en Bitmap (ce qui a évidemment tué mon animation car ce n'est plus un 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
place de .Image
. Il avait la mauvaise méthode. .ImageLocation
fonctionne à partir de la racine du projet Visual Studio, alors disons que vous avez un Images
dossier, votre chemin est alors imgBox.ImageLocation = "/Images/my.gif";
. Si vous avez un dossier appelé Views
où vous avez une vue qui affiche l'image, pour revenir à Images
, vous auriez à utiliser 2 points: imgBox.ImageLocation = "../Images/my.gif";
.
J'ai essayé tout le chemin ci-dessus, mais chacun a sa brièveté, et grâce à vous tous, je travaille sur mon propre 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);
}
}
}
Usage:
<localComponents:GifImage x:Name="gifImage" IsAutoStart="True" GifSource="{Binding Path=value}" />
Comme cela ne causerait pas de fuite de mémoire et animerait la propre ligne temporelle de l'image gif, vous pouvez l'essayer.
IsAutoStart
, mais sinon, a fonctionné comme un champion!
Auparavant, j'ai rencontré un problème similaire, j'avais besoin de lire un .gif
fichier dans votre projet. J'avais deux choix:
en utilisant PictureBox de WinForms
en utilisant une bibliothèque tierce, telle que WPFAnimatedGif de codeplex.com.
La version avec PictureBox
ne fonctionnait pas pour moi et le projet ne pouvait pas utiliser de bibliothèques externes pour cela. Je l'ai donc fait pour moi Bitmap
avec de l'aide ImageAnimator
. Parce que, standard BitmapImage
ne prend pas en charge la lecture des .gif
fichiers.
Exemple complet:
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
ne prend pas en charge la directive URI , je charge donc le .gif
fichier à partir du répertoire actuel.
Petite amélioration de la GifImage.Initialize()
méthode, qui lit le bon timing de trame à partir des métadonnées 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;
}
Je ne sais pas si cela a été résolu mais la meilleure façon est d'utiliser la bibliothèque WpfAnimatedGid . Il est très facile, simple et direct à utiliser. Il ne nécessite que 2 lignes de code XAML et environ 5 lignes de code C # dans le code derrière.
Vous verrez tous les détails nécessaires sur la façon dont cela peut être utilisé là-bas. C'est ce que j'ai aussi utilisé au lieu de réinventer la roue
En plus de la réponse principale qui recommande l'utilisation de WpfAnimatedGif , vous devez ajouter les lignes suivantes à la fin si vous échangez une image avec un Gif pour vous assurer que l'animation s'exécute réellement:
ImageBehavior.SetRepeatBehavior(img, new RepeatBehavior(0));
ImageBehavior.SetRepeatBehavior(img, RepeatBehavior.Forever);
Votre code ressemblera donc à:
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);
Vérifiez mon code, j'espère que cela vous a aidé :)
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
{
}
}
});
}
ou
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{}
}
});
}
Une alternative à l'animation en attente dans WPF est:
<ProgressBar Height="20" Width="100" IsIndeterminate="True"/>
Il affichera une barre de progression animée.