Atif Shabeer
Atif Shabeer

Reputation: 167

Image Caching in Windows Phone 8.1

I am writing a Windows Phone 8.1 (WINRT) application. It displays an image which it gets from internet.

I want to save this image to local storage and then display it. What I mean to say is, every time app opens, it checks if the image is in local storage, show it and parallely load the new copy of this image from internet, and refresh ui to new image.

Means, till the new copy of the image is being loaded from internet, cache copy of this image taken from local storage will act as placeholder.

But issue is I am getting blank UI till image loads from internet instead of local image acting as placeholder.

C#

public sealed partial class ProfileView : Page
{
    const string PROFILE_PIC_FILE_NAME = "UmangProfilePic.jpg";
    public ProfileView()
    {
        this.InitializeComponent();

        this.NavigationCacheMode = NavigationCacheMode.Required;
    }


    protected override async void OnNavigatedTo(NavigationEventArgs e)
    {
        //Testing:
        //string url = "http://www.nokia.com/sites/default/files/styles/medium/public/media/nokia_white_logo.png"; //small image
        string url = "http://anderson.chem.ox.ac.uk/images/group2015.jpg"; //large image

        await UmangImageCacheAsync(url);
    }

    /// <summary>
    /// Check if image exists in local storage, load that image to UI control
    /// Then, get latest image from internet and storage that image in local storage
    /// Then, load that new image from local storage again to UI control
    /// </summary>
    /// <param name="url"></param>
    /// <returns></returns>
    private async Task UmangImageCacheAsync(string url)
    {
        loadingProfilePic.IsActive = true;

        //var dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
        //var ignored = dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
        //{


        //});

        var dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
        var ignored = dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
        {
            if (await FileExists(PROFILE_PIC_FILE_NAME))
            {
                profilePicImage.Source = await GetLocalImageAsync();
            }
        });

        var result = await GetRemoteImageAsync(url);

        if (result is BitmapImage)
        {
            profilePicImage.Source = result as BitmapImage;
            loadingProfilePic.IsActive = false;
        }
        else
        {
            loadingProfilePic.IsActive = false;
        }




    }

    private async Task<BitmapImage> GetLocalImageAsync()
    //  private async Task<bool> GetLocalImageAsync()
    {
        StorageFolder localFolder = ApplicationData.Current.LocalFolder;
        StorageFile file = await localFolder.GetFileAsync(PROFILE_PIC_FILE_NAME);
        var image = await FileIO.ReadBufferAsync(file);
        Uri uri = new Uri(file.Path);
        return new BitmapImage(new Uri(file.Path));

        //return true;
    }

    private static async Task<object> GetRemoteImageAsync(string url)
    //private static async Task GetRemoteImageAsync(string url)
    {
        StorageFolder localFolder = ApplicationData.Current.LocalFolder;
        StorageFile file = await localFolder.CreateFileAsync(PROFILE_PIC_FILE_NAME, CreationCollisionOption.ReplaceExisting);

        HttpClient client = new HttpClient();

        try
        {
            byte[] responseBytes = await client.GetByteArrayAsync(url);

            var stream = await file.OpenAsync(FileAccessMode.ReadWrite);

            using (var outputStream = stream.GetOutputStreamAt(0))
            {
                DataWriter writer = new DataWriter(outputStream);
                writer.WriteBytes(responseBytes);
                await writer.StoreAsync();
                await outputStream.FlushAsync();
            }
            var image = await FileIO.ReadBufferAsync(file);
            Uri uri = new Uri(file.Path);
            return new BitmapImage(new Uri(file.Path));

        }
        catch (Exception _ex)
        {
            return false;
        }
    }


    public static async Task<bool> FileExists(string fileName)
    {
        StorageFolder localFolder = ApplicationData.Current.LocalFolder;
        try
        {
            StorageFile file = await localFolder.GetFileAsync(fileName);
        }
        catch (Exception _fileEx)
        {
            return false;
        }
        return true;
    }
}

XAML:

<Grid>
    <Grid>
        <Image
        x:Name="profilePicImage"
        Width="400"
        Height="400" />

        <ProgressRing x:Name="loadingProfilePic"
        IsActive="False"
        IsEnabled="True" >

        </ProgressRing>
    </Grid>
</Grid>

To reproduce this issue: execute app using first "url"(small image), then debugg again using second url commenting the first url till the new image downloads from the internet, first image should act as placeholder but what you will observe is progress ring only

Upvotes: 2

Views: 76

Answers (1)

Stefan Wick  MSFT
Stefan Wick MSFT

Reputation: 13850

Your call to GetLocalImageAsync() gets executed after the call to GetRemoteImageAsync(), because you are posting it to the dispatcher queue. I think you want this the other way around to accomplish your scenario.

Thanks, Stefan Wick - Windows Developer Platform

Upvotes: 1

Related Questions