2009-10-15 10 views
0

Je suis en train de jouer avec le (MVVM) Model View Voir le modèle motif de motif mais j'ai rencontré un problème avec le motif. Dans mon scénario, j'utilise un DataTable comme Voir le modèle. Ce modèle de vue est défini en tant que DataSource d'un DataGridView. Normalement, lorsque le Présentateur ajoute une nouvelle ligne au Modèle de Vue, une nouvelle ligne est ajoutée au DataGridView. Toutefois, si le Présentateur met à jour le Modèle de Vue depuis un thread autre que le thread d'affichage, il ne met pas correctement à jour DataGridView. Il existe plusieurs solutions de contournement, en fait j'en ai un couple dans mon exemple, mais ils semblent apporter trop de connaissances inférées sur l'interface utilisateur à la couche Presenter. Je n'ai pas besoin de savoir pourquoi cela se produit, je cherche plutôt des retours sur une approche de meilleure pratique pour traiter ce problème.(MVVM) Voir le modèle Voir le modèle et le filetage

Merci,

// 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 < 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(); 
     } 
    } 
} 

Répondre

0

Vous voudrez peut-être regarder le modèle de décorateur. Le modèle de décorateur vous permet de séparer les préoccupations de se soucier d'être sur le fil de l'interface utilisateur des préoccupations de tout ce que votre classe est censée faire.

Si vous aviez une interface IFoo comme ceci:

public interface IFoo 
{ 
    void Bar(string str); 
} 

Le décorateur pour cette classe pourrait ressembler à ceci:

public class FooDecorator : IFoo 
{ 
    private IFoo _impl; 

    public FooDecorator(IFoo impl) 
    { 
    _impl = impl; 
    } 

    public void Bar(string str) 
    { 
    InvokeOnUIThread(_impl.Bar(x)); //use whatever UI invocation you need 
    } 
} 

Maintenant, vous avez un décorateur, vous pouvez écrire une mise en œuvre concrète votre IFoo qui fait ce que vous avez besoin de faire. Passer une instance au décorateur comme ceci:

IFoo foo = new FooDecorator(someConcreteFooInstance); 

http://www.dofactory.com/Patterns/PatternDecorator.aspx

Wikipedia

Questions connexes