2017-05-27 45 views
0

私はWPF UIを使用してインターネットからダウンロードしたサムネイルを表示し、必要に応じて複数のファイルに書き込むアプリケーションで作業しています。私は1つのサムネイルサイズしか提供されていないので、サムネイルURLを一度ダウンロードし、それを表示して、オプションでそれを書き込んでください。URLを複数回ダウンロードする必要はありません。WPF Imageコントロールでカスタムキャッシュを使用する

イメージが何度も要求され、書き込まれる可能性があり、同じファイルをダウンロードすることが非常に非効率的であるため、後者のシナリオではキャッシングクラスを作成しました。しかし、UIで何度も同じ画像をリクエストすることができますが、コントロールがキャッシュURIをキャッシュしている間に、自分自身のキャッシュ実装を使用して、再度ダウンロードすることを避けることができます。メモリ内の全く同じ画像の

しかし、私が考えることができる唯一の方法は、Imageに拡張プロパティを作成してキャッシュキーを取得し、すべての重要なロジックを実行することです。私が理解しているところでは、毎回ゼロから新しいBitmapImageを作成しなければならないので、これは組み込みのキャッシュとダウンロードロジックを妨害するでしょう。

したがって、組み込みキャッシュインスタンスにフックしてそれを聴いたり、サブクラス化してホイールを再構成する必要はありませんか?非UIロジックがWPFビルトインキャッシュをメインキャッシュとして使用する逆のシナリオでも、受け入れられます。

+0

BitmapImageまたはBitmapFrameのインスタンスをキャッシュする簡単なバインディングコンバーター(string/UriからImageSourceへ)はどうですか? – Clemens

答えて

0

1つの方法は、キャッシュinterceptorを使用することです。依存関係を管理するためにUnityまたはWindsorのような依存性注入コンテナを使用すると、そこからいくつかの追加値を得ることができます。

あなたはこのインタフェースと実装を持っている画像をダウンロードするための仮定:

public interface IImageDownloader 
{ 
    BitmapImage GetImage(Uri uri); 
} 

public class ImageDownloader : IImageDownloader 
{ 
    public BitmapImage GetImage(Uri uri) 
    { 
     // Your implementation 
     return null; 
    } 
} 

あなたはIImageDownloaderを必要とするときImageDownloaderのインスタンスを作成する(例として、ユニティを使用して)あなたのコンテナを伝えることができます。

container.RegisterType<IImageDownloader, ImageDownloader>(); 

これまでのところ、これはキャッシングとは関係ありません。それは、他のクラスが具体的なクラスの代わりにインターフェース(IImageDownloader)に依存するように、依存性注入を使用しているだけです。

しかし、あなたは傍受の追加の利点を得る。コンテナは、クラスの周りの他の振る舞いを「ラップ」することができます。ここでthis documentationに基づく例です:

public class ImageDownloadCacheInterceptionBehavior : IInterceptionBehavior 
{ 
    public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) 
    { 
     Uri uriArgument = (input.Arguments.Count > 0) ? input.Arguments[0] as Uri : null; 
     if (uriArgument == null) 
     { 
      return getNext()(input, getNext); 
     } 
     var key = CreateKey(uriArgument); 
     using (var cache = MemoryCache.Default) 
     { 
      var cached = cache[key] as BitmapImage; 
      if (cached == null) 
      { 
       cached = getNext()(input, getNext).ReturnValue as BitmapImage; 
       if(cached!=null) 
       cache.Add(key, cached, DateTimeOffset.Now.AddHours(1)); 
      } 
      return input.CreateMethodReturn(cached); 
     } 
    } 

    private string CreateKey(Uri uri) 
    { 
     return "ImageCache" + uri; 
    } 

    public IEnumerable<Type> GetRequiredInterfaces() 
    { 
     return Type.EmptyTypes; 
    } 

    public bool WillExecute => true; 
} 

インターセプターを使用するようにUnityに伝えるために:

container.AddNewExtension<Interception>(); 
container.RegisterType<IImageDownloader, ImageDownloader>(
    new Interceptor<InterfaceInterceptor>(), 
    new InterceptionBehavior<ImageDownloadCacheInterceptionBehavior>() 
); 

これは単なる典型的なキャッシュ動作です。 Uriからキャッシュキーを作成していて、そのアイテムがキャッシュにあればそれが返されます。そうでない場合は、呼び出されているメソッドを呼び出し続け、完了したら戻り値をキャッシュに戻してから返します。

この作業の主な利点は、キャッシングの動作がImageDownloaderクラスにないことです。そのクラスには1つの仕事があります - イメージをダウンロードする - それは簡単で簡単にテストできます。その方法でキャッシングを実行しなければならない場合は、より長く複雑になります。インターセプトでは、に属していないクラスに振る舞いを追加して、クラスに振り分けることができます。

たとえば、いくつかのケースではキャッシュしたいが他のケースではキャッシュしない場合はどうすればよいでしょうか?あなたは、bool shouldCacheまたはTimeSpan cacheDurationのようなパラメータを使ってメソッドに奇妙なオーバーロードを書き込むことになります。

このようにしても、キャッシュの動作は複雑ですが、別々にしておくことができます。

もう1つのアプリケーションは例外処理です。すべてのクラスに独自のtry/catch/logメソッドがある場合は、苦痛を伴うことがあります。しかし、その例外処理をそのままにして、あなたのクラスに自分のことをさせることができます。インターセプタは例外を捕捉してログに記録できます。あなたがそれを使用し始めると、あなたはそれが動作するように取得しているとき


このすべては、単に余分な作業の多くのように思えます。しかし、一度あなたがコードを複雑にするのを助けるために奇跡を働かせることができます。

最初の課題は、特定のタイプのアプリケーションで依存性注入コンテナを使用する方法を学習することです。 Here's an articleをWPFアプリケーションで設定してください。

組み込み依存関係注入または依存関係注入との互換性を宣言しているフレームワーク(ASP.NET Core、Angular)の数を確認すると、パターンの値が明らかになります。

+0

これは本当に私の質問のポイントではありません。私はDIが何であるか、いつ使用するのかを知っていますが、この例ではWPFイメージとデータの両方にグローバルキャッシュを使用できるかどうか疑問に思っていました。 – svbnet

関連する問題