У меня было два способа сделать это. Оба способа используют класс, который оборачивает изображение и реализует INotifyPropertyChanged
:
class ImageSourceWrapper : ObservableObject
{
private ImageSource _image;
public ImageSource Image
{
get { return _image; }
set
{
if (value != _image)
{
_image = value;
RaiseOnPropertyChanged("Image");
}
}
}
public ImageSourceWrapper(ImageSource image)
{
Image = image;
}
}
Первый подход
Получив это, я могу заставить мое расширение разметки возвращать объект ImageSourceWrapper
и связываться с ним, например,
<Image Source="{Binding Source={my:ImageProvider ImageName=myImageName}, Path=Image}" />
Мне не очень понравился этот способ, потому что он довольно грязный и предполагает необходимость знать класс ImageSourceWrapper
, а не просто работать с ImageSource
. Затем я придумал второй подход.
Второй подход
В этом подходе я все еще использую класс ImageSourceWrapper
, но вместо того, чтобы мое расширение разметки возвращало объект ImageSourceWrapper
, я возвращаю объект привязки, который я настроил для привязки к объекту ImageSourceWrapper
.
Расширение разметки выглядит примерно так:
private ImageSourceWrapper _imageSourceWrapper;
public override object ProvideValue(IServiceProvider serviceProvider)
{
// Get the object and the property to be bound.
IProvideValueTarget service = IProvideValueTarget)provider.GetService(typeof(IProvideValueTarget));
DependencyObject targetObject = service.TargetObject as DependencyObject;
DependencyProperty targetProperty = service.TargetProperty as DependencyProperty;
// Set up the binding with the default image.
_imageSourceWrapper = new ImageSourceWrapper(DefaultImage);
Binding binding = new Binding("Image");
binding.Source = _imageSourceWrapper;
BindingOperations.SetBinding(targetObject, targetProperty, binding);
// Retrieve the actual image asynchronously.
GetImageAsync();
return binding.ProvideValue(serviceProvider);
}
private void GetImageAsync()
{
// Get the image asynchronously.
// Freeze the image so it could be accessed from all threads regardless
// of which thread it was created on.
newImage.Freeze();
// Got the image - update the _imageSourceWrapper object.
_imageSourceWrapper = newImage;
}
Тогда я могу использовать его в XAML следующим образом
<Image Source="{my:ImageProvider ImageName=myImageName}" />
Таким образом, изображение по умолчанию отображается первым, и как только запрошенное изображение будет получено, оно будет отображаться вместо него.
Извините, если код здесь не совсем правильный, сейчас я не рядом с кодом. Надеюсь, на данный момент этого достаточно, чтобы выразить основную идею.