我有一个WPF标记扩展程序,负责按名称检索图像,返回BitmapImage
个对象。
<Image Source="{my:ImageProvider ImageName=myImageName}"></Image>
由于检索图像的操作可能需要几秒钟,因此我想显示默认图像并在准备好后显示所请求的图像。
我尝试做的是这样的,但是因为这可能会改变BitmapImage
对象,所以它不会更新UI(示例代码):
BitmapImage img;
public override object ProvideValue(IServiceProvider serviceProvider)
{
img = new BitmapImage(new Uri(@"D:\defaultImage.png", UriKind.Absolute));
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync();
return img;
}
void bw_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(5000);
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
img.UriSource = new Uri(@"D:\actualImage.png", UriKind.Absolute);
}
有没有办法可以更新UI以使用修改后的BitmapImage
(类似于INotifyPropertyChanged),还是有不同的方法来实现这一目标?
答案 0 :(得分:1)
PriorityBinding
我猜你可能正在寻找。
您可以将实际图像源绑定到两个不同的DP作为最高绑定,并且不要忘记将该绑定的IsAsync
属性设置为true。
一旦您的图像源准备就绪,它将自动替换第二个绑定。
请参阅此链接以开始使用 - http://msdn.microsoft.com/en-us/library/system.windows.data.prioritybinding.aspx
答案 1 :(得分:0)
我最终有两种方法可以做到这一点。两种方式都使用包装图像的类并实现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
对象,而是返回一个绑定对象,我将其设置为绑定到{{1对象。
标记扩展名如下所示:
ImageSourceWrapper
然后我可以像这样在XAML中使用它
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;
}
这样就首先显示默认图像,一旦检索到请求的图像,它就会显示出来。
很抱歉,如果此处的代码不完全正确,我现在不在代码附近。希望现在足以表达主要想法。