0

我有一个 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),或者是否有不同的方法来实现这一点?

4

2 回答 2

1

PriorityBinding我猜是你可能正在寻找的东西。您可以将实际图像源作为最高绑定绑定到两个不同的 DP,并且不要忘记IsAsync将该绑定的属性设置为 true。一旦您的图像源准备就绪,它将自动替换第二个绑定。

请参阅此链接以开始使用 - http://msdn.microsoft.com/en-us/library/system.windows.data.prioritybinding.aspx

于 2011-12-18T16:46:06.730 回答
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对象,而是返回一个绑定对象,我将其设置为绑定到一个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}" />

这种方式首先显示默认图像,一旦检索到请求的图像,就会显示它。

抱歉,如果这里的代码不完全正确,我目前不在代码附近。希望这足以表达主要思想。

于 2012-01-07T13:11:44.480 回答