关于使用Dispatcher时BackgroundWorker上的wpf:c#MVVM InvalidOperationException

c# MVVM InvalidOperationException on BackgroundWorker when using Dispatcher

我目前在C#WPF中遇到问题。我编写了一个应用程序,该应用程序在后台任务中生成长时间运行的报告。我将棱镜与MVVM结合使用,并尝试通过Async ICommand实现和BackgroundWorker运行昂贵的后台任务。但是当我尝试检索生成的报告

1
Report = asyncTask.Result;

我收到一个InvalidOperationException声明,"调用线程无法访问此对象,因为另一个线程拥有它。"

是的,我已经尝试调用调度程序(当您搜索异常消息时,它会在google,stackoverflow等上找到的第一件事)。我已经尝试了多种变体,例如:

1
Dispatcher.CurrentDispatcher.Invoke(() => Report = asyncTaks.Result);

1
Report.Dispatcher.Invoke(() => Report = asyncTask.Result);

但是每次我收到此异常时。

我怀疑我调用报告UI的方式不充分。

结构简要如下:

1
2
3
4
5
6
7
MainWindowViewModel
  -> SubWindowCommand
      SubWindowViewModel
        -> GenerateReportCommand
            ReportViewModel
              -> GenerateReportAsyncCommand
              <- Exception on callback

我没主意了,有人知道我可能做错了什么吗?

下面是一些代码片段

报表生成器视图模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
public class ReportFlowDocumentViewModel : BindableBase
{
    private IUnityContainer _container;
    private bool _isReportGenerationInProgress;
    private FlowDocument _report;

    public FlowDocument Report
    {
        get { return _report; }
        set
        {
            if (object.Equals(_report, value) == false)
            {
                SetProperty(ref _report, value);
            }
        }
    }

    public bool IsReportGenerationInProgress
    {
        get { return _isReportGenerationInProgress; }
        set
        {
            if (_isReportGenerationInProgress != value)
            {
                SetProperty(ref _isReportGenerationInProgress, value);
            }
        }
    }


    public ReportFlowDocumentView View { get; set; }

    public DelegateCommand PrintCommand { get; set; }

    public AsyncCommand GenerateReportCommand { get; set; }

    public ReportFlowDocumentViewModel(ReportFlowDocumentView view, IUnityContainer c)
    {
        _container = c;

        view.DataContext = this;
        View = view;
        view.ViewModel = this;

        InitializeGenerateReportAsyncCommand();

        IsReportGenerationInProgress = false;
    }

    private void InitializeGenerateReportAsyncCommand()
    {
        GenerateReportCommand = new CreateReportAsyncCommand(_container);
        GenerateReportCommand.RunWorkerStarting += (sender, args) =>
        {
            IsReportGenerationInProgress = true;

            var reportGeneratorService = new ReportGeneratorService();
            _container.RegisterInstance<ReportGeneratorService>(reportGeneratorService);
        };

        GenerateReportCommand.RunWorkerCompleted += (sender, args) =>
        {
            IsReportGenerationInProgress = false;
            var report = GenerateReportCommand.Result as FlowDocument;
            var dispatcher = Application.Current.MainWindow.Dispatcher;

            try
            {
                dispatcher.VerifyAccess();

                if (Report == null)
                {
                    Report = new FlowDocument();
                }

                Dispatcher.CurrentDispatcher.Invoke(() =>
                {
                    Report = report;
                });

            }
            catch (InvalidOperationException inex)
            {
                // here goes my exception
            }
        };
    }

    public void TriggerReportGeneration()
    {
        GenerateReportCommand.Execute(null);
    }

}

这是我启动ReportView窗口的方式

1
2
3
4
5
6
7
8
9
10
11
var reportViewModel = _container.Resolve<ReportFlowDocumentViewModel>();

View.ReportViewerWindowAction.WindowContent = reportViewModel.View;

reportViewModel.TriggerReportGeneration();

var popupNotification = new Notification()
{
    Title ="Report Viewer",
};
ShowReportViewerRequest.Raise(popupNotification);

1
ShowReportViewerRequest = new InteractionRequest<INotification>();

AsyncCommand定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public abstract class AsyncCommand : ICommand
{
    public event EventHandler CanExecuteChanged;
    public event EventHandler RunWorkerStarting;
    public event RunWorkerCompletedEventHandler RunWorkerCompleted;

    public abstract object Result { get; protected set; }
    private bool _isExecuting;
    public bool IsExecuting
    {
        get { return _isExecuting; }
        private set
        {
            _isExecuting = value;
            if (CanExecuteChanged != null)
                CanExecuteChanged(this, EventArgs.Empty);
        }
    }

    protected abstract void OnExecute(object parameter);

    public void Execute(object parameter)
    {
        try
        {
            onRunWorkerStarting();

            var worker = new BackgroundWorker();
            worker.DoWork += ((sender, e) => OnExecute(e.Argument));
            worker.RunWorkerCompleted += ((sender, e) => onRunWorkerCompleted(e));
            worker.RunWorkerAsync(parameter);
        }
        catch (Exception ex)
        {
            onRunWorkerCompleted(new RunWorkerCompletedEventArgs(null, ex, true));
        }
    }

    private void onRunWorkerStarting()
    {
        IsExecuting = true;
        if (RunWorkerStarting != null)
            RunWorkerStarting(this, EventArgs.Empty);
    }

    private void onRunWorkerCompleted(RunWorkerCompletedEventArgs e)
    {
        IsExecuting = false;
        if (RunWorkerCompleted != null)
            RunWorkerCompleted(this, e);
    }

    public virtual bool CanExecute(object parameter)
    {
        return !IsExecuting;
    }
}

CreateReportAsyncCommand:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class CreateReportAsyncCommand : AsyncCommand
{
    private IUnityContainer _container;

    public CreateReportAsyncCommand(IUnityContainer container)
    {
        _container = container;
    }

    public override object Result { get; protected set; }
    protected override void OnExecute(object parameter)
    {
        var reportGeneratorService = _container.Resolve<ReportGeneratorService>();
        Result = reportGeneratorService?.GenerateReport();
    }

}


我想我现在明白我的问题了。我不能在BackgroundThread中使用FlowDocument,然后再对其进行更新,对吗?

那么我如何在后台线程中创建FlowDocument,或者至少异步生成文档?

我正在创建的FlowDocument包含很多表,并且当我同步运行报告生成时,UI冻结大约30秒,这对于常规使用是不可接受的。

编辑:
在这里找到解决方案:
在BackgroundWorker线程上创建FlowDocument

简而言之:我在ReportGeneratorService中创建一个流文档,然后将FlowDocument序列化为字符串。在我的后台工作程序回调中,我接收到序列化的字符串并反序列化它-都使用XamlWriter和XmlReader进行显示,如下所示:


您的问题是,您在另一个线程中创建了FlowDocument。将数据放到非GUI容器中,并在bg返回UI线程后使用它们。