Reputation: 2407
When i run the project a runtime error ocure: Error: Property 'UriSource' or property 'StreamSource' must be set. because this.ImageUri is null , i don't know why this.ImageUri be null ! help me
I have been working with the WPF ListBox using images as my list box items. The sourced image path points to a server hosting those images. While on fast network, the images appeared without any noticeable delay. However it became apparent over a slow link that the user experience degraded and I really wanted to show a placeholder image while the image was downloaded and decoded.
Surprisingly, I didn't find a solution in the blogosphere for this issue so I coded up a derived class to address this.
The sample XAML below is from my item container style. I replaced Image with my local class implementation local:ImageLoader.
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyData}">
...
<StackPanel Grid.Column="0" Margin="5">
<Border BorderThickness="0">
<MyControl:ImageLoader Width="50" Height="50" ImageUri="{Binding Path=profile_image_url_https, FallbackValue=profile_image_url_https}" InitialImage="/MyProject;component/Images/nopic.png" HorizontalAlignment="Left"></imgz:ImageLoader>
</Border>
</StackPanel>
...
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Source = {StaticResource MyData}}" />
</Grid>
The heart of the handling for the initial image is in the OnLoaded() method, where I use a BitmapImage as the source and set the UriSource to the derived class' ImageUri dependency property, which allows for data binding. The initial image is updated to the actual image when the download completes or when a failure event is received. The class also optionally allows you to specify a "LoadFailedImage".
public class ImageLoader : Image
{
public static readonly DependencyProperty ImageUriProperty = DependencyProperty.Register(
"ImageUri", typeof(Uri), typeof(ImageLoader), new PropertyMetadata(null, null));
private BitmapImage loadedImage;
public ImageLoader()
{
this.Loaded += this.OnLoaded;
}
public string LoadFailedImage
{
get;
set;
}
public Uri ImageUri
{
get {return this.GetValue(ImageUriProperty) as Uri;}
set {this.SetValue(ImageUriProperty, value);}
}
public string InitialImage
{
get;
set;
}
private new ImageSource Source
{
get {return base.Source;}
set {base.Source = value;}
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
// Loading the specified image
this.loadedImage = new BitmapImage();
this.loadedImage.BeginInit();
this.loadedImage.CacheOption = BitmapCacheOption.OnDemand;
this.loadedImage.DownloadCompleted += this.OnDownloadCompleted;
this.loadedImage.DownloadFailed += this.OnDownloadFailed;
this.loadedImage.UriSource = this.ImageUri;
this.loadedImage.EndInit();
// The image may be cached, in which case we will not use the initial image
if (!this.loadedImage.IsDownloading)
{
this.Source = this.loadedImage;
}
else
{
// Create InitialImage source if path is specified
if (!string.IsNullOrWhiteSpace(this.InitialImage))
{
BitmapImage initialImage = new BitmapImage();
// Load the initial bitmap from the local resource
initialImage.BeginInit();
initialImage.UriSource = new Uri(this.InitialImage, UriKind.Relative);
initialImage.DecodePixelWidth = (int)this.Width;
initialImage.EndInit();
// Set the initial image as the image source
this.Source = initialImage;
}
}
e.Handled = true;
}
private void OnDownloadFailed(object sender, ExceptionEventArgs e)
{
if (!string.IsNullOrWhiteSpace(this.LoadFailedImage))
{
BitmapImage failedImage = new BitmapImage();
// Load the initial bitmap from the local resource
failedImage.BeginInit();
failedImage.UriSource = new Uri(this.LoadFailedImage, UriKind.Relative);
failedImage.DecodePixelWidth = (int)this.Width;
failedImage.EndInit();
this.Source = failedImage;
}
}
private void OnDownloadCompleted(object sender, EventArgs e)
{
this.Source = this.loadedImage;
}
}
When i run the project a runtime error ocured: Error: Property 'UriSource' or property 'StreamSource' must be set. because this.ImageUri is null , i don't know why this.ImageUri be null ! help me
Upvotes: 2
Views: 4004
Reputation: 17083
If it isn't the semicolon typo in InitialImage="/MyProject;component/Images/nopic.png",
maybe it's better to set your InitialImage as Default in ImageUri
public static readonly DependencyProperty ImageUriProperty = DependencyProperty.Register(
"ImageUri", typeof(Uri), typeof(ImageLoader), new PropertyMetadata(new Uri("/MyProject/component/Images/nopic.png"), null));
UPDATE:
You have to bind to Image.Source
and you could use PriorityBinding
to show a placeholder.
<Image.Source>
<PriorityBinding>
<!--highest priority sources are first in the list-->
<Binding Path="YourImageUri"
IsAsync="True" />
<Binding Path="InitialImageUri"
IsAsync="True" />
</PriorityBinding>
</Image.Source>
For a "LoadFailedImage" a would subsribe to Image.ImageFailed
Event.
Hope this helps.
Upvotes: 2