ReactiveUI WPF - 因为不同的线程拥有该对象,所以调用线程无法访问该对象。

7
感谢@GlennWatson指出我需要添加对Nuget软件包ReactiveUI.WPF的引用,除了ReactiveUI软件包。
我有一个ReactiveObject视图模型,在其中我想使用一个OpenFileDialog来设置我的视图模型属性之一(PdfFilePath)的值。我尝试的所有方法都导致了一个“调用线程无法访问此对象,因为一个不同的线程拥有它”的错误。
我意识到下面的代码不符合MVVM规范,因为我在视图模型中使用了'显式引用/实例化视图的类型'的代码,但我只是在寻找一个最小的可行示例,以便我可以往回工作,将视图和视图模型代码分开,并最终传递一个服务到我的视图模型,处理整个“选择文件路径”的部分。
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; }
}

正如我之前提到的,我尝试了很多不同的选项,包括将服务注入到我的视图模型中,但无论我在哪里实例化OpenFileDialog(例如在主视图中),我总是遇到相同的错误。
我还通过Google搜索“ReactiveUI”和“OpenFileDialog”,但我找到的代码似乎都不是最新的(例如使用ReactiveCommand<Unit,Unit>),也与任何其他示例不一致!谢谢。
更新
感谢@GlennWatson指出我需要添加对Nuget包ReactiveUI.WPF的引用,除了ReactiveUI包之外。
我添加它后,代码就可以工作了!
现在的代码看起来像这样,我相信它符合MVVM规范,使用依赖注入,并使用ReactiveUI的最新功能/最佳实践(虽然我显然接受批评!): 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;
        }
    }
}

更新

由于缺少相应的包,我还遇到了下面的错误:此CollectionView类型不支持来自与Dispatcher线程不同的线程对其SourceCollection进行更改


通常解决这种问题的方法是使用“Application.Current.Dispatcher.Invoke()”将导致问题的行进行包装。 - Robin Bennett
1
@RobinBennett 感谢您的评论...然而,在我的Google搜索中,我发现了ReactiveUI的作者Paul Betts在这篇文章的末尾发表的评论,他说这不是正确的方法!https://stackoverflow.com/a/16596310/5040941。我只是无法弄清楚如何将他实际的答案(在该帖子上面)推广到适合我的情况。此外,他的答案来自2013年,我认为ReactiveUI已经发生了足够的变化,现在有更好的方法可用。 - 3-14159265358979323846264
好的 - 我应该提到我对ReactiveUI一无所知! - Robin Bennett
1
你在哪个平台上运行?如果你没有包含正确的NuGet包,你经常会遇到线程问题。例如reactiveui.wpf。 - Glenn Watson
避免使用抽象基类。请使用ReactiveCommand<Unit, Unit>。 - Glenn Watson
显示剩余3条评论
1个回答

8

如果在WPF或WinForms平台上运行,则需要确保引用了ReactiveUI.WPF或ReactiveUI.Winforms的NuGet。


1
再次感谢您,格伦。我在问题的顶部进行了强调,因为它似乎可以解决比我的OpenFileDialog问题更多的问题!:0) - 3-14159265358979323846264

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接