我一定是在搜索方面落后了,因为这是另一个看似常见的问题,但我无法解决。
这是我的问题 - 我正在使用 WPF 和 MVVM,并且我有一个在模型中执行的状态机。如果发生错误,我需要将信息传递给 ViewModel 来显示错误。这部分似乎工作正常。当用户单击所需的行为时,模型中的代码将继续执行,并查看用户与之交互的对象以确定下一步要做什么。
问题是模型需要重新加载文件,这会使用该文件的内容更新 GUI。因为模型是在线程中执行的,所以你可以想象我接下来要问的是什么——你到底如何正确地与 GUI 同步?在 MFC 中,我会使用 SendMessage 或 PostMessage 来完成 GUI 更新。
我阅读了有关 WinForms 的文章,建议在必要时使用 InvokeRequired 自动调用 BeginInvoke 。我实际上不知道 BeginInvoke 能实现我想要的,所以我被鼓励学习这一点。
我如何从我的模型中实际调用 BeginInvoke?此方法是否适用于 WPF? 我继续实现了一个委托,然后调用 Invoke,但我得到了相同的错误,告诉我无法从此线程修改集合。我还尝试了 BeginInvoke,但我认为这也行不通,因为无论如何它都会从不同的线程启动。
使困惑。如果我错过了互联网上发布的一些非常明显的内容,请继续对我进行口头批评,我可能活该。
编辑 - 我可能还应该补充一点,我正在寻找除计时器或基于BackgroundWorker的解决方案之外的其他解决方案,除非这是在WPF / MVVM中解决此问题的唯一方法。另外,我想知道是否有任何 MVVM 工具包已经具备此类功能......
I must be retarded with searching, because here's another seemingly common problem that I haven't been able to solve.
Here's my problem -- I am using WPF and MVVM, and I have a statemachine that executes in the model. If an error occurs, I need to pass information up to the ViewModel to display the error. This part seems to work okay. When the user clicks the desired behavior, the code in the model continues, and looks at the object the user interacts with to determine what to do next.
The problem is that the model needs to reload a file, which updates the GUI with the contents of said file. Because the model is executing in a thread, you can imagine what I'm going to ask next -- how the hell do you synchronize with the GUI properly? In MFC, I would have used either SendMessage or PostMessage to accomplish the GUI update.
I've read articles for WinForms that suggest using InvokeRequired to automatically call BeginInvoke if necessary. I actually didn't know that BeginInvoke would accomplish what I wanted, so I was encouraged to learn this.
How do I actually call BeginInvoke from my model? Does this method even apply to WPF? I went ahead and implemented a delegate and then called Invoke, but I get the same error that tells me the collection can't be modified from this thread. I also tried BeginInvoke for the hell of it, but I assume that also wouldn't work because it would just launch from a different thread anyway.
Confused. If I have missed something really obvious that's been posted about all over the internet, go ahead and give me a verbal lashing, I probably deserve it.
EDIT - I should probably also add that I am looking for something other than a timer or BackgroundWorker-based solution, unless that's the only way to solve this in WPF / MVVM. Also, I wonder if any of the MVVM toolkits would have facilities for this sort of thing already...
发布评论
评论(3)
如果要将某些工作从后台线程安排到 WPF 中的 UI 线程,请使用
DispatcherObject
。这是一篇关于如何使用调度程序构建更多响应式应用程序的好文章。更新:请注意,如果您使用事件从 Model 向 ViewModel 发送通知,您仍然需要在某处切换到 UI 线程。该开关是否应该在模型中或视图模型中是一个很好的设计讨论,但它与您的问题正交。
该事件将在相应的 Dispatcher 线程上引发。由于您需要访问 UI 线程,因此需要使用在 UI 线程上创建的 Dispatcher。获取它的最简单方法是在 UI 元素之一上使用
DispatcherObject.Dispatcher
属性。另一种方法是在模型或视图模型中创建一个。如果您是设计纯粹主义者,我建议您在模型中创建 Dispatcher,并在引发 ViewModel 正在侦听的事件之前将调用分派回 UI 线程。这样,所有线程切换和管理都包含在模型中,并且 ViewModel 仅在 UI 线程上作为单线程工作。If you want to schedule some work from a background thread to the UI thread in WPF, use the
DispatcherObject
. Here's a nice article on how to Build More Responsive Apps with the Dispatcher.Update: Note that if you use an event to send notifications from the Model to the ViewModel, you still need to switch to the UI thread somewhere. Whether that switch should be in the Model or the ViewModel is a good design discussion, but it's orthogonal to your question.
The event will be raised on the corresponding Dispatcher thread. Since you need to get to the UI thread, you need to use a Dispatcher that is created on the UI thread. The easiest way to get one is to use the
DispatcherObject.Dispatcher
property on one of the UI elements. The alternative is to create one in your Model or ViewModel. If you are a design purist, I would suggest you create the Dispatcher in your Model and dispatch the call back to the UI thread before you raise the event to which the ViewModel is listening. This way all the thread switching and management is contained in your Model and the ViewModel works as a single-threaded on the UI thread only.我认为你的 ViewModel 确实不应该知道有关 View 的任何信息,包括它是否是 WPF UI,或者该 UI 是否具有 Dispatcher 线程的概念,因此一旦你开始,红旗就应该飘扬在 ViewModel 中编写尝试 CheckAccess() 或 InvokeRequired 的代码,以便将某些代码编组到 UI 线程。相反,我会让模型引发一个视图可以侦听并相应更新自身的事件,或者让 ViewModel 公开一个属性(例如 bool FileIsLoading),视图只需绑定到该属性即可检测并显示模型是什么异步执行,ViewModel 负责确保该属性的值准确。
例如:
I think that your ViewModel really shouldn't know anything about the View, including whether or not it's a WPF UI, or whether or not that UI even has the concept of a Dispatcher thread, so the red flag should fly as soon as you start writing code in your ViewModel that attempts to CheckAccess() or InvokeRequired in order to marshal some code to the UI thread. Instead I'd have the model raise an event that the View can listen for and update itself accordingly, or have the ViewModel expose a property (eg. bool FileIsLoading) that the View simply binds to in order to detect and display what the model is doing asynchronously, and it's the ViewModel's responsibility to ensure that the value of that property is accurate.
For example:
我有另一种似乎有效的方法,我只是想把它扔在那里以获得一些评论(如果有人再读这个问题的话!)。
我开始使用 MVVM Light Toolkit 的 Messenger 类,它似乎对我来说非常有效。例如,我们以 ProgressBar 为例。我向 ViewModel 注册了两条消息,用于设置进度值和进度最大值。然后在我的模型中,当它设置任务和整个流程时,它会发送这些消息。当虚拟机收到消息时,它只会更新数据绑定值,并且我的 GUI 会自动更新!这非常简单,但我想知道你们对这种方法有何看法。还有其他人这样做没有发生任何事故吗?
I've got another approach that seems to work, and I just wanted to throw it out there to get some comments (if anyone is even reading this question anymore!).
I started to use the MVVM Light Toolkit's Messenger class, and it seems to work really well for me. For example, let's take the ProgressBar as an example. I registered two messages with my ViewModel for setting the progress value and progress maximum. Then in my model, as it sets up the tasks and overall process, it sends these messages. When the VM receives the messages, it just updates databound values, and my GUI updates automatically! It's super duper easy, but I was wondering what you all thought about this approach. Is anyone else doing this without incident?