Click here to Skip to main content
Click here to Skip to main content

Local image caching with custom control in WPF

, 14 Nov 2012
Rate this:
Please Sign up or sign in to vote.
Local image caching with custom control in WPF.
This is an old version of the currently published technical blog.

Recently I needed my WPF application to cache images locally, when there’s connectivity issues or data is not updated. I searched web if there’s a standard way to do this, but it seems like you can do whatever you like and then I found a post on stackoverflow, where the same topic was discussed. Based on this I made custom control which:

  • can download images asynchronously and get them from cache if image was downloaded
  • is thread safe
  • has dependency property to which you can bind to
  • update images, providing new names in initial feed (don’t forget to maintain cache clean operation, e.g., you can parse your feed and asynchronously delete images with no links in feed)

Class of custom control is below:

public class CachedImage : Image
{
    static CachedImage()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CachedImage), 
                 new FrameworkPropertyMetadata(typeof(CachedImage)));
    }

    public readonly static DependencyProperty ImageUrlProperty = DependencyProperty.Register(
      "ImageUrl", typeof(string), typeof(CachedImage), 
      new PropertyMetadata("", ImageUrlPropertyChanged));

    public string ImageUrl
    {
        get
        {
            return (string)GetValue(ImageUrlProperty);
        }
        set
        {
            SetValue(ImageUrlProperty, value);
        }
    }

    private static readonly object SafeCopy = new object();

    private static void ImageUrlPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        var url = (String)e.NewValue;
        if (String.IsNullOrEmpty(url))
            return;

        var uri = new Uri(url);
        var localFile = String.Format(Path.Combine(Globals.CacheFolder, uri.Segments[uri.Segments.Length - 1]));
        var tempFile = String.Format(Path.Combine(Globals.CacheFolder, Guid.NewGuid().ToString()));

        if (File.Exists(localFile))
        {
            SetSource((CachedImage)obj, localFile);
        }
        else
        {
            var webClient = new WebClient();
            webClient.DownloadFileCompleted += (sender, args) =>
                        {
                            if (args.Error != null)
                            {
                                File.Delete(tempFile);
                                return;
                            }
                            if (File.Exists(localFile))
                                return;
                            lock (SafeCopy)
                            {
                                File.Move(tempFile, localFile);
                            }
                            SetSource((CachedImage)obj, localFile);
                        };

            webClient.DownloadFileAsync(uri, tempFile);
        }
    }

    private static void SetSource(Image inst, String path)
    {
        inst.Source = new BitmapImage(new Uri(path));
    }
}

DownloadFileAsync creates local file even if request failed, so I introduced temporary file, which is deleted if error occurred.

Globals.CacheFolder is just a class that holds static properties with paths and creates directories if they don’t exist, so you should replace it with your existing cache folder path.

Now you can use this CachedImage control in XAML, getting benefits from binding:

<Cache:CachedImage ImageUrl="{Binding Icon}"/>

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Ivan Leonenko
Software Developer (Senior) Enkata
Russian Federation Russian Federation
More than 7 years of software design and development.
 
Specialties: C#, .NET 2 3.5 4, WPF (MVVM), Silverlight, ASP.NET, WCF, Javascript, Flash, ActionScript, SQL, WinAPI, Powershell
 
My blog http://ileonenko.wordpress.com
Follow on   Google+

Comments and Discussions


Discussions posted for the Published version of this article. Posting a message here will take you to the publicly available article in order to continue your conversation in public.
 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web01 | 2.8.140827.1 | Last Updated 14 Nov 2012
Article Copyright 2012 by Ivan Leonenko
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid