Equivalent to PostMessage in C # for synchronizing with the main thread with MVVM?

I have to be slowed down with the search, because here is another seemingly common problem that I could not solve.

Here is my problem - I use WPF and MVVM, and I have a statemachine that runs in the model. If an error occurs, I need to pass the information to the ViewModel in order to display this error. This part seems to be working fine. When the user clicks the desired behavior, the code in the model continues and looks at the object with which the user interacts to determine what to do next.

The problem is that the model needs to reload the file, which updates the GUI with the contents of the specified file. Since the model runs in a thread, you can imagine what I will ask next - how the hell did you synchronize correctly with the GUI? In MFC, I would use SendMessage or PostMessage to perform GUI updates.

I read articles for WinForms that suggest using InvokeRequired to automatically call BeginInvoke if necessary. I really did not know that BeginInvoke would do what I wanted, so I was asked to study this.

How can I call BeginInvoke from my model? Does this method even apply to WPF? . I went ahead and implemented the delegate and then called Invoke, but I get the same error that tells me that the collection cannot be modified from this thread. I also tried BeginInvoke, damn it, but I guess that won't work either, because it will still start from another thread.

Confused If I missed something really obvious that was published all over the Internet, go ahead and give me a verbal overlay, I probably deserve it.

EDIT . I should probably add that I'm looking for something else besides a timer or a BackgroundWorker based solution, if that is the only way to solve this problem in WPF / MVVM. Also, I am wondering if any of the MVVM toolkits can have such features ...

+6
synchronization user-interface c # wpf mvvm
Mar 09 '10 at 17:34
source share
3 answers

If you want to schedule some work from a background thread to a user interface thread in WPF, use DispatcherObject . Here is a good article on how to create more responsive apps with Dispatcher .

Update: Please note that if you use the event to send notifications from the model to the ViewModel, you still need to switch to the user interface thread somewhere. Whether this switch should be in Model or ViewModel is a good design discussion, but it is orthogonal to your question.

The event will be raised in the appropriate dispatcher thread. Since you need to go to the user interface thread, you need to use the dispatcher created in the user interface thread. The easiest way to get this is to use the DispatcherObject.Dispatcher property on one of the interface elements. An alternative is to create one in your model or ViewModel. If you are a purist designer, I would suggest you create a Dispatcher in your model and send the call back to the UI thread before raising the event that the ViewModel is listening to. Thus, all thread switching and control are contained in your model, and the ViewModel only works single-threaded in the user interface thread.

+7
Mar 09 '10 at 17:43
source share

I think that your ViewModel really does not need to know anything about the view, including whether it is a WPF interface or not, that this interface even has a Dispatcher stream concept, so the red flag should fly as soon as possible when you start writing code in ViewModel, which is trying CheckAccess () or InvokeRequired to march some code into the UI thread. Instead, I would have a model to raise an event that the View can listen to and update accordingly, or the ViewModel provides a property (e.g. bool FileIsLoading) that the View simply binds to detect and display what the model is asynchronously, and it is ViewModel's responsibility to ensure that the value of this property is correct.

For example:

 public partial class MainWindow : Window { private ViewModel _model = new ViewModel(); public MainWindow() { InitializeComponent(); DataContext = _model; } private void Button_Click(object sender, RoutedEventArgs e) { _model.Run(); } } <Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <Button Click="Button_Click" Content="Run" IsEnabled="{Binding IsIdle}" /> </Grid> </Window> public class ViewModel : INotifyPropertyChanged { private bool _isIdle = true; public bool IsIdle { get { return _isIdle; } set { _isIdle = value; OnPropertyChanged("IsIdle"); } } public void Run() { ThreadPool.QueueUserWorkItem((state) => { IsIdle = false; Thread.Sleep(10000); IsIdle = true; }); } #region INotifyPropertyChanged Implementation protected void OnPropertyChanged(string propertyName) { PropertyChangedEventHandler propertyChanged = this.PropertyChanged; if (propertyChanged != null) { propertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } public event PropertyChangedEventHandler PropertyChanged; #endregion } 
+4
Mar 09 '10 at 17:42
source share

I have another approach that seems to work, and I just wanted to throw it away to get some comments (if anyone even reads this question more!).

I started using the MVVM Light Toolkit Messenger class, and it seems to work very well for me. For example, consider the ProgressBar example. I logged two posts using ViewModel to set progress value and maximum progress. Then in my model, when he sets the tasks and the overall process, he sends these messages. When a virtual machine receives messages, it simply updates the data values ​​in the database and automatically updates my GUIs! This is super duper simple, but I was wondering what you all thought about this approach. Does anyone else do this without incident?

+1
May 26 '10 at 16:03
source share



All Articles