2

I am trying to load a remote image in XAML which requires an authentication header to successfully download.

Currently I'm using my own caching service where I download the image using HttpClient and store the file to disk. On subsequent loads, I just bind the image to the absolute file path of the cached file like so:

              <Image Width="50"
                   Height="50"
                   Stretch="UniformToFill"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center"
                   Source="{Binding CachedImagePath}"/>

This works however I'm not too pleased with the time it takes to load the image from the file. It seems much slower than if I use XAML caching.

So my questions are:

1) Is there a way to pass an authentication header when I bind my Image to a remote URI?

2) If not, can you recommend a quicker way to load the image from disk than how I currently have it?

Justin Horst
  • 201
  • 1
  • 9

2 Answers2

1

The Windows Community Toolkit includes an ImageEx control that can be used to manage both local caching of remote images and asynhcronous loading of the images in the UI.

As a side effect of how they have implemented the downloading of the image we can access the static HttpClient instance that it uses and set the DefaultRequestHeaders with Authentication or other Headers that your API may require.

This solution works if all your images are hosted from the same authenticated server, the toolkit is open source so you can download the source code and change the implementation for your own needs.

The ImageEx control manages the Visible state while the image is being loaded with a nice fade in animation by default, however you can override this or even provide a PlaceHolder image or text.

  • An advanced scenario is to use the PlaceHolder to first render a low-res version of the image that may be aggressively cached. That's out-of-scope for this post, but highlights the extensibility of this solution.

The magic happens inside the ImageCache that this control uses, we can force the ImageCache to be used by setting both EnableLazyLoading and the IsCacheEnabled properties to True, as long as we set the request headers before the images are loaded then we can bind the Source directly to the external URL to access the files.

xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
<controls.ImageEx Width="50"
                  Height="50"
                  Stretch="UniformToFill"
                  HorizontalAlignment="Center"
                  VerticalAlignment="Center"

                  EnableLazyLoading="True"
                  IsCacheEnabled="True"
                  Source="{Binding ImageUrl}" />

This behaviour is not explicitly documented, by using reflection to access the internal HttpClient we accept there is a risk that this may not work in future versions.

Ideally, becuase ImageCache.Instance is a static member, we only need to set the Auth headers when the user logs in, the following code shows how you can do this with an arbitrary header:

// set the auth header on the Image Cache!
var httpClient = ImageCache.Instance
                           .GetType()
                           .GetProperty("HttpClient", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetProperty | System.Reflection.BindingFlags.NonPublic)
                           .GetValue(ImageCache.Instance)
                           as System.Net.Http.HttpClient;
        Runtime.ConfigureHttpClient(httpClient);

httpClient.DefaultRequestHeaders.Authorization = 
    new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "---MyUserAuthToken---");
httpClient.DefaultRequestHeaders.Add("My-Token", "---arbitrary token---");

The runtime performance is great, but you can configure the ImageCache to keep a number of the images in memory, or we can explicitly or pre-emptively load images in advance:

In the following, assume that item is a data object that has an ImageUrl that holds a URL to a resource that requires authentication.

// Define max memory cache size
ImageCache.Instance.MaxMemoryCacheCount = 200;

// Precache data and save it in memory
await ImageCache.Instance.PreCacheAsync(new Uri(item.ImageUrl), Path.GetFileName(item.Thumbnail), true);

// Precache data and save it in on local hard drive
await ImageCache.Instance.PreCacheAsync(new Uri(item.ImageUrl), Path.GetFileName(item.Thumbnail), false);

// Clear cache
await ImageCache.Instance.ClearAsync();
Chris Schaller
  • 13,704
  • 3
  • 43
  • 81
0

This is possible by implementing IWeRequestCreate. You may need to change your app.config file by adding the class details for the implemented interface.

Hence whenever the webrequest is getting created it will go through this custom implementation and there you can pass the authentication headers as HttpWeRequest contains a property Header.

For more details check out this post.

Community
  • 1
  • 1