显示默认图像并在实际图像完成加载后更改它

时间:2011-12-18 13:00:11

标签: c# .net wpf bitmapimage markup-extensions

我有一个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),还是有不同的方法来实现这一目标?

2 个答案:

答案 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;
}

这样就首先显示默认图像,一旦检索到请求的图像,它就会显示出来。

很抱歉,如果此处的代码不完全正确,我现在不在代码附近。希望现在足以表达主要想法。