アニメーション GIF と、Silverlight で直接サポートされている画像を表示する必要がある WindowsPhone 7.8 アプリケーションがあります。public sealed partial class ExtendedImageControl : UserControl, IDisposable
そのため、ImageSource という名前の DependancyProperty を持つカスタム ユーザー コントロールを作成しました。このプロパティは URL にバインドされています。コード ビハインドは、通常の Image コントロールを LayoutRoot に挿入するか、AnimatedImage を挿入します。ただし、AnimatedImage が表示されなくなったり、含まれているページが閉じられたりしても、メモリは解放されません。
読み込みロジックは次のとおりです。
ExtendedImage loadedImage = (ExtendedImage)await Utils.Caching.Image.LoadCachedImageFromUrlAsync<ExtendedImage>(loadedLocation);
if (ImageSource == loadedLocation)
{
AnimatedImage image = new AnimatedImage();
image.Stretch = stretch;
image.Source = loadedImage;
LayoutRoot.Children.Add(image);
CurrentImageMode = ExtendedImageMode.AnimatedImage;
loadedImage = null;
#if DEBUG
App.logger.log("Loaded {0} as animated image", loadedLocation);
#endif
imageDisplay = image;
raiseImageUpdated();
}
最終的に、画像は次のようにロードされます
WebClient client = new WebClient();
ExtendedImage image = new ExtendedImage();
using (Stream source = await client.OpenReadTaskAsync(location))
{
if (location.ToString().EndsWith("gif", StringComparison.InvariantCultureIgnoreCase))
{
image.SetSource(source);
TaskCompletionSource<ExtendedImage> imageLoaded = new TaskCompletionSource<ExtendedImage>();
EventHandler loadingCompleteHandler = new EventHandler((sender, e) =>
{
imageLoaded.SetResult(image);
});
EventHandler<UnhandledExceptionEventArgs> loadingFailedHandler = new EventHandler<UnhandledExceptionEventArgs>((sender, e) =>
{
imageLoaded.SetResult(image);
#if DEBUG
if (System.Diagnostics.Debugger.IsAttached)
System.Diagnostics.Debugger.Break();
#endif
});
image.LoadingCompleted += loadingCompleteHandler;
image.LoadingFailed += loadingFailedHandler;
image = await imageLoaded.Task;
//Remove handlers, otherwise the object might be kept in the memory
image.LoadingCompleted -= loadingCompleteHandler;
image.LoadingFailed -= loadingFailedHandler;
}
else
{
//... load with native Silverlight methods
}
}
return image;
非常に早い段階でメモリの問題に気付いたので、コントロールは IDisposable インターフェイスを実装します。
public void Dispose()
{
unloadImage();
GC.SuppressFinalize(this);
}
private void unloadImage()
{
SmartDispatcher.BeginInvoke(() =>
{
if (imageDisplay != null)
{
if (imageDisplay is AnimatedImage)
{
if ((imageDisplay as AnimatedImage).Source != null & (imageDisplay as AnimatedImage).Source.Frames != null)
(imageDisplay as AnimatedImage).Source.Frames.Clear();
(imageDisplay as AnimatedImage).Stop();
(imageDisplay as AnimatedImage).Source = null;
}
else if (imageDisplay is Image && ((Image)imageDisplay).Source != GIFplaceholder)
{
(imageDisplay as Image).Source = null;
}
imageDisplay = null;
}
});
}
ただし、画像に対して Dispose メソッドが呼び出されることはありません。
このオブジェクトが GC によって検出されない理由を調べるにはどうすればよいですか? 私が理解している限り、イベントハンドラーを登録していません。アプリケーションページから離れたときに収集する必要があります。デストラクタも追加しようとしましたが、これも呼び出されません。