어떻게 제어 유형 사용한다 - 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의 의견에 따라 Silverlight 프로젝트에 애니메이션 GIF 지원을 추가하려면 github.com/XamlAnimatedGif/XamlAnimatedGif 를 사용 하십시오
img
여기 가 무엇입니까 ?
이미지 컨트롤을 확장하고 Gif Decoder를 사용하는 솔루션을 게시했습니다. gif 디코더에는 프레임 속성이 있습니다. FrameIndex
속성을 애니메이션합니다 . 이 이벤트 ChangingFrameIndex
는 source 속성을 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
스레드에서 컨트롤을 언급했지만 다소 무거운 컨트롤이라고 언급되어 있으므로 컨트롤을 기반으로하는 적어도 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로 설정하고 출력 디렉토리로 복사하십시오.
다음은 애니메이션 이미지 컨트롤의 내 버전입니다. 이미지 소스를 지정하기 위해 표준 특성 소스를 사용할 수 있습니다. 나는 그것을 더 향상시켰다. 나는 러시아어, 프로젝트는 러시아어이므로 의견도 러시아어입니다. 그러나 어쨌든 당신은 의견없이 모든 것을 이해할 수 있어야합니다. :)
/// <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
먼저, 패키지 관리자 콘솔을 사용하여 라이브러리를 프로젝트에 설치하십시오.
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 솔루션이지만 이번에는 코드 숨김을 사용하여 프로젝트에서 Embedded Resource를 사용합니다.
XAML에서 :
<WindowsFormsHost x:Name="_loadingHost">
<Forms:PictureBox x:Name="_loadingPictureBox"/>
</WindowsFormsHost>
코드 숨김에서 :
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 App Project에서 생성하고 스타일에서 템플릿 재정의를 삭제해야합니다.
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 지원 부재를 해결하는 데 도움이되었습니다. Winforms API로 인해 pictureBoxLoading.Image 속성을 설정하는 데 시간이 많이 걸리기 때문에 약간의 코드를 추가하는 것입니다.
애니메이션 GIF 이미지의 Build Action을 "Content"로 설정하고 Copy to output 디렉토리를 "New if if newer"또는 "always"로 설정해야했습니다. 그런 다음 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
하여 프로젝트에서 파일 을 재생 해야했습니다. 두 가지 선택이있었습니다.
WinForms에서 PictureBox 사용
WPFAnimatedGif와 같은 타사 라이브러리 사용 codeplex.com의
with 버전이 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 라이브러리 를 사용하는 것입니다 입니다. 사용이 매우 쉽고 간단하며 간단합니다. 코드 뒤에 2 줄의 XAML 코드와 약 5 줄의 C # 코드 만 있으면됩니다.
이것이 어떻게 사용될 수 있는지에 대한 모든 필요한 세부 사항을 볼 수 있습니다. 이것은 바퀴를 다시 발명하는 대신 내가 사용한 것입니다.
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"/>
애니메이션 진행률 표시 줄이 표시됩니다.