WPF 值转换器中的异步加载图像

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24731335/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 12:08:53  来源:igfitidea点击:

Async loading images in WPF value converter

c#wpf

提问by scott

I have a WPF listbox, which will bind a lot of images. Each image may will from local disk or get icon from Exe itself.

我有一个 WPF 列表框,它将绑定很多图像。每个图像可能来自本地磁盘或从 Exe 本身获取图标。

I put all those parse codes in MultiValueConverter. But it now seems block the UI. How to make that async?

我将所有这些解析代码放在 MultiValueConverter 中。但现在似乎阻止了用户界面。如何使异步?

Code Sample: https://github.com/qianlifeng/Wox/blob/master/Wox/Converters/ImagePathConverter.cs#L53

代码示例:https: //github.com/qianlifeng/Wox/blob/master/Wox/Converters/ImagePathConverter.cs#L53

回答by pushpraj

You can leverage IsAsyncproperty of Binding

您可以利用IsAsync的财产Binding

From MSDN:

MSDN

Use the IsAsync property when the get accessor of your binding source property might take a long time. One example is an image property with a get accessor that downloads from the Web. Setting IsAsync to true avoids blocking the UI while the download occurs.

当绑定源属性的 get 访问器可能需要很长时间时,请使用 IsAsync 属性。一个示例是带有从 Web 下载的 get 访问器的图像属性。将 IsAsync 设置为 true 可避免在下载发生时阻塞 UI。

example

例子

<Image Source="{Binding MyImage,IsAsync=True, Converter={StaticResource MyConverter}}" />

more on Binding.IsAsync

更多关于Binding.IsAsync



Async Converter

异步转换器

I managed to create a async converter

我设法创建了一个异步转换器

namespace CSharpWPF
{
    class AsyncConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return new AsyncTask(() =>
             {
                 Thread.Sleep(4000); //long running job eg. download image.
                 return "success";
             });
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        public class AsyncTask : INotifyPropertyChanged
        {
            public AsyncTask(Func<object> valueFunc)
            {
                AsyncValue = "loading async value"; //temp value for demo
                LoadValue(valueFunc);  
            }

            private async Task LoadValue(Func<object> valueFunc)
            {
                AsyncValue =  await Task<object>.Run(()=> 
                    {
                        return valueFunc();
                    });
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("AsyncValue"));
            }

            public event PropertyChangedEventHandler PropertyChanged;

            public object AsyncValue { get; set; }
        }
    }
}

this converter will return an instance of AsyncTaskwhich will encapsulate the long running job within

此转换器将返回一个实例,AsyncTask该实例将在其中封装长时间运行的作业

class AsyncTaskwill execute the task asynchronously and will set the result to AsyncValueas it also implements INotifyPropertyChangedhence using the notification to update the UI

AsyncTask将异步执行任务并将结果设置 AsyncValue为它也实现INotifyPropertyChanged因此使用通知来更新 UI

usage

用法

<Grid xmlns:l="clr-namespace:CSharpWPF">
    <Grid.Resources>
        <l:AsyncConverter x:Key="AsyncConverter" />
    </Grid.Resources>
    <TextBlock DataContext="{Binding MyProperty,Converter={StaticResource AsyncConverter}}"
               Text="{Binding AsyncValue}" />
</Grid>

Idea is to bind the DataContextof the element to the converter and the desired property to the AsyncValue of the new data context

想法是将DataContext元素的绑定到转换器,将所需的属性绑定到新数据上下文的 AsyncValue

above example is using Text property of a text block for easy demo

上面的例子是使用文本块的 Text 属性来简单演示

exmaple is made for a IValueConvertersame approach can be used for IMultiValueConvertertoo.

exmaple 是为IValueConverter同样的方法制作的,也可以用于IMultiValueConverter

回答by Harsh Baid

First you should provide some sample code so it easy to reproduce for answerers.

首先,您应该提供一些示例代码,以便回答者轻松重现。

IsAsyncwill not help

IsAsync不会有帮助

Using IsAsyncbinding will not help because of below reasons:

IsAsync由于以下原因,使用绑定将无济于事:

  1. I have observed that when loading images dynamically with IsAsync it will lead to memory leaks at times. So avoid this sugar candy IsAsyncproperty
  2. IsAsyncwill not always help as one problem case mentioned in the question herewhere OP is trying to load image from web but wpf is resolving DNS on main thread so again application hangs for while
  1. 我观察到,当使用 IsAsync 动态加载图像时,它有时会导致内存泄漏。所以避免这种糖果IsAsync属性
  2. IsAsync作为这里问题中提到的一个问题案例,并不总是有帮助,其中 OP 试图从 Web 加载图像,但 wpf 正在主线程上解析 DNS,因此应用程序再次挂起一段时间

Solution is Use Attached Binding Property in WPF

解决方案是在 WPF 中使用附加绑定属性

Details:

细节:

  1. You should put some thumbnail image on Sourcein XAML
  2. Write one Attached Property class to load the image in background and Update Image Source in when its available (sample code below for similar kind of use case)
  1. 你应该Source在 XAML 中放一些缩略图
  2. 编写一个附加属性类以在后台加载图像并在可用时更新图像源(以下示例代码用于类似的用例)
<Image my:ImageAsyncHelper.SourceUri="{Binding Author.IconUrl}" />

Attached Property class

附加属性类

public class ImageAsyncHelper : DependencyObject
{
  public static Uri GetSourceUri(DependencyObject obj) { return (Uri)obj.GetValue(SourceUriProperty); }
  public static void SetSourceUri(DependencyObject obj, Uri value) { obj.SetValue(SourceUriProperty, value); }
  public static readonly DependencyProperty SourceUriProperty = DependencyProperty.RegisterAttached("SourceUri", typeof(Uri), typeof(ImageAsyncHelper), new PropertyMetadata
  {
    PropertyChangedCallback = (obj, e) =>
    {
      ((Image)obj).SetBinding(Image.SourceProperty,
        new Binding("VerifiedUri")
        {
          Source = new ImageAsyncHelper { GivenUri = (Uri)e.NewValue },
          IsAsync = true,
        });
    }
  });

  Uri GivenUri;
  public Uri VerifiedUri
  {
    get
    {
      try
      {
        Dns.GetHostEntry(GivenUri.DnsSafeHost);
        return GivenUri;
      }
      catch(Exception)
      {
        return null;
      }

    } 
  } 
}

And if you need to use IMultiValueConverterwith attached property defined above then it should go like below xaml code:

如果你需要使用IMultiValueConverter上面定义的附加属性,那么它应该像下面的 xaml 代码:

Attached Property with IMultiValueConverter

附加属性 IMultiValueConverter

<Image>
    <my:ImageAsyncHelper.SourceUri>
        <MultiBinding Converter="{StaticResource MyImageMultiValueConverter}">
            <Binding Source="Author" Path="IconUrl"/> <!-- Binding Parameters -->
            <Binding Path="ImageType"/> <!-- Binding Parameters -->
            <Binding Path="MyParameterToConverter"/> <!-- Binding Parameters -->
        </MultiBinding>
    </my:ImageAsyncHelper.SourceUri>
</Image>

Reference links

参考链接

  1. How can I keep a WPF Image from blocking if the ImageSource references an unreachable Url?
  2. Using multibinding to set custom attached property in WPF
  1. 如果 ImageSource 引用了无法访问的 Url,如何防止 WPF 图像被阻塞?
  2. 使用多重绑定在 WPF 中设置自定义附加属性