Pergunta

I have a problem, with my WPF application. I'm trying to databind an image field, in my gridview, with the image property from my viewmodel.

<DataGridTemplateColumn Header="Image" IsReadOnly="True">
    <DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
        <Image Source="{Binding Path=Image, IsAsync=True}" />
    </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

This is no problem, if I don't use IsAsync. However, I would like to do it async, as it's a lot of images to load, and it needs to load them from a webservice.

The code for the Image property is this, which just calls a handler dll, that calls a webservice.

    public BitmapSource Image
    {
        get { return image ?? (image = ImageHandler.GetDefaultImages(new[] {ItemNumber},160,160)[0].BitmapSource()); }
    }

But, as soon as I add the IsAsync=true, I get the following exception, once the form has loaded:

The calling thread cannot access this object because a different thread owns it.

I am quite new to WPF, and I kind of assumed, that it handled the threading itself, when async was set to true. Do I need to somehow invoke, in the databinding? If so, how on earth do I do this?

Foi útil?

Solução

IsAsync means that the bound property will be accessed from another thread, so make sure that whatever you're creating can then be used in a cross-thread manner. The final step of the binding will always occur on the main thread.

Almost all classes in WPF inherit from DispatcherObject, which basically 'links' an object to the current thread when it's created. The BitmapSource in your code is created on another thread and then cannot be used on the main UI thread.

However, BitmapSource inherits from Freezable: simply call the Freeze method on your BitmapSource before returning, to make it immutable and usable from any thread.

Outras dicas

How about if you access the image on separate thread and don't use IsAsync. Something like

public BitmapSource Image
    {
        get 
        { 
            return image ?? (Task.Factory.StartNew(() => image = ImageHandler.GetDefaultImages(new[] { ItemNumber }, 160, 160)[0].BitmapSource());) 
        }
    }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top