Windows Phone 8.1 Loading Images from Remote URLs Placeholder
asynchronous, c#, image, windows-phone, windows-phone-8.1
Solution
1) With your current code you do not block the UI thread as yes you are setting the `URI` on the UI thread, but the actually loading of the image is done on another thread automatically. (For doing manually downloading of images, strings etc, you will probably use async/await to avoid locking the UI thread).
2) The image goes black because you change the `ImageSource` before the new image has loaded. There are as you mention several ways to deal with this. Common for most of them though is that you will want to use the `ImageOpened` and `ImageFailed` events on the Image control, which triggers whenever the image is done loading (or an error occurred, for example no internet connection). Here is an example of displaying a loading bar while it is loading, which just hides/shows the loading progress:
In the `page.xaml` file:
<Grid Grid.Row="1" x:Name="ContentRoot">
<Image x:Name="KittyPic" Tapped="KittyPic_Tapped" ImageOpened="KittyPic_ImageOpened" ImageFailed="KittyPic_ImageFailed" />
<StackPanel x:Name="LoadingPanel" VerticalAlignment="Center">
<ProgressBar IsIndeterminate="True" IsEnabled="True" />
<TextBlock Text="Loading image..." HorizontalAlignment="Center" />
</StackPanel>
</Grid>
And in the page.xaml.cs file
private void KittyPic_Tapped(object sender, TappedRoutedEventArgs e)
{
LoadingPanel.Visibility = Visibility.Visible;
Uri myUri = new Uri("http://thecatapi.com/api/images/get?format=src&type=jpg", UriKind.Absolute);
BitmapImage bmi = new BitmapImage();
bmi.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
bmi.UriSource = myUri;
KittyPic.Source = bmi;
}
private void KittyPic_ImageOpened(object sender, RoutedEventArgs e)
{
LoadingPanel.Visibility = Visibility.Collapsed;
}
private async void KittyPic_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
LoadingPanel.Visibility = Visibility.Collapsed;
await new MessageDialog("Failed to load the image").ShowAsync();
}
Instead of the `TextBlock` and `ProgressBar` you could of course show whatever you want, or for example swapping between two images to keep showing the old one.
For other advice I think when you got used to the basics is to take a look at data bindings which is very helpful and powerful. Also take a look at this article about MVVM pattern.
Problem
I'm messing around with some windows phone development as I'm new to it coming from an Android background. I am using "theCatAPI" to load a random picture of a cat and show it, then when the picture is clicked, or the button at the bottom of the screen, the image refreshes to a new one. I have the following so far: XAML: ``` <Page x:Class="CatFactsPics.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:CatFactsPics" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid x:Name="LayoutRoot"> <Grid.ChildrenTransitions> <TransitionCollection> <EntranceThemeTransition/> </TransitionCollection> </Grid.ChildrenTransitions> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!-- TitlePanel --> <StackPanel Grid.Row="0" Margin="24,17,0,28"> <TextBlock Text="My Application" Style="{ThemeResource TitleTextBlockStyle}" Typography.Capitals="SmallCaps"/> <TextBlock Text="page title" Margin="0,12,0,0" Style="{ThemeResource HeaderTextBlockStyle}"/> </StackPanel> <!--TODO: Content should be placed within the following grid--> <Grid Grid.Row="1" x:Name="ContentRoot"> <Image HorizontalAlignment="Center" Stretch="UniformToFill" VerticalAlignment="Center" x:Name="KittyPic" Tapped="KittyPic_Tapped"/> <Button HorizontalAlignment="Center" VerticalAlignment="Bottom" x:Name="newPic" Click="newPic_Click" >New Kitty</Button> </Grid> </Grid> </Page> ``` and in the page.cs: ``` ... protected override void OnNavigatedTo(NavigationEventArgs e) { this.navigationHelper.OnNavigatedTo(e); Uri myUri = new Uri("http://thecatapi.com/api/images/get?format=src&type=jpg", UriKind.Absolute); KittyPic.Source = new BitmapImage(myUri); } ... private void newPic_Click(object sender, RoutedEventArgs e) { Uri myUri = new Uri("http://thecatapi.com/api/images/get?format=src&type=jpg", UriKind.Absolute); BitmapImage bmi = new BitmapImage(); bmi.CreateOptions = BitmapCreateOptions.IgnoreImageCache; bmi.UriSource = myUri; KittyPic.Source = bmi; } ``` I have a couple of questions: 1) is this the correct way of doing things? In Android I'd try and do things asynchronously to avoid stalling the UI thread. That being said, I don't appear to be having any issues with things the way they are now. I'm not familiar with the Windows way of doing things, and haven't found any resources giving any explanation or advice on doing so. 2) There is a delay in displaying the new picture causing a short (couple of second) period where the image view turns black, before the new image reappears. Is there a way of setting it up so either the old picture remains until the new one is physically ready to be displayed, or alternatively display a placeholder "loading" image until the new one can replace it. Any other advice or tips on how to do things would be great, thanks.