I'm trying to use MVP and I notice that my View must know Model that should not happen in MVP I presume.
here is example:
public partial class TestForm : Form, ITestView
{
public void LoadList(IEnumerable<AppSignature> data)
{
testPresenterBindingSource.DataSource = data;
}
}
public interface ITestView
{
event EventHandler<EventArgs> Load;
void LoadList(IEnumerable<AppSignature> data);
}
public class TestPresenter
{
private ITestView view;
public TestPresenter(ITestView view)
{
this.view = view;
view.Load += View_Load;
}
private void View_Load(object sender, EventArgs e)
{
var data = // get from model
view.LoadList(data);
}
}
and the problem is that in TestForm I need reference to AppSignature.
In all tutorials I saw, there are some simple examples like
public void LoadList(IEnumerable<String> data) where there is no need reference to model. But how i.e DataGridView can publish current row data?
Your form is a View, it is not a Presenter. Thus it should implement interface ITestView:
public interface ITestView
{
event EventHandler Load;
void LoadList(IEnumerable<AppSignatureDto> data);
}
And your Presenter is someone, who subscribes to view's events and uses view properties to read and update view:
public class TestPresenter
{
private ITestView view;
public TestPresenter(ITestView view)
{
this.view = view;
view.Load += View_Load;
}
private void View_Load(object sender, EventArgs e)
{
List<AppSignature> signatures = // get from model
List<AppSignatureDto> signatureDtos = // map domain class to dto
view.LoadList(signatureDtos);
}
}
And you form, as I already said, is a view, it does not know anything about presenter and model:
public partial class TestForm : Form, ITestView
{
public event EventHandler Load;
private void ButtonLoad_Click(object sender, EventArgs e)
{
if (Load != null)
Load(this, EventArgs.Empty);
}
public void LoadList(IEnumerable<AppSignatureDto> data)
{
// populate grid view here
}
}
How to deal with reference to domain classes? Usually I provide to view only simple data (strings, integers, dates, etc), or I create data transfer objects, which are passed to view (you can name them FooView, FooDto, etc). You can easily map them with something like AtoMapper:
List<AppSignatureDto> signatureDtos =
Mapper.Map<List<AppSignature>, List<AppSignatureDto>>(signatures);
The View may have knowledge of Model as long as the interaction is limited to data binding only. i.e. View should not try to manipulate Model directly. View will always redirect user input to Presenter and Presenter will take care of further actions. If any action performed by Presenter results in a change in state of Model, Model will notify View via data binding. Model will be completely unaware of existence of View.
Is it OK to get DataSource in Presenter and there set its DataSource ?
e.g.
Presenter code:
Public void LoadData()
{
_view.Data.DataSource = Business.GetData().ToList();
}
Form code:
Public BindingSource Data
{
get
{
return this.bsData;
}
}
Thanks to that I dont need to add any references to the View, but I didn't see that solution in any other sources.
Related
I'm trying to make some changes to the code a colleague made.
So, I have a ShellView that loads documents (and shows them as its content) with a method defined in its ViewModel, and the child view, a StatusBarView which holds the path navigated in the documents and some other infos.
public class ShellViewModel
{
public StatusBarViewModel StatusBar { get; }
public ShellViewModel(StatusBarViewModel statusBarViewModel, ...)
{
StatusBar = statusBarViewModel;
var keymap = new Keymap();
keymap.Map("F2", new SimpleCommand("open-file",
"Shows the open file dialog",
param => OpenFile());
}
private void OpenFile()
{
// Logic to open the file that uses other methods
// inside this VM to validate the file
}
}
At the moment you can load a new pack of documents pressing a key, I'd like to do the same with a button in the Status Bar and calling that method.
What is the proper way to call a method existing in the parent view from the child view?
In your child view Define an event Handler
public EventHandler OpenFileHandler
On the click of the button of your Status Bar view do this:
public Btn_Click(object sender, RoutedEventArgs e)
{
OpenFileHandler(this, e);
}
in your parent view, when you create your status bar view, define the delegate
statusbar.OpenFileHandler+= delegate
{
ShellViewModel instance = this.DataContext as ShellViewModel;
instance.OpenFile();
}
statusbar is the name i gave to your status bar view, but it represent the instance of it
There are many ways. First need to analyze your purpose.
- You can create an ActionEvent or EventHandler inside childview and on button click you can raise that event.
- Another way use can use Mediator pattern/Observer pattern
Example:
Inside child ViewModel:
public event EventHandler openFileEvent;
Inside click button action on status bar:
private void btnClick()
{
if(null != openFileEvent)
{
openFileEvent(this, new EventArgs{});
}
}
Inside Parent ViewModel:
statusBarViewModel.openFileEvent += new EventHandler(EventHandlerName);
private void EventHandlerName(objehct sender, EventArgs...)
{
...
OpenFile();
...
}
Using the Web Forms MVP framework in an ASP.NET 4.5 Web Forms application, how do I get a reference to a page's ModelStateDictionary object from inside the Presenter object for that page?
I'd like my presenter to be able to set model state errors when something goes wrong. For example: errors when attempting to insert records that violate a UNIQUE constraint.
try
{
dbcontext.SaveChanges();
}
catch (DbUpdateException updateException)
{
// how to get a reference to the model state?
ModelStateDictionary state = null;
// add the error to the model state for display by the view
state.AddModelError("key", updateException.GetBaseException().Message);
}
A google search for "webformsmvp presenter modelstatedictionary" yields an alarmingly low number of relevant results.
One way is to pass the model state from the view to the presenter as an event argument.
First, the EventArgs class:
public class ModelStateEventArgs : EventArgs
{
public ModelStateEventArgs(ModelStateDictionary modelState)
{
this.ModelState = modelState;
}
public ModelStateDictionary ModelState { get; private set; }
}
derive from this class if you need additional event arguments
Then, the IView that is implemented by the view:
public interface IDataContextView : IView<DataContextVM>
{
event EventHandler<ModelStateEventArgs> Update;
}
Raising the event in the view itself:
MvpPage views
this.Update(this, new ModelStateEventArgs(this.ModelState));
MvpUserControl views
this.Update(this, new ModelStateEventArgs(this.Page.ModelState));
Finally, the Presenter, which can subscribe to the Update events and get the model state for each event as it happens:
public class DataContextPresenter : Presenter<IDataContextView>
{
public DataContextPresenter(IDataContextView view)
: base(view)
{
this.View.Update += OnUpdating();
}
private void OnUpdating(object sender, ModelStateEventArgs e)
{
var entity = ConvertViewModelToEntity(this.View.Model);
dbcontext.Entry(entity).State = EntityState.Modified;
try
{
dbcontext.SaveChanges();
}
catch (DbUpdateException updateException)
{
// add the error to the model state for display by the view
e.ModelState.AddModelError(string.Empty, updateException.GetBaseException().Message);
}
}
}
I've been looking in the MVP pattern for a while, and managed to create some simple MVP-compliant applications.
I am now trying to apply the pattern to a more complex application, and I have some doubts on the best way of doing that.
My application has a single WinForm, with two buttons for loading two different kinds of data. My view interface looks like the following:
interface IView_MainForm
{
// Load input
//
event EventHandler<InputLoadEventArgs> LoadInput_01;
event EventHandler<InputLoadEventArgs> LoadInput_02;
bool Input01_Loaded { get; set; }
bool Input02_Loaded { get; set; }
}
The IView is referenced in my presenter via constructor injection:
public Presenter_MainForm(IView_MainForm view)
{
this.View = view;
this.View.LoadInput_01 += new EventHandler<InputLoadEventArgs>(OnLoadInput_01);
this.View.LoadInput_02 += new EventHandler<InputLoadEventArgs>(OnLoadInput_02);
}
So far, so good. When the user clicks any of the two buttons for loading data, a LoadInput_## event is raised, the Presenter is handling it, checks the input for errors and structures it according to my data model.
My next step would be displaying the processed data back in the View.
I'm striving to keep my View as passive and "dumb" as possible, assuming it knows nothing of the Presenter (it doesn't subscribe to its events, the Presenter sends data to the View by calling IView methods instead), let alone of the Model.
How am I supposed to populate a control like a TreeView, if the View has no idea of what the data model looks like?
Also, am I getting the whole MVP thing right, or is there anything I have missed?
There is nothing wrong with having complex type properties in your View. Let's say you have some ComplexType.
class ComplexType
{
public string ParentNode {get;set;}
public List<string> ChildNodes {get;set;}
// some other properties
}
Let's also assume ComplexType is data model for your TreeView. It is perfectly fine with MVP pattern to have properties on your View that will have ComplexType. So having something like this is perfectly fine
interface IView_MainForm
{
// Load input
//
event EventHandler<InputLoadEventArgs> LoadInput_01;
event EventHandler<InputLoadEventArgs> LoadInput_02;
bool Input01_Loaded { get; set; }
bool Input02_Loaded { get; set; }
ComplexType Input01Data {get;set;} // you might actually have some code in get/set setters
ComplexType Input02Data {get;set;} // you might actually have some code in get/set setters
public void SetInput01Data(ComplexType input01Data)
{
Input01Data = input01Data;
// some other stuff
}
}
And since your Model is for View that has 2 inputs, your Model could look something like this
public interface IModel
{
public ComplexType Input01Data {get;set;}
public ComplexType Input02Data {get;set;}
}
Now in your Presenter you would just handle event fired from View, populate Model and set properties on View
class Presenter
{
private IModel _myModel...
private IRepository _repository;
public Presenter(IView_MainForm view, IRepository repository)
{
_repository = repository;
this.View = view;
this.View.LoadInput_01 += new EventHandler<InputLoadEventArgs>(OnLoadInput_01);
this.View.LoadInput_02 += new EventHandler<InputLoadEventArgs>(OnLoadInput_02);
}
public void OnLoadInput_01(object sender, InputLoadEventArgs e)
{
// get data based on passed arguments (e.SomeProperty)
// construct IModel
myModel = _repository.GetData(e.SomeProperty);
// pass data to IView_MainForm
View.SetInput01Data(myModel.Input01Data);
}
}
And regarding your concern
I'm striving to keep my View as passive and "dumb" as possible,
assuming it knows nothing of the Presenter (it doesn't subscribe to
its events, the Presenter sends data to the View by calling IView
methods instead), let alone of the Model.
Your View still doesn't know anything about Presenter nor Model. It just fires events, get data from Presenter and binds its controls. And you have testability in place (please note this Unit Test is pseudo code, since I don't know how you retrieve data, what input you required in button click event etc...) .
[Test]
public void ShouldLoadInput01DataOnButtonClick()
{
// Arrange
IModel data = // create dummy data
Mock<IView_MainForm> clientsViewMock = new Mock<IView_MainForm>();
Mock<IRepository> clientsRepositoryMock = new Mock<IRepository>();
clientsRepositoryMock.Setup(repository => repository.GetData(something)).Returns(data.Input01Data);
var presenter = new Presenter(clientsViewMock.Object, clientsRepositoryMock .Object);
// Act
clientsViewMock.Raise(view => view.LoadInput01 += null, new InputLoadEventArgs());
// Assert
clientsViewMock.Verify(view => view.SetInput01Data(data.Input01Data), "Input01 data expected be set on button click.");
}
To those who are MVVM Purist, my question is, Is there a more simplistic, user readable,and unit testable code solution to the problem "How to create message box or dialog box in MVVM design pattern application" then what I come up with here? Disclaimer, I'm not a MVVM Purist and I will add a few lines of code in the View's code-behind if it means more simplistic, user readable and unit testable code. My solution builds upon what awardcoder.blogspot suggested. The first thing on notice in the solution is there is View's code-behind for handling MessageBox. From this point, I realize the fact that adding code in the View's code-behind is already heading down a not MVVM Purist path. Therefore, my solution take full advantage of this single rule breakage without additional rule breaking.
BaseModel.cs
public class BaseModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}
MessageBoxModel.cs
public class MessageBoxModel : BaseModel
{
private string msgboxcontent;
public string MsgBoxContent
{
get
{
return msgboxcontent;
}
set
{
this.msgboxcontent = value;
OnPropertyChanged("MsgBoxContent");
}
}
}
MessageBoxViewModel.cs // Child View-Model
public class MessageBoxViewModel
{
private MessageBoxModel MB;
public MessageBoxViewModel()
{
MB = new MessageBoxModel();
MB.msgboxcontent = "My Message Box Content";
}
public MessageBoxModel MessageBoxModel
{
get
{
return MB;
}
}
MainWindowViewModel.cs // Parent View-Model
public class MainWindowViewModel
{
private MessageBoxViewModel child_MsgBoxviewmodel;
public MainWindowViewModel()
{
child_MsgBoxviewmodel = new MessageBoxViewModel();
}
public MessageBoxViewModel MsgBoxViewModel
{
get
{
return child_MsgBoxviewmodel;
}
}
}
MainWindow.xaml.cs //Parent View
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
private void MessageBoxButton_Clicked(object sender, RoutedEventArgs e)
{
// Instantiate the dialog box
MessageBoxView dlgView = new MessageBoxView();
// Call parent view model to get child view model
MainWindowViewModel mvm = this.DataContext as MainWindowViewModel;
// Configure the dialog box
dlgView.DataContext = mvm.MsgBoxViewModel ;
// Open the dialog/message box
dlgView.ShowDialog();
}
}
MessageBoxView.xaml.cs //Child View
public partial class MessageBoxView : Window
{
public MessageBoxView()
{ //DialogBox
InitializeComponent();
}
}
The xmal files are not shown here because that one of the advantage of using MVVM. View styling is all up to the UI designer.
The message box will appear once someone click on the messageboxbutton.
Unit testing can then be done as usual on the model and viewmodel classes without worrying about popup windows during the test.
JP
Let me say first that I am also not a MVVM Purist but I can already spot one thing that from my point of view I would change in your code.
The button click event you have there should change to a binding in xaml to a ICommand of you MainWindowViewModel.
This will allow you to completely remove that code behind code, in the future you may also apply contracts (interfaces) with this approach to make your solution even more extensible :)
Cheers!
I hope you guys can help me out as I can't find anything useful that helps with the understanding of my problem:
I'm trying to realize a passive MVP approach on my C# WinForms application which has list views and corresponding detail views.
So far I've got the following structure (pseudo code):
ListPresenter(new Repository(), new ListView(), new DetailPresenter(new DetailView());
Implementation:
public class UserDetailPresenter : IPresenter<IUserDetailView>
{
private IDetailView _view;
public UserDetailPresenter(IDetailView detailView)
{
_view = detailView;
}
public void Show(IUser user)
{
InitializeView(user);
_view.Show();
}
}
public class UserListPresenter
{
//private members (_userRepo, _listView, _detailPresenter)
public UserListView(IUserRepository userRepo, IListView listView, IDetailPresenter detailPresenter)
{
//wire up private members..
_listView.EditCommandFired += this.ShowEditForm;
}
private void OnListViewEditCommandFired(object sender, EventArgs args)
{
_detailPresenter.LoadUser(_listView.SelectedUser);
_detailPresenter.Show(); //modal
}
}
public class UserListForm : Form, IUserListView
{
public event EventHandler EditCommandFired;
public IUser SelectedUser { get { return gridView.FocusedRowHandle as IUser; } }
public void LoadUsers(List<IUser> users)
{
gridView.DataSource = users;
}
// other UI stuff
}
My problem is: I can only show the edit form once. As soon as I try to open it for a second time my View (the form) is disposed (System.ObjectDisposedException).
How do I fix that? Do I have the wrong approach here? Do I either cancel the form's close and just hide it and trust the garbage collector to collect it once the DetailPresenter is disposed? Do I create (new() up) a new presenter each time the Edit event is fired? I would then have to introduce some kind of factory as I somehow lose dependency injection. I'd appreaciate if someone could point out how the best practice in this case would look like and what I may be doing wrong here..
I was doing Winforms MVP a while ago so not sure if I can help, but the case my be as follows.
In my approach, the view was owning presenter, pseudo code:
MyForm form = new MyForm(new PresenterX);
form.Show(); //or showdialog
In this case instance is still there after closing.
In your case since presenter owns the view, its possible that once presenter is not used, GC disposes presenter and contained view.
Or even if presenter is still in use, since view is private GC may collect it once closed.
Try to debug in Release mode and see what happens with closed form instance.
EDIT:
Other idea is:
Create instance of view first and then pass to presenter
So approach that may fail (I don' see full code so guessing)
UserDetailPresenter p = new UserDetailPresenter(new YourView());
Try
YourForm view = new YourForm(); //as global variable, view should be reusable anyway
Somewhere in code
UserDetailPresenter p = new UserDetailPresenter(view);
p.Show(userInstance);
You're using one instance of DetailPresenter to show details for different objects. So you'll have to initialize the view of the DetailPresenter each time you want to show it, in your current implementation. This could be one way of doing it, the ListPresenter can inject a new instance of DetailsView everytime it asks the DetailPresenter to show it.
public class UserDetailPresenter : IPresenter<IUserDetailView>
{
private IDetailView _view;
public UserDetailPresenter()
{
}
public void Show(IUser user, IDetailView detailView)
{
_view = detailView;
InitializeView(user);
_view.Show();
}
}
Or another cleaner way could be some sort of ViewFactory to get a new instance of the view before showing it.
private IDetailViewFactory _detailViewFactory;
public UserDetailPresenter(IDetailViewFactory detailViewFactory)
{
_detailViewFactory = detailViewFactory;
}
public void Show(IUser user )
{
_view = _detailViewFactory.Resolve();//Some method to get a new view
InitializeView(user);
_view.Show();
}
But if you want to do it a bit differently, this is more passive view way.
In the ListPresenter:
private void OnListViewEditCommandFired(object sender, EventArgs args)
{
_listView.Show(_listView.SelectedUser);//tells view to show another view
}
In the ListView:
public ListView()
{
new ListPresenter(this); // initializes presenter
}
public void Show(IUser user)
{
new DetailsView(user); // creates a new view
}
In the DetailsView:
public DetailsView(IUser user)
{
new DetailsPresenter(this, user); //creates presenter
}
Finally:
public class UserDetailPresenter : IPresenter<IUserDetailView>
{
private IDetailView _view;
public UserDetailPresenter(IDetailView detailView, IUser user)
{
_view = detailView;
LoadUser(user);
_view.SomeProperty = _userData;//to populate view with data
_view.Show(); // tells the view to show data
}
}