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

发布于 2024-12-22 06:42:12 字数 1064 浏览 4 评论 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)或者是否有不同的方法来实现此目的?

I have a WPF markup extension in charge of retrieving images by name, returning a BitmapImage object.

<Image Source="{my:ImageProvider ImageName=myImageName}"></Image>

Since retrieving an image is an operation that can possibly take a few seconds, I'd like to show a default image and display the requested image once it's ready.

What I tried to do is something like this, but as this may change the BitmapImage object, it won't update the UI (sample code):

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);
}

Is there a way I can update the UI to use the modified BitmapImage (something like INotifyPropertyChanged) or is there a different approach to achieve this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

伴梦长久 2024-12-29 06:42:12

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

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

PriorityBinding is what you might be looking for i guess.
You can bind to two different DP's with your actual image source as the highest binding and not forgot to set IsAsync property to true for that binding.
Once, your image source is ready it will automatically replaced the second binding.

Refer to this link to get started - http://msdn.microsoft.com/en-us/library/system.windows.data.prioritybinding.aspx

許願樹丅啲祈禱 2024-12-29 06:42:12

我最终有两种方法可以做到这一点。两种方法都使用包装图像并实现 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}" />

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

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

I ended up with two ways to do this. Both ways use a class that wraps the image and implements 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;
    }
}

First Approach

Once I have this, I can have my markup extension return an ImageSourceWrapper object and bind to it, like so

<Image Source="{Binding Source={my:ImageProvider ImageName=myImageName}, Path=Image}" />

I didn't really like this way, since it's pretty messy and involves having to know the ImageSourceWrapper class rather than working simply with ImageSource. Then I came up with the second approach.

Second Approach

In this approach I still use the ImageSourceWrapper class, but instead of having my markup extension return an ImageSourceWrapper object, I return a binding object which I set up to be bound to an ImageSourceWrapper object.

The markup extension looks something like this:

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

Then I can use it in XAML like this

<Image Source="{my:ImageProvider ImageName=myImageName}" />

This way the default image is displayed first, and once the requested image is retrieved, it will be displayed instead.

Sorry if the code here isn't entirely correct, I'm not near the code at the moment. Hopefully this is enough at the moment to express the main idea.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文