Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ReactiveUI WPF - The calling thread cannot access this object because a different thread owns it

Thanks to @GlennWatson for pointing out that I needed to add a reference to the Nuget Package ReactiveUI.WPF, in addition to the ReactiveUI package.

I have a ReactiveObject view model, within which I would like to use an OpenFileDialog to set the value of one of my view model properties (PdfFilePath). Everything I have tried results in a The calling thread cannot access this object because a different thread owns it error.

I realise that the code below is not MVVM compliant because I am using code that 'explicitly references the type of/instantiates the view' in the view model, but I'm just looking for a minimal example that works so I can work backwards, splitting the view and view model code apart, and ultimately passing in a service to my view model that handles the whole 'selecting a file path' part.

public class ImportPdfViewModel : ReactiveObject
{
    public ImportPdfViewModel()
    {
        SelectFilePathCommand = ReactiveCommand.Create(() =>
        {
            OpenFileDialog ofd = new OpenFileDialog() { };
            //
            if (ofd.ShowDialog() == DialogResult.OK)
                PdfFilePath = ofd.FileName;
        });
    }

    private string _PdfFilePath;
    public string PdfFilePath
    {
        get => _PdfFilePath;
        set => this.RaiseAndSetIfChanged(ref _PdfFilePath, value);
    }

    public ReactiveCommand SelectFilePathCommand { get; set; }
}

As i mentioned, I have tried lots of different options, including injecting a service into my view model, but no matter where I instantiate the OpenFileDialog (eg in the main view), I always end up with the same error.

I've also googled the hell out of "ReactiveUI" and "OpenFileDialog", but none of the code I find seems to be up to date (eg using ReactiveCommand<Unit, Unit>), nor consistent with any other example! Thanks.


UPDATE

Thanks to @GlennWatson for pointing out that I needed to add a reference to the Nuget Package ReactiveUI.WPF, in addition to the ReactiveUI package.

As soon as I added it, the code worked!

The code now looks like this, which I believe is MVVM compliant, uses dependency injection, and uses the latest features/best practices of ReactiveUI (though I'm obviously open to criticism!):

ImportPdf

public class ImportPdfViewModel : ReactiveObject
{
    public ImportPdfViewModel(IIOService openFileDialogService)
    {
        SelectFilePathCommand = ReactiveCommand
            .Create(() => openFileDialogService.OpenFileDialog(@"C:\Default\Path\To\File"));
        SelectFilePathCommand.Subscribe((pdfFilePath) => { PdfFilePath = pdfFilePath; });
    }

    private string _PdfFilePath;
    public string PdfFilePath
    {
        get => _PdfFilePath;
        set => this.RaiseAndSetIfChanged(ref _PdfFilePath, value);
    }

    public ReactiveCommand<Unit, String> SelectFilePathCommand { get; set; }
}

IIOService

public interface IIOService
{
    string OpenFileDialog(string defaultPath);
}

OpenFileDialogService

public class OpenFileDialogService : IIOService
{
    public string OpenFileDialog(string defaultPath)
    {
        OpenFileDialog ofd = new OpenFileDialog() { FileName = defaultPath };
        //
        if (ofd.ShowDialog() == DialogResult.OK)
        {
            return ofd.FileName;
        }
        else
        {
            return null;
        }
    }
}

UPDATE

I've also had the error below caused by the same missing package ... This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread

like image 603
3-14159265358979323846264 Avatar asked Nov 06 '18 11:11

3-14159265358979323846264


1 Answers

If running on a WPF or WinForms platform you need to make sure you include a nuget reference to ReactiveUI.WPF or ReactiveUI.Winforms.

like image 132
Glenn Watson Avatar answered Sep 22 '22 16:09

Glenn Watson