|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThe terms "Model View Controller" and "Model View Presenter" (MVP) are used to describe patterns that have been in use for some time in other technology areas but have recently come to the fore in the C# world. The Model View Presenter is a derivation of the Model View Controller pattern. With modern ides such as Visual Studio inserting event handling into what we refer to as the view, it makes sense to leave them there rather than trying to implement them on a controller. I struggled to find a simple MVP example on the web that was geared towards C# WinForms, and after reading Bill McCafferty's excellent article on MVP within ASP.NET, I decided to throw my hat into the ring. I'm going to concentrate on the code. For a background on MVP, I suggest you try this link: Why MVP?The Model View Presenter pattern is designed to abstract the display of data away from the data and associated actions (e.g., saving the state). This, in theory, should make testing easier and more relevant, and remove the tight coupling typically found between data and forms within the Windows environment. The ViewThe View is a user control that inherits from public class UserView : Form, IUserView
The ModelIt is a representation of the data being manipulated. In my simple example, this a user object. The Model should implement an interface ( public class UserModel : IUserModel
The PresenterThe Presenter marries the View to the Model. When first called, it updates all the properties of the View to correspond to the Model. Furthermore, it binds the View's events to methods in itself. Typically, the Presenter will update the Model based on changes in the View. Once a user has finished making changes in the View, the Model should be in sync and will be saved down correctly. The Presenter does not require an interface. public UserPresenter(IUserModel model,IUserView view)
{
this._model = model;
this._view = view;
this.SetViewPropertiesFromModel();
this.WireUpViewEvents();
}
In my example, I use Reflection to iterate through the properties of the View / Model and update the corresponding field. Reflection is slow, but I haven't experienced any tangible slow down in any of my Windows apps yet - it would be easy to pass through a reference to the specific property being updated if speed starts to become an issue. Putting it TogetherIUserModel model = new UserModel();
IUserView view = new UserView();
new UserPresenter(model,view);
view.Show();
The code to get the ball rolling is simplicity itself, as shown above. TestingThe example with this article includes some tests. Because our Presenter expects interfaces as opposed to concrete objects, it allows us to perform dependency injection with stubs and mocks, should we choose. [Test]
public void DoesViewCorrespondToModel()
{
StubView stub = new StubView();
new UserPresenter(this._mock, stub);
In my tests, I have a My stub implements Event Mocking, e.g.: public void FireDataChanged()
{
if (this.DataChanged != null)
{
this.DataChanged(null, EventArgs.Empty);
}
}
From within my test, I simply call ' A Word on DataBindingYou may be wondering why I go to the trouble of implementing code in the Presenter to update the Model / View when Microsoft kindly provides us with databinding technology to bind data to Windows controls. The great thing about databinding is that it removes the need for having to write code in every Presenter to take care of the state, thus speeding the development cycle. The problem with databinding is that it breaks our encapsulation by tightly coupling the 'View' to the data. Also, it cannot be tested from within an NUnit environment. However, one still needs to write laborious code within the Presenter to keep the Model in sync with the View. I hope that the code example I have provided you with that uses Reflection will ease this pain. Simply expose the data you require in the View and Model interfaces, and it should all be taken care of for you. I am not a pattern zealot; you may find that given time constraints and the nature of a task you need to perform, a tightly bound view using databinding is the best option - however, I would encourage the abstraction of logic from presentation and the use of tests wherever possible. Refactoring the InterfacesIt became apparent in the development of this article that a lot of property definitions are shared between the FinallyUsing MVP within WinForms is a learning curve, and I am keen to hear constructive criticism from anyone who thinks the example I have provided can be improved. By sharing knowledge, we all gain. CreditsThe MVP pattern has been developed by many coders over a long period of time. Martin Fowler presents that knowledge in a nice concise form on his website (link at the top of the article). Thanks to Bill McCafferty for investing so much time in the article he wrote about MVP in ASP.NET (link at the top of article) which inspired me to submit a WinForms focused article.
|
||||||||||||||||||||||