2017-05-27 97 views
0

我正在研究使用WPF UI顯示從互聯網下載的縮略圖的應用程序,然後可以將它們寫入多個文件。由於我只提供一個縮略圖大小,因此我只有下載縮略圖URL然後顯示它並選擇性地寫入縮略圖纔有意義,因爲我想避免不止一次下載URL。在WPF圖像控件中使用自定義緩存

我爲後一種情況創建了一個緩存類,因爲可能會多次請求和寫入圖像,並且下載相同的文件效率很低。但是,同樣的圖像也可以在UI上多次請求,雖然Image控件確實緩存了URI,但我更喜歡如果我可以使用自己的緩存實現,以便可能避免再次下載它,並且還避免有兩個副本在記憶中完全相同的圖像。

但是,我能想到的唯一方法就是在Image上創建一個擴展屬性,它需要一個緩存鍵並執行所有重要的邏輯。據我所知,這會干擾內置的緩存和下載邏輯,因爲我必須每次都從頭創建一個新的BitmapImage

因此,有沒有辦法掛鉤到內置緩存實例中,只是聽它甚至子類化,所以我不必重新發明輪子?即使是非UI邏輯使用WPF內置緩存作爲其主緩存的逆向場景也是可以接受的。

+0

如何緩存BitmapImage或BitmapFrame實例的簡單綁定轉換器(從字符串/ Uri到ImageSource)? – Clemens

回答

0

一種方法是使用緩存interceptor。如果您使用依賴注入容器(如UnityWindsor)來管理依賴關係,那麼您可以從中獲得一些附加值。

假設圖像下載你有這個接口和實現:

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

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

你可以告訴你的容器(使用Unity作爲一個例子)創建的ImageDownloader實例時,你需要一個IImageDownloader

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; 
} 

現在就來告訴團結了使用的攔截:

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

這只是典型的緩存行爲。它從Uri創建緩存密鑰,然後如果該項目在緩存中,則返回該緩存密鑰。如果不是,它會繼續調用正在調用的方法,並且在完成後它將返回值放入緩存中,然後再返回。

從所有這些工作中獲得的主要好處是緩存行爲不在ImageDownloader類中。該課程有一項工作要做 - 下載一張圖片 - 因此它測試簡單且容易。如果你不得不把緩存放在那個方法中,那麼它會更長更復雜。攔截允許您將行爲添加到不屬於的類中,該類在中。

例如,如果您想在某些情況下緩存但不在其他情況下如何緩存?您可能最終會在您的方法中使用bool shouldCacheTimeSpan cacheDuration等參數編寫奇怪的重載。

這樣你仍然有你的緩存行爲的複雜性,但你可以保持它的獨立性。

另一個應用程序是異常處理。如果每個班級都有自己的try/catch/log方法,這可能會很痛苦,特別是如果有一天您的記錄方法發生變化。但相反,您可以將這個異常處理掉,讓您的課程自行完成。攔截器可以捕獲異常並將其記錄下來。


所有這一切只是好像很多額外的工作,當你開始使用它,你得到它的工作。但是,一旦你克服了這個麻煩,它可以創造奇蹟,幫助管理你的代碼的複雜性。

最初的挑戰只是學習如何在給定類型的應用程序中使用依賴注入容器。 Here's an article在WPF應用程序中進行設置。

當您看到有多少框架(ASP.NET Core,Angular)宣告內置依賴注入或與依賴注入的兼容性時,模式的值就會變得明顯。

+0

這不是我的問題的重點。我知道DI是什麼以及何時使用它,但在這種情況下,我想知道是否可以爲我的WPF圖像和數據使用全局緩存。 – svbnet