我正在使用(MVVM)模型视图模型设计模式,但我遇到了模式的问题。在我的场景中,我使用DataTable作为视图模型。此视图模型设置为DataGridView的DataSource。通常,当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();
}
}
}
答案 0 :(得分:0)
您可能想要查看装饰器模式。装饰器模式允许您将关心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 );