0

Currently I'm re-writing an WPF APP with .NET7 and ReactiveUI. I love the ReactiveUI framework and how easy async stuff can be achieved. However I'm struggling to binding an image. I keep getting a TargetInvocationException.

My idea is to have an async ReactiveCommand that uses the LoadImageFromUrl Akavache extension and expose the result through a OAPH.

The ViewModel looks like the following:

public sealed class PartViewModel : ReactiveObject, IActivatableViewModel
{
    private string? ImageUrl => Part.ImageUrl;

    public ViewModelActivator Activator { get; } = new();

    public PartDto Part { get; set; }

    private readonly ObservableAsPropertyHelper<IBitmap?> _image;
    public IBitmap? Image => _image.Value;

    public PartSummaryViewModel(PartDto partDto)
    {
        Part = partDto;

        var loadImage = ReactiveCommand
            .CreateFromTask(LoadImageAsync);

        _image = loadImage
            .ToProperty(this, vm => vm.Image, scheduler: RxApp.MainThreadScheduler);

        this.WhenActivated(disposables =>
        {
            loadImage
                .Execute()
                .Subscribe()
                .DisposeWith(disposables);
        });
    }

    private async Task<IBitmap?> LoadImageAsync(CancellationToken ct)
    {
        if (string.IsNullOrEmpty(ImageUrl))
        {
            return null;
        }

        var bitmap = await BlobCache.LocalMachine.LoadImageFromUrl(ImageUrl);
        return bitmap;
    }
}

The View looks like the following:

public partial class PartView : ReactiveUserControl<PartViewModel> 
{
    public PartView()
    {
        InitializeComponent();

        this.WhenActivated(disposables =>
        {
            this.OneWayBind(ViewModel,
                vm => vm.Image,
                v => v.PartImage.Source,
                (IBitmap? bitmap) => bitmap?.ToNative())
                .DisposeWith(disposables);
        });
    }
}

The exception message I receive says:

*v.PartImage.Source Binding received an Exception! <s:ReactiveUI.PropertyBinderImplementation>
     System.Reflection.TargetInvocationException: 
       Exception has been thrown by the target of an invocation. 
---> System.InvalidOperationException: 
       The calling thread cannot access this object because a different thread owns it.*

I also tried to set the outputScheduler on the ReactiveCommand like so

var loadImage = ReactiveCommand
  .CreateFromTask(LoadImageAsync, outputScheduler: RxApp.MainThreadScheduler);

But this results in the same exception. I also tried to specify the scheduler in the WhenActivated-part but get the same exception

loadImage
   .Execute()
   .ObserveOn(RxApp.MainThreadScheduler)
   .Subscribe()
   .DisposeWith(disposables);

Any idea what I'm missing?

ouflak
  • 2,458
  • 10
  • 44
  • 49
  • No idea how exactly bitmaps are loaded by these libraries, but you do typically get that exception when a BitmapSource that is created in a background thread is not frozen before being used in the UI thread. – Clemens Dec 02 '22 at 19:58

0 Answers0