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

1

7

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

Amnesty answered 6/11, 2018 at 11:3 Comment(8)
Often the solution that that sort of problem is to wrap the line that causes the problem with 'Application.Current.Dispatcher.Invoke()'Preponderate
@RobinBennett Thanks for your comment ... however, during my Googleathon, i came across this comment (at the end of the post) by Paul Betts, the author of ReactiveUI, who says that is not the right way to do it! https://mcmap.net/q/1621889/-reactiveui-the-calling-thread-cannot-access-this-object-because-a-different-thread-owns-it. I just can't work out how to extrapolate his actual answer (above that post) to fit my scenario. Also, his answer is from 2013 and I'm under the impression that ReactiveUI has changed sufficiently enough that there are better ways available now.Amnesty
Fair enough - I should have mentioned that I know nothing about ReactiveUI !Preponderate
:0) you're not the only one \0/!!Amnesty
What platform you running on? You often get issues with threading if you don't include the right NuGet packages. Eg reactiveui.wpfMane
Avoid the abstract base class. Use ReactiveCommand<Unit, Unit>Mane
@GlennWatson. Just added a reference to the nuget package reactiveui.wpf and it worked straight away! If you change your comment to an answer I'll accept it straight away!Amnesty
OMG I just banged my head around with this. Thank god for this question and answer.Karajan
M
8

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

Mane answered 13/11, 2018 at 21:37 Comment(1)
Thanks again Glenn. I've highlighted this at the top of my question because it seems like it could fix a whole lot more than my OpenFileDialog issue! :0)Amnesty

© 2022 - 2024 — McMap. All rights reserved.