【问题标题】:(MVVM) Model View View Model And Threading(MVVM) 模型视图视图模型和线程
【发布时间】:2010-12-07 04:41:22
【问题描述】:

我正在使用 (MVVM) Model View View Model 设计模式,但我遇到了该模式的问题。在我的场景中,我使用 DataTable 作为 View Model。此视图模型设置为 DataGridView 的数据源。通常,当 Presenter 向 View Model 添加新行时,会向 DataGridView 添加新行,但是,如果 Presenter 从显示线程以外的线程内更新 View Model,它不会正确更新 DataGridView。有几种解决方法,实际上我的示例中有几个,但它们似乎为 Presenter 层带来了太多关于 UI 的推断知识。我不需要知道为什么会发生这种情况,相反,我正在寻找有关处理此问题的最佳实践方法的一些反馈。

谢谢,

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

【问题讨论】:

    标签: .net winforms data-binding mvvm multithreading


    【解决方案1】:

    您可能想查看装饰器模式。装饰器模式允许您将关注在 UI 线程上的关注与您的类应该做的任何关注分开。

    如果你有这样的接口 IFoo:

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

    这个类的装饰器可能如下所示:

    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
      }
    }
    

    现在你有了一个装饰器,你可以为你的 IFoo 编写一个具体的实现来做你需要它做的任何事情。像这样将实例传递给装饰器:

    IFoo foo = new FooDecorator( someConcreteFooInstance );  
    

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

    Wikipedia

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-08
      • 2011-05-20
      • 2011-02-08
      • 2012-08-06
      • 1970-01-01
      相关资源
      最近更新 更多