0

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.

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

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

1
  • 2
    You should post the relevant parts of your code here. Commented Jul 14, 2014 at 7:24

2 Answers 2

7

You can leverage IsAsync property of Binding

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

example

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

more on 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 AsyncTask which will encapsulate the long running job within

class AsyncTask will execute the task asynchronously and will set the result to AsyncValue as it also implements INotifyPropertyChanged hence using the notification to update the 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 DataContext of the element to the converter and the desired property to the AsyncValue of the new data context

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

exmaple is made for a IValueConverter same approach can be used for IMultiValueConverter too.

Sign up to request clarification or add additional context in comments.

10 Comments

This will only call the MyImage getter asynchronously, not the converter (which was what OP asked for). And please mark citations from other resources (e.g. From MSDN: Use the IsAsync property ...).
I got the point. As a workaround a calculate property can be used to execute the long running function instead of converter. I am trying to find a way to use converters async.
A possible workaround would be another view model property, that you could bind asynchronously without a converter.
@pushpraj check this question where user is loading image from web but then also it hangs the application because some operation is still executing on main thread so IsAsync is not good idea.
@Clemens I'm going to do that if other ways won't work.
|
-2

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

IsAsync will not help

Using IsAsync binding will not help because of below reasons:

  1. I have observed that when loading images dynamically with IsAsync it will lead to memory leaks at times. So avoid this sugar candy IsAsync property
  2. IsAsync will not always help as one problem case mentioned in the question here where OP is trying to load image from web but wpf is resolving DNS on main thread so again application hangs for while

Solution is Use Attached Binding Property in WPF

Details:

  1. You should put some thumbnail image on Source in 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)
<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 IMultiValueConverter with attached property defined above then it should go like below xaml code:

Attached Property with 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 Comment

This solution seems to depend entirely on IsAsync (in the new Binding() code), despite your warnings not to use it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.