title: (MVVM) Model View View Model And Threading
body: <p>I'm playing around with the (MVVM) <em>Model View View Model</em> design pattern but I ran into a problem with the pattern. In my scenario I'm using a DataTable as the <strong>View Model</strong>. This View Model is set as the DataSource of a DataGridView. Normally, when the Presenter adds a new row to the View Model, a new row is added to the DataGridView however, if the Presenter updates the View Model from within a thread other than the display thread, it doesn't properly update the DataGridView. There are several workarounds, in fact I have a couple in my example, but they seem to bring too much inferred knowledge about the UI to the Presenter layer. I don't need to know why this happens, instead, I'm looking for some feedback on a best practice approach with dealing with this issue.</p>
  
  <p>Thanks,</p>
  
  <pre><code>// Implements View
  namespace WinApp
  {
      using System.Data;
      using System.Windows.Forms;
      using MVVC;
  
      class View : Form, IView
      {
          [System.STAThread]
          static void Main()
          {
              Application.Run(new View());
          }
  
          private Presenter _presenter;
          private int _topOffset;
  
          public View()
          {
              _presenter = new Presenter(this);
              AddDataGridView();
              AddButton(OperationTypes.PreferredApproach);
              AddButton(OperationTypes.ControlInvoke);
              AddButton(OperationTypes.SynchronizationContextSend);
          }
  
          void AddDataGridView()
          {
              DataGridView c = new DataGridView() { Top = _topOffset, Width = this.Width - 10, Height = 150 };
              c.DataSource = this.ViewModel;
              _topOffset += c.Height + 5;
              this.Controls.Add(c);
          }
  
          void AddButton(OperationTypes operationTypes)
          {
              Button c = new Button() { Text = operationTypes.ToString(), Top = _topOffset, Width = this.Width - 10 };
              c.Click += delegate
              {
                  this.ViewModel.Clear();
                  _presenter.LoadProgressBars(operationTypes);
              };
              _topOffset += c.Height + 5;
              this.Controls.Add(c);
          }
  
          #region IView Members
  
          public void Send(SendCallback sendCallback)
          {
              // If calling thread is not the display thread then we must use the invoke method.
              if (InvokeRequired)
              {
                  Invoke(new MethodInvoker(delegate
                  {
                      sendCallback();
                  }));
              }
              else
              {
                  sendCallback();
              }
          }
  
          public DataTable ViewModel { get; set; }
  
          #endregion
      }
  }
  
  // Doesn't have a notion of UI implementation (System.Windows.Forms)
  namespace MVVC
  {
      using System.Collections;
      using System.Data;
      using System.Threading;
  
      public delegate void SendCallback();
  
      public interface IView
      {
          DataTable ViewModel { get; set; }
  
          void Send(SendCallback sendCallback);
      }
  
      public enum OperationTypes
      {
          PreferredApproach,
          ControlInvoke,
          SynchronizationContextSend
      }
  
      public class Presenter
      {
          private IView _view;
          private Thread _thread;
  
          public Presenter(IView view)
          {
              _view = view;
              _view.ViewModel = new DataTable("TridTable");
              _view.ViewModel.Columns.Add("Column1", typeof(int));
          }
  
          public void LoadProgressBars(OperationTypes operationType)
          {
              SynchronizationContext context = SynchronizationContext.Current;
              if (_thread != null)
              {
                  _thread.Abort();
              }
              _thread = new Thread(delegate()
              {
                  string[] batch = new string[10];
                  for (int i = 0; i &lt; batch.Length; i++)
                  {
                      // Emulate long running process. (e.g. scanning large file, creating images, figuring out the meaning of life ...)
                      Thread.Sleep(500);
  
                      switch (operationType)
                      {
                          case OperationTypes.PreferredApproach:
                              // Doesn't Work
                              // Different thread so the bindings won't get notified.
                              _view.ViewModel.Rows.Add(i);
                              break;
                          case OperationTypes.ControlInvoke:
                              // Does Work
                              // Send back to view to delegate work
                              _view.Send(delegate
                              {
                                  _view.ViewModel.Rows.Add(i);
                              });
                              break;
                          case OperationTypes.SynchronizationContextSend:
                              // Does Work
                              // Dispatch a synchronous message to the Synchronization Context of the display thread
                              context.Send(delegate
                              {
                                  _view.ViewModel.Rows.Add(i);
                              }, null);
                              break;
                      }
                  }
              });
              _thread.Start();
          }
      }
  }
  </code></pre>