현재 C# WPF에서 문제가 있습니다. 백그라운드 작업에서 장기 실행 보고서를 생성하는 응용 프로그램을 작성했습니다. 프리즘을 MVVM과 함께 사용하고 비동기 ICommand 구현과 BackgroundWorker로 비싼 백그라운드 작업을 실행하려고합니다. 난 결과 보고서Dispatcher를 사용할 때 BackgroundWorker에서 MVVM InvalidOperationException이 발생했습니다.
Report = asyncTask.Result;
를 검색 할 때하지만 난라는 InvalidOperationException이 얻을 "다른 스레드가 그것을 소유하고 있기 때문에 호출 스레드가이 개체에 액세스 할 수 있습니다.".
예, 예외 메시지를 검색 할 때 Google에서 가장 먼저 발견 할 것으로 보이는 발송자 (예 : stackoverflow 등)를 이미 호출하려고했습니다. 나는 예를 들어 같은 몇 가지 변종을 시도 :
Dispatcher.CurrentDispatcher.Invoke(() => Report = asyncTaks.Result);
또는
Report.Dispatcher.Invoke(() => Report = asyncTask.Result);
하지만 때마다 내가이 예외를 얻을.
보고서 UI를 호출하는 방식이 적절하지 않다고 의심됩니다. 내가 생각에서입니다
MainWindowViewModel
-> SubWindowCommand
SubWindowViewModel
-> GenerateReportCommand
ReportViewModel
-> GenerateReportAsyncCommand
<- Exception on callback
는 아무도 내가 잘못된 일을 할 수있는 어떤 단서가 않습니다 다음과 같이
구조는 간략하게 보인다? 내가
으로 ReportView 창var reportViewModel = _container.Resolve<ReportFlowDocumentViewModel>();
View.ReportViewerWindowAction.WindowContent = reportViewModel.View;
reportViewModel.TriggerReportGeneration();
var popupNotification = new Notification()
{
Title = "Report Viewer",
};
ShowReportViewerRequest.Raise(popupNotification);
을 시작하는 방법
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);
}
}
이것은 : 아래
는보고서 생성기보기 모델 몇 가지 코드 조각입니다 10
AsyncCommand 정의
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 :
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();
}
}
'BackgroundWorker.DoWork' 이벤트 처리기를 사용하여 백그라운드 스레드에서 보고서를 생성하지만 액세스하려고합니다. 나중에 메인 스레드에서. 따라서 예외. – dymanoid
'Dispatcher.CurrentDispatcher.Invoke'는 UI 스레드를 사용합니다. UI 요소를 업데이트하지만 데이터 바인딩에 INotifyPropertyChanged가 있기 때문에 MVAVM에서 유용하지 않습니다. –
보고서는 FlowDocument UI 요소에 바인딩되어 있으므로 사용할 수 있다고 생각합니다. Dispatcher.CurrentDispatcher.Invoke를 사용할 수 없다면, 그 밖의 무엇입니까? – Michael