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

时间:2014-07-14 07:14:31

标签: c# wpf

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

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

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

2 个答案:

答案 0 :(得分:7)

您可以利用IsAsync

Binding属性

来自MSDN

  

在绑定源的get访问器时使用IsAsync属性   财产可能需要很长时间。一个例子是带有的图像属性   从Web下载的get访问器。将IsAsync设置为true   避免在下载过程中阻止UI。

例如

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

更多关于Binding.IsAsync


异步转换器

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

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

此转换器将返回AsyncTask的实例,该实例将封装长期运行的作业

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

使用

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

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

以上示例使用文本块的Text属性进行简单演示

例如,IValueConverter同样的方法也适用于IMultiValueConverter

答案 1 :(得分:-1)

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

IsAsync无效

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

  1. 我观察到,使用IsAsync动态加载图像时,有时会导致内存泄漏。所以要避免这种糖果IsAsync财产
  2. IsAsync并不总是有帮助作为question here中提到的一个问题案例,其中OP正在尝试从网络加载图片,但是wpf正在解析主线程上的DNS,因此应用程序再次挂起
  3. 解决方案是在WPF中使用附加绑定属性

    详细信息:

    1. 您应该在XAML中的Source放置一些缩略图
    2. 编写一个附加属性类以在后台加载图像并在可用时更新图像源(下面的示例代码用于类似用例)
    3. <Image my:ImageAsyncHelper.SourceUri="{Binding Author.IconUrl}" />
      

      附加属性类

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

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

      附加财产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>
      

      参考链接

      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