I'm trying to refactor a WindowsForm to the MVP Pattern. The app is a tile editor. The form has a custom control where i display the tilemap (TileDisplay). After loading a map from a file i call a method named "AdjustHScrollBar" to readjust the horizontal scrollbar to the tilemap-size. I'm not 100% sure how to split the method according to MVP.
First the original none MVP method:
private void AdjustHScrollBar()
{
if (tileMap.GetWidthInPixels() > tileDisplay.Width)
{
hTileScrollBar.Visible = true;
hTileScrollBar.Minimum = 0;
hTileScrollBar.Maximum = tileMap.GetWidth();
}
else
{
hTileScrollBar.Visible = false;
}
}
This is the method after using MVP in the presenter:
private void AdjustHScrollBar()
{
if (mainFormModel.TileMap.GetWidthInPixels() > mainFormView.GetTileDisplayWidth())
{
mainFormView.EnableHScrollBar(mainFormModel.TileMap.GetWidth());
}
else
{
mainFormView.DisableHScrollBar();
}
}
The view doesn't know the presenter. My question is if the presenter should know the controls of the view. In my implementation it doesn't - that's the reason for the GetTileDisplayWidth, EnableHScrollBar and DisableHScrollBar - methods. Afaik that's the right way, but this seems to become a lot of work if i have to make a method for every property i need from the view. I have basicly the "same" code for the vertical scroll bar for e.g. (so that's 6 methods for readjusting the scroll bars).
The point of the Presenter layer is to be able to communicate with the View layer without knowing the specifics of the view, i.e. anything to do with size, color,what type of view it is or any other properties.
Usually the presenter class will take the view object in it's constructor. Ideally you would have the Adjust method on the view and the presenter would call it directly, even more ideally you would do this through an interface rather than direct view class.
In your code you are accessing all of the view's properties and then trying to manipulate them through the presenter, you don't necessarily have to do that unless you have some sort of dependency on business logic. So you can do the whole operation on the View layer and then call it from the Presenterlayer.
MVP involves a lot of code and that is the trade-off that you have to accept.
I would do something like this in the presenter:
public interface ITileMapView
{
event EventHandler<string> TileMapFileLoaded;
void OnTileMapLoaded(TileMapModel model);
}
public class TileMapPresenter
{
private readonly ITileMapView view;
public TileMapPresenter(ITileMapView view)
{
this.view = view;
view.TileMapFileLoaded += OnTileMapFileLoaded;
}
private void OnTileMapFileLoaded(object sender, string filename)
{
// Parse data from file
// Populate model
// Tell view
view.OnTileMapLoaded(model); //Implement the 'AdjustHScrollBar' logic in the view
}
}
The Presenter knows when and how to get data, and how to interpret the data. You should not let the Presenter engage in any view specific logic.
Related
I'm implementing a mini program in C# using the MVC Architectural Pattern. The goal of this program is to update the value of a mouse click counter (which is in the Model), by clicking a button (which is in the View) through a Controller that must handle the Button Click Event.
The code I've written so far works (it compiles without errors), but if fails to handle the button click event because I can't figure out what kind of code I must put into the View and what into the Controller. The only solution I tried and found working is to give the View a reference to its Controller. In this way, the Event Handler is registered and implemented in the View and it invokes a method of the Controller (e.g. Controller.DoSomethingOnButtonClick()). But this solution breaks the MVC Pattern, because the View, as far as I understood, should NOT be aware of its Controller.
The Model (implements the Observer Pattern, it is the "observable"):
class Model : Subject
{
private int counter = 0;
public void IncreaseCounterByOne()
{
counter++;
Notify(); // notify the observers
}
public int GetCounter()
{
return counter;
}
}
The View (implements the Observer Pattern, it is the "observer"):
class View : IObserver
{
private Model Model;
private Form MyForm = new Form();
private Label MyLabel = new Label();
private Button MyButton = new Button();
public View(Model model)
{
this.Model = model;
this.Model.Attach(this);
}
public void CreateView()
{
// create and display the view (MyForm, MyLabel, MyButton)
}
public void Update(Subject subject)
{
UpdateLabel();
}
private void UpdateLabel()
{
MyLabel.Text = "Click Counter: " + Model.GetCounter();
}
}
The Controller:
class Controller
{
private Model Model;
private View View;
public Controller(Model model, View view)
{
this.Model = model;
this.View = view;
this.View.CreateView();
}
private void UpdateCounter()
{
this.Model.IncreaseCounterByOne();
}
}
What I want to achieve is that the Controller catch the Button Click Event generated by MyButton and handle it in its Event Handler, which I assume to be something like:
public void OnButtonClicked(object sender, EventArgs e)
{
UpdateCounter();
}
How can I accomplish this without using a reference to the Controller? Is it possible?
PS. I already read a lot of similar questions, but not found the solution I'm looking for.
You're running into a problem where trivial examples for patterns like MVC fall down: the UI often takes on both roles of V and C - it shows the data and offers a way to manipulate it. If your example were more involved (eg a web service is the controller)the concerns would be easier to see separated
The separation is still there logically, but you can't easily put the button in one class and the label that shows the counter in another as a method of separation, when you're essentially trying to combine it al in the same UI - but you shouldn't worry about this
Accept that your UI contains your view and also contains your controller so it's the UI class(es) job to unify all the things. Consider that your label is actually the view, not your class that happens to be called View. The concept appreciation you need to acquire is that you can have 3 different ways of changing the model (a timer, a button, a tcp socket that you send some data to) via the controller and 3 different ways of visualizing it (a label showing a number, a progress bar, a call to a website page that returns a string of 'A' as long as the current counter) and you've separated your concerns - the label/progbar/website are independent of the timer/button/website and they have no knowledge of each other, they don't interact, you don't need a reference to one passing into the other in order for everything to function. You can remove the button and the socket and the timer will carry on causing model manipulations that the label/progbar will carry on showing
I have a WPF application which is written with an implementation of MVVM. There's no extra framework for the MVVM pattern.
My entities from EF db first are wrapped in their own viewmodels and I have a modelcontroller to load them into their viewmodels from a 'window' viewmodel.
Example of an entity viewmodel:
public class PurchaseOrderViewModel : ViewModels.ViewModelBase
{
private someType _prop;
public someType Prop
{
get
{
return _prop;
}
set
{
_prop = value;
OnPropertyChanged();
}
}
// ...
// Other Properties
// ...
public PurchaseOrderViewModel() {
// default constructor for LINQ
}
public PurchaseOrderViewModel(purchaseorder entity)
{
// load values from entity in properties
}
}
Example of a window viewmodel:
public class MainViewModel: ViewModels.ViewModelBase
{
private IModelController modelController = new ModelController();
private List<PurchaseOrderViewModel> _poList;
public List<PurchaseOrderViewModel> POList
{
get
{
return _poList;
}
set
{
_poList = value;
OnPropertyChanged();
}
}
// ...
// Other Properties
// ...
public MainViewModel()
{
POList = modelController.GetPurchaseOrders();
}
}
Example of ModelController:
public class ModelController : IModelController
{
public List<PurchaseOrderViewModel> GetPurchaseOrders()
{
using (var model = new DBContext())
{
return model.purchaseorders
.Select(new PurchaseOrderViewModel { /* assign properties */ })
.ToList();
}
}
}
Where am I supposed to save this wrapped viewmodel (PurchaseOrderViewModel) once the user is done editing it? As I see it, there are 2 options:
Create a save function in each viewmodel that points back to the modelController, but this feels like an inappropriate approach.
Create a save function in the modelcontroller and pass the viewmodel as an argument
It's most likely that I'm missing something in the MVVM pattern, but please point me in the right direction. Thank you!
EDIT: I excluded the view (MainView) from the info provided, but this view binds directly to the properties exposed by MainViewModel.
First up, I problably wouldn't name it ModelController as that's slightly confusing makes people think you are speaking MVC. Instead, if you call it xxxxService (e.g. PurchaseOrdersService) it makes more sense and it no longer feels "inappropriate" because having a VM delegate the actual work is what many users of IoC do. Plus it keeps your VM clean.
NOTE: By "service" I don't necessarily mean that your VM will be calling a WCF service directly (nor should you). Service is just a means to achieve something in an abstract and encapsulated way on behalf of clients. Examples include:
saving information to a DB
getting the current log mechanism
They can even be facades whereby they create a WCF client proxy and call a remote service on your behalf without you having to know the details.
So a typical flow is:
Command >> View code behind >> VM >> Service
The reason I include the view's code behind is that typically this is where you:
Catch exceptions
The starting point of async/await for asynchonous calls to your VM and service
Now when you pass context fromt the VM back to the service, there is no rule on what exactly you pass however I see no reason to pass VM to the service because that contains information the service doesn't care about.
Just pass the M which your VM should have bound to in the first place and continued to update via binding.
I have a view in wpf, that has a range of different boxes, for example, First/Last Name (TextBox), Date of Birth (DatePickers), Marital Status (ComboBox) etc.
What I want to be able to do, is get the text entered into the TextBoxes and show them in a TextBlock on a seperate view.
I have added properties for all the corresponding items in there retrospective ViewModels, but from there on in, I'm unsure on how to implement this any further.
Other questions I have looked at aren't very clear or easy to follow.
You simply need to set the DataContext of both views to the same instance of your ViewModel.
<StackPanel>
<Local.EditableView DataContext={Binding Person} />
<Local.ReadOnlyView DataContext={Binding Person} />
</StackPanel>
There are multiple ways to achieve this. I assuming you are not using any framework like Caliburn.Micro.
Simple Approach:
Create a global static class that shares information across multiple ViewModel.
Now, from the first ViewModel, update the static class property using the ViewModel property setter, something like
private string _lastName;
public string LastName{
get{
return _lastName
}
set{
_lastName = value;
SharedClass.LastName = value;
}
}
Now access this shared class from the other ViewModel.
One approach is to use a Mediator to communicate between view models.
You would typically register a "target" view model -- "colleague" -- with the mediator for certain operations that the view model is interested in and provide a callback action for what is supposed to happen when that operation occurs. Then the other view model -- the one performing the operation that the target is interested in -- would notify the mediator when the operation happens, and the mediator would then perform the associated action on all the colleagues that are registered for that operation.
Here's an example of a mediator:
static class Mediator
{
private static Dictionary<string, List<Action<Object>>> _tokenCallbacks
= new Dictionary<string, List<Action<object>>>();
internal static void Register(string token, Action<Object> callback)
{
token = token.ToLower();
if (_tokenCallbacks.ContainsKey(token))
{
var l = _tokenCallbacks[token];
var found = false;
foreach (var existingCallback in l)
{
if (existingCallback.Equals(callback))
{
found = true;
break;
}
}
if (!found) l.Add(callback);
}
else
{
var l = new List<Action<Object>>(new[] { callback });
_tokenCallbacks.Add(token, l);
}
}
internal static void NotifyColleagues(string callbackToken, Object args)
{
callbackToken = callbackToken.ToLower();
if (_tokenCallbacks.ContainsKey(callbackToken))
_tokenCallbacks[callbackToken].ForEach((x) => x(args));
}
}
Those views and their view models should reference a shared model of the data.
So that when data is entered in one view, its view model updates the model and the model update triggers a update in the other view model and finally in the other view.
If you have lots of cross viewmodel communication, use Messenger. That acts as a mediator and simplified lots of issues like this. You can either implement one yourself or use either the MVVM light or Prism toolkits.
For my project I need to know which View is using my ViewModel
So i created this ViewModel:
public class HistoriqueViewModel : INotifyPropertyChanged
{
public HistoriqueViewModel(MetroWindow view)
{
this.MetroWindow = view;
this.ExportCommand = new RelayCommand(Export_Ex);
}
private MetroWindow _metroWindow;
public MetroWindow MetroWindow
{
get { return _metroWindow; }
set
{
if (Equals(value, _metroWindow)) return;
_metroWindow = value;
OnPropertyChanged();
}
}
//.........
}
And in the View constructor:
public partial class ViewHisto : MetroWindow
{
public ViewHisto()
{
InitializeComponent();
DataContext=new HistoriqueMV(this) ;
}
}
It Work perfectly for me but I want to know if this Break the MVVM Pattern?
Yes, this breaks MVVM. A properly constructed view model shouldn't care about what the view is.
Nothing in your code really suggests why you are passing that reference (other than exposing the view as a public property, which is an even bigger no-no) but there are several ways around it:
Pass the view as an interface and hold/expose that
Use a mediator to pass whatever messages necessary between the view model/view
Have the view invoke whatever methods it needs on the view model, and have the view model raise events that the view can listen to.
Any of the above approaches will provide far better decoupling than the one you are going with.
One other thing, its "View Model", not "Model View"
Question on the MVVM pattern where I think I have it wrong.
When a touchdown event occurs in a view I want to popup a message i.e.:
private void marker_TouchDown(MessageObject msgData)
{
CustomMessageControl message = new CustomMessageControl() {Width = 610, Height = 332};
CustomMessageViewModel messageVM = new CustomMessageViewModel(msgData);
message.DataContext = messageVM;
//Add to canvas
}
My viewmodel:
public class CustomMessageViewModel
{
public MessageObject message { get; set; }
public CustomMessageViewModel(MessageObject message)
{
this.MessageObject = message;
}
}
This works but doesn't feel right. Is this an acceptable way to populate the view model?
I believe that you are violating MVVM in creating the control in the view model. This isn't testable, your view model has to create the control now and that shouldn't be a requirement for testing (this emphasizes the lack of the separation of concerns between the UI and the view model).
Instead of creating the control, it is completely acceptable for your view model to fire an event of it's own. In this event, you'd pass the view model that you want the dialog/overlay/control to bind to, something like this:
public class CustomMessageControlEventArgs : EventArgs
{
public CustomMessageViewModel CustomMessageViewModel { get; set; }
}
public event EventHandler<CustomMessageControlEventArgs>
ShowCustomMessageControl;
private void marker_TouchDown(MessageObject msgData)
{
// Create the view model.
var message = ...;
// Get the events.
var events = ShowCustomMessageControl;
// Fire.
if (events != null) events(this,
new CustomMessageControlEventArgs {
MessageObject = new CustomMessageViewModel(msgData)
});
}
Then, in your UI code, you would bind to the event and then show the appropriate user interface for that event.
Remember, MVVM isn't strictly about being able to declare everything in XAML or binding data to the UI through just data bindings, it's about proper separation of code.
You want to separate the what of what is displayed (the view model) from the how of what is displayed (the UI); in firing an event, you're maintaining that separation of concerns.
Yes, you'll have to write some code behind (or you could do it through property notification changes, but it's uglier, frankly), but it maintains the separation and allows for easy testability without having to bring in any user interface elements.