Click here to Skip to main content
15,885,546 members
Articles / Desktop Programming / WPF

Local Image Caching with Custom Image Control in WPF

Rate me:
Please Sign up or sign in to vote.
4.50/5 (2 votes)
14 Nov 2012CPOL1 min read 16.1K   4  
Local image caching with custom control in WPF

Recently, I needed my WPF application to cache images locally when there’s connectivity issues or data is not updated. I searched the web to find 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 a custom control which:

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

The class for the custom control is given below:

C#
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 a local file even if the request fails, so I introduced a temporary file which is deleted if an error occurs. 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:

XML
<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)


Written By
Technical Lead Bloomberg LP
United States United States
10+ years of software design and development.

Using C/C++, C#, JavaScript, Java, Python, AngularJS, React, React Native, .NET, WinAPI, WPF, PureMVC, MVVM, MVC, Unix/Linux, AWS, Azure, Heroku, Git, grafana, InfluxDB, TeamCity, Jenkins, Gulp, MS SQL, PostgreSQL, MySQL, Snowflake SQL, MongoDB

http://ileonenko.wordpress.com

Comments and Discussions

 
-- There are no messages in this forum --