Data mapping in winforms MVP

I have a WinForms application implemented in MVP. My form has a TextBox , and I want to bind its Text property to a property in the Model. I do not want to refer to the model in the view.

After searching on Google, I found that data binding by connecting the model and view is a bad idea. My initialization of the Model , View and Presenter sample is as follows.

 class View : Form, IView { public View() { InitializeComponent(); new Presenter(this); } } class Presenter { public Presenter(IView) : this.Presenter(this, new Model()) { } public Presenter(IView view) { } } class Model : IModel { public Model() { } } 

I currently have 3 projects for Model , View and Presenter . View has a link to Presenter and Presenter has a link to Model . Can someone explain to me how to form the data binding to the control in the View to the property in the Model ?

EDIT

I know what needs to be done in Grid. We can assign the Datasource property of a List (or something similar) mesh in the presenter, for example:

 _view.DataSource = _model.ListOfEmployees; 

This will reflect the value in the user interface when ListOfEmployees changes in the model. But what about the TextBox that provides the Text property? How can I link this in MVP architecture?

+6
source share
3 answers

My recommendation is to encapsulate the view and model in the presenter. This means a specialized rapporteur (in most cases) for this presentation. In my opinion, this works well, as most models will still be different.

 class Presenter { readonly IView view; readonly IModel model; public Presenter() { // if view needs ref. to presenter, pass into view ctor view = new View(this); model = new Model(); } // alternatively - with the model injected - my preference public Presenter(IModel Model) { // if view needs ref. to presenter, pass into view ctor view = new View(this); model = Model; } } 

In your IView, derive a control or data source control property:

 interface IView { object GridDataSource { get; set; } } 

Add a method to your presenter:

 void SetGridDatasource() { view.GridDatasource = model.SomeBindableData; } 

View implementation:

 public object GridDatasource { get { return myGridView.DataSource; } set { myGridView.DataSource = value; } } 

Note:
Code snippets are not validated and are recommended as a starting point.

Update comments:
INotifyPropertyChanged is a very valuable mechanism for updating properties between IModel and IModel .

Most controls have some kind of binding. I would recommend using these DataBinding methods whenever possible. Simply output these properties via IView and let Presenter set these bindings to the IModel properties.

+8
source

Explanation:

If you want to link the binding of the text field and the base property of the model:

First: Like many other states, everything that contains your properties must implement the INotifyPropertyChanged interface so that when the property of the object changes, the necessary event is sent to notify the view of the change. I would like to use the viewmodel as a property of your model in this regard to encapsulate specific properties that you would like to bind to your view.

Secondly: your IView will include the viewmodel property that your View should implement.

Third: your view will implement the IView property with just the access element of the viewmodel to bind each text field to the dto properties. Pay attention to the example below, as I never manually set the text box again after loading the view. The textbox.text values ​​will now be updated when the modelmodel property of the base model changes. This works both ways (two way data binding). Editing a text field using user input will change the value of the dto property of the base model.

Fourth: your presenter will set the IView property to the Model property only once, at boot time.

Example: keep in mind this is a very simplified simplified example and does not have any model abstraction such as OP, but should provide a good starting point for binding text fields in Winforms MVP. Another thing that I would change in a production application would be to make a stateless model and move the viewmodel (person) to the presenter.

 //VIEWMODEL public class Person : INotifyPropertyChanged { string _firstName; string _lastName; public string FirstName { get { return _firstName; } set { if(value != _firstName) { _firstName = value; NotifyPropertyChanged("FirstName"); } } } public string LastName { get { return _lastName; } set { if (value != _lastName) { _lastName = value; NotifyPropertyChanged("LastName"); } } } public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(String info) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(info)); } } } //MODEL class Model { Person _person; public Person Person { get { return _person; } } public Model() { //Set default value _person = new Person(){ FirstName = "Test", LastName = "Subject" }; } public void ChangePerson() { //When presenter calls this method, it will change the underlying source field and will reflect the changes in the View. _person.FirstName = "Homer"; _person.LastName = "Simpson"; } } //PRESENTER class Presenter { readonly View _view; readonly Model _model; public Presenter(View view) { _view = view; _model = new Model(); _view.OnViewLoad += Load; _view.OnChangePerson += ChangePerson; } private void Load() { _view.Person = _model.Person; } private void ChangePerson() { _model.ChangePerson(); } } //IVIEW interface IView { Person person { set; } event Action OnViewLoad; event Action OnChangePerson; } //VIEW public partial class View : IView { public View() { Presenter presenter = new Presenter(this); this.Load += (s, e) => OnViewLoad(); //Shorthand event delegate this.btnChange.Click += (s, e) => OnChangePerson(); //Shorthand event delegate } public event Action OnViewLoad; public event Action OnChangePerson; public Person person { //This is how you set textbox two-way databinding set { //Databinding syntax: property of control, source, source property, enable formatting, when to update datasource, null value txtFirstName.DataBindings.Add(new Binding("Text", value, "FirstName", true, DataSourceUpdateMode.OnPropertyChanged, string.Empty)); txtLastName.DataBindings.Add(new Binding("Text", value, "LastName", true, DataSourceUpdateMode.OnPropertyChanged, string.Empty)); } } } 
+5
source

I played a little with MVP in WinForms, and there are many problems to solve. Most of the problems come from the fact that you are in VS, and it's nice to be able to easily create forms using Designers forms.

I tried the WinForms MVP API, which was developed from the widely used WebForms MVP project, but since the code behind the file for the form used Generics (for example, the public class TheForm: UserControl), you lose the ability to create the form because the designer knows how to handle Generics .

I ended up working with the main interfaces, IPresenter, IView, IViewModel. I ALWAYS created an intermediate interface for a specific implementation, even if I do not add any additional properties, mainly because it is simply easier to transfer the changes later when I want to add additional functions. IPresenter will take the covariant type of an IView type generator, so in the inheritance chain I can make presentators of a certain type of child type. In the end, creating a dialog is done by creating an instance of Presenter and calling Show:

 ISomePresenter<ISomeView> somePresenter = new SomeFactory.GetSomePresenter(); somePresenter.Show(); 

My view contains a copy of IViewModel:

 public void Show() { ISomeView theView = new V(); theView.ViewModel = new SomePresenterViewModel(); . . . } 

There is no answer to the original question ... SampleView cannot know about ISampleViewModel, so it is impossible to perform standard data binding to ViewModel without putting the listing somewhere. This got out of hand in a project in which I developed all this, and people threw all the places in the event handlers, as well as the BindingSource wizard. The whole point of MVP has been lost.

So, now I am very strict about how to handle events and whether I set the control as public properties (and the accompanying ISampleView property) so that the Presenter can see them or just create repeating events to restart the event that will be picked up by the Lead, I thought about the whole data puzzle. Indeed, the only way to do this is without the support of the designer and do whatever the designer does in the code inside the Presenter. Perhaps use the constructor to get the automatically generated code in the .designer.cs file, but cut out all the code in Presenter. Maybe do it once to get the syntax, etc., And then bash enter the boiler plate code or create a fragment based on what is generated. You still need access to the actual control for the view to specify the binding, so along with it also add a property to ISampleView, which returns an instance of the control. In addition, I would recommend placing instances of BindingSource in Presenter, as well as, or at least some other class in which Presenter has an instance.

I like to use the constructor as much as possible, but sometimes you need to take a break. As I said, the WinForms MVP project on CodePlex is great, but all forms are designed in code. In my scenario, this is just a DataBinding that needs to be done in code, which is not really a visual thing, so this is easier to handle.

In addition, as an optional note, NotifyPropertyWeaver (IL Weaving) supports full data binding. This is brilliant in that you can create automatic properties in your view models that keep your code concise and readable without having to send NotifyPropertyChanging calls, etc. For each property. IL Weaving with Fody does all these compilations before the final build exit. Very comfortably.

Anyway, I hope that this brain dump concept around a problem is appreciated by someone. I understood for a long time, but for me it is very good.

Steve

Edit 2014-04-23

You know that .NET databinding is a huge pain in the ass. Recently, in a project, we just finished our own data binding code for a specific control, because everything is simply difficult to work with.

Rethinking my initial answer with even newer experiences, the basic model should be completely divided. I tended to create what I called the ViewModel, which negotiates with the database and is a DataBindable and viewable View. The data comparison caused me the same grief, especially when handling control events such as DateTimePicker ValueChanged. In one scenario, I have a start and end date picker, as well as a checked checkbox to set the end date to one day after the start date and other range rules that I need to consider. When binding data configured on a virtual machine when changing values ​​based on some rule, the events light up again and end with the excellent choices I made. In the end, I need to add bool values ​​to find out if the event handler should continue or not, then there are potential race conditions or I don’t know if the event handler (in another thread) should wait or not. It turns out randomly very quickly.

So now my approach is to create a large model that relates to the database and can perform validation of validation rules based on the captured data, but I will create a small, lighter version that simply contains properties for data binding. The division into the real model is still in place, and any events that the Presenter / Controller responds to can simply be copied from the virtual machine to the main model while saving data / data saving time. If I respond to events, then by setting the vm values ​​that should be tied to the fact that the lighter weight of the VM, I can create a whole new instance of the virtual machine and reassign the test results, then set this new instance of VM as .DataSource BindingSource in the view when I'm ready, which avoids the mess of an event handler.

The main model can respond to the NotifyPropertyChanged event in order to update itself with changes or even better just make the presenter do it at the right time.

By the way, it seems that Visual Studio 2012 and 2013 now handle common controls in the designer, which is very cool.

As an additional note, I recently developed iOS. One thing that I'm very impressed with is how they baked in MVC as part of the process, unlike .NET, which allows us to come up with all kinds of hacking methods. I have learned some lessons from this and applied them to .NET, and finding my brain does not break so much. One thing in particular I like is how the list controls work, which are very similar to the MVC Qt (C ++) controls. The ability to have monolithic backend lists of objects, but the view only contains what it needs in the visible area, is much better than the default behavior of the .NET control.

In any case, good luck with .NET. I personally recommend to all new users ... not to use it and just make the controller assign all the values ​​explicitly at the appropriate time points. But if you are comfortable and understand the annoying nuances, I hope that some of what I said reaches someone.

+3
source

All Articles