简体   繁体   English

WPF MVVMLight Messenger UI线程问题

[英]WPF MVVMLight Messenger UI thread issue

I have an issue regarding using MVVMLight messenger in a WPF (MVVM) application. 我在WPF(MVVM)应用程序中使用MVVMLight Messenger时遇到问题。 In a view I have an element ( LoadingControl ) which visibility property is bound to a viewmodel property ( IsBusy ) by using custom visibility converter. 在视图中,我具有一个元素( LoadingControl ),该元素的可见性属性通过使用自定义可见性转换器绑定到viewmodel属性( IsBusy )。 In a viewmodel I change the property value ( IsBusy ) by sending a MVVM message: 在视图模型中,我通过发送MVVM消息来更改属性值( IsBusy ):

Messenger.Default.Send(new LoadingMessage(true));

Here I have the method in a viewmodel which is resgistered to accept LoadingMessage messages, where IsBusy property is changed. 在这里,我有一个视图模型中的方法,该方法重新设计为接受LoadingMessage消息,其中IsBusy属性已更改。

This works fine, however in one of my viewmodel methods where I send this message, UI thread blocks and IsBusy property value changes, but visibility of my UI element LoadingControl doesn't until main operation is not finished, eg: 这很好用,但是在我发送此消息的viewmodel方法之一中,UI线程块和IsBusy属性值发生了变化,但是直到主要操作未完成时,UI元素LoadingControl的可见性才出现,例如:

void Call() {
    Messenger.Default.Send(new LoadingMessage(true));

    SomeServiceCall();
    Navigate(); // <- this works with UI, too
} // <- this is the place when LoadingControl is shown.

I guess that the problem is because in a viewmodel the execution is not on the ui thread, so I tried to call this directly in a Call() method: 我猜问题是因为在视图模型中执行不在ui线程上,所以我尝试直接在Call()方法中调用此方法:

DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
    IsBusy = true;
});

But this doesn't work too, LoadingControl is not shown until method ends. 但这也行不通,直到方法结束才显示LoadingControl As I'm not so familiar with multithreading, I supose that there is some other issue here. 由于我不太熟悉多线程,因此我认为这里还有其他问题。 Any help is appreciated. 任何帮助表示赞赏。

Messenger sends message in your thread so you need to wait. Messenger在您的线程中发送消息,因此您需要等待。 I think you need to send message in another thread. 我认为您需要在另一个线程中发送消息。

DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
    Messenger.Default.Send(new LoadingMessage(true));
});

Another (lighter) way can be to fire and forget your code with Task 另一种(更轻松的)方法是使用Task触发并忘记代码

Task.Factory.StartNew(() => 
   { 
       Messenger.Default.Send(new LoadingMessage(true));
       SomeServiceCall();
       Navigate();
   });

If you want to force your Action to be executed on another thread, you can pass TaskScheduler.Default to the StartNew() call. 如果要强制在另一个线程上执行Action ,则可以将TaskScheduler.Default传递给StartNew()调用。 But by doing so, you might fall on Invalid cross-thread access Exception , since you code no longer executes on the UI thread, but on a Background one. 但是这样做可能会导致Invalid cross-thread access Exception ,因为您的代码不再在UI线程上执行,而是在Background线程上执行。 So you might need to Dispatch your code setting view-bounded properties (like your IsBusy ) back to the UI-Thread by using the Dispatcher : 因此,您可能需要使用Dispatcher将代码设置视图范围的属性(例如IsBusyDispatcher回UI-Thread:

Application.Current.Dispatcher.BeginInvoke(() =>
{
    IsBusy = true;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM