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.
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 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.
I am building a WPF application with the aid of MVVM light and Unity.
I have a ribbon control in the main form, with one tab without tab category, and two tabs in a tab category. User control 3 includes user control 2 and user control 1.
I would like to make visible the Tab Category and tabs 2 and 3 when I click in user control 2 and make them invisible when click in user control 3 or 1.
I confused a little bit with the event handling in mvvm structure.
I would like to do this properly with the MVVM rules. Could you give me an example or some really helpful directions how to do it?
Check how it looks like
Your Ribbon or tabs for that matter, should be backed up by a ViewModel, let's say TabCategoryViewModel or Tab2ViewModel and Tab3ViewModel. Inside these ViewModels you inject the IMessanger service (of course register it before if not already done) and create a POCO event message, like SelectedViewMessage
public class SelectedViewMessage
{
public string ViewName { get; set; }
}
Inside your TabCategoryViewModel you would register to listen to this message
public class TabCategoryViewModel : ViewModelBase
{
public readonly IMessanger messageService;
public TabCategoryViewModel(IMessanger messageService)
{
if(messageService == null)
{
throw ArgumentNullException("messageService");
}
this.messageService = messageService;
this.messageService.Register<GoToPageMessage>(this, OnSelectedViewChanged);
}
protected void OnSelectedViewChanged(SelectedViewMessage message)
{
this.IsVisible = message.ViewName == "UserControl2";
}
private bool isVisible;
public bool IsVisible
{
get { return isVisible; }
set
{
if(isVisible != value)
{
isVisible = value;
RaisePropertyChanged();
}
}
}
}
You inject the same IMessanger service into your ViewModel you use to Bind your UserControl2 and fire the message via
var message = new SelectedViewMessage {
ViewName = "UserControl2";
};
this.messangerService.Send<SelectedViewMessage>(message);
This code can be placed inside a ViewSelectedCommand or something similar, and you can use Blend Interactivity Triggers/Actions to bind this to certain events on the View/UserControl
This can be achieved by adding event onPropertyChange whenever user clicks on the tab and add that property in xaml under Visibility tag. Also look at Handling UI Control to understand that mapping in xaml and ti understand event handling from Here
Hope it helps.
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.
I am trying to implement MVVM (Model View ViewModel) pattern for my WinForms application. I am using C# 2005.
My application has a MainForm (View) with 2 multi line textboxes and 3 buttons. The purpose of the 1st textbox is to show a running commentary of what the application is doing, when the button is clicked. I keep on appending lines to the TextBox to update the user what is happening. The purpose of the 2nd textbox is to update the user about any error condition, conflicts, duplicate values; in short, anything which is required by the user to review. It classifies each message as either an INFO or a WARNING or an ERROR. Each of the 3 buttons perform an action, and keeps updating the 2 textboxes.
I have created a MainFormViewModel class.
1st question:
When the user clicks on the button in MainForm, I have to clear the contents of the 2 textboxes, and disable the button so that it cant be clicked again until 1st operation is completed. Should I do this textbox and button updation directly in the MainForm or I should use MainFormViewModel in some way?
2nd question:
The button click calls a method on the MainFormViewModel class. Before calling the method and after calling the method, I want to show a message in the 1st textbox something like "Operation A started / ended". I do this by calling a Common class which has a Log method to log messages to a TextBox or a file or both. Again whether it is ok to do this directly from the MainForm? I call this logging method at the start and end of the event handler.
3rd question:
How do I propagate error messages from ViewModel back to View? I have created a custom Exception class "TbtException". So do I have to write 2 catch blocks in each and every button, one for TbtException and other for genetic Exception class?
Thanks.
You should perform operations in the view only with regard to the state of the ViewModel object. E.g. you shouldn't assume the view model is calculating when you click a button, but you should add a state to the view model that says it's doing something longer and then recognize that state in the view. You shouldn't disable or enable buttons in the view as you please, but only if there's a state that demands these buttons to be changed. This can go as far as to have a property that indicates which item in a list is currently selected, so the UI doesn't call the list control's SelectedItem member, but the viewmodel's. And when the user clicks remove, then the view model will remove the selected member from its list and the view is automatically updated through state changes in the form of events.
Here's what I would call a view model for your view. It exposes messages through an observable collection to which the view can bind (ie. register event handlers, since binding is not well supported in WinForms). The textbox at any time renders only the contents of the collection. It has actions for clearing those collections which your view can call. The view can also call actions of the underlying model, but it should be updated through the viewmodel only! The view should never register any event handlers for events exposed by the underlying model. If you ever want to do that you should hook up that event in the view model and expose it to the view there. Sometimes that may feel like "just another level of indirection" which is why it may be overkill for very simple applications such as yours.
public class MainFormViewModel : INotifyPropertyChanged {
private object syncObject = new object();
private MainFormModel model;
public virtual MainFormModel Model {
get { return model; }
set {
bool changed = (model != value);
if (changed && model != null) DeregisterModelEvents();
model = value;
if (changed) {
OnPropertyChanged("Model");
if (model != null) RegisterModelEvents();
}
}
}
private bool isCalculating;
public bool IsCalculating {
get { return isCalculating; }
protected set {
bool changed = (isCalculating != value);
isCalculating = value;
if (changed) OnPropertyChanged("IsCalculating");
}
}
public ObservableCollection<string> Messages { get; private set; }
public ObservableCollection<Exception> Exceptions { get; private set; }
protected MainFormViewModel() {
this.Messages = new ObservableCollection<string>();
this.Exceptions = new ObservableCollection<string>();
}
public MainFormViewModel(MainFormModel model)
: this() {
Model = model;
}
protected virtual void RegisterModelEvents() {
Model.NewMessage += new EventHandler<SomeEventArg>(Model_NewMessage);
Model.ExceptionThrown += new EventHandler<OtherEventArg>(Model_ExceptionThrown);
}
protected virtual void DeregisterModelEvents() {
Model.NewMessage -= new EventHandler<SomeEventArg>(Model_NewMessage);
Model.ExceptionThrown -= new EventHandler<OtherEventArg>(Model_ExceptionThrown);
}
protected virtual void Model_NewMessage(object sender, SomeEventArg e) {
Messages.Add(e.Message);
}
protected virtual void Model_ExceptionThrown(object sender, OtherEventArg e) {
Exceptions.Add(e.Exception);
}
public virtual void ClearMessages() {
lock (syncObject) {
IsCalculating = true;
try {
Messages.Clear();
} finally { IsCalculating = false; }
}
}
public virtual void ClearExceptions() {
lock (syncObject) {
IsCalculating = true;
try {
Exceptions.Clear();
} finally { IsCalculating = false; }
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropetyChanged(string property) {
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(property));
}
}
EDIT: Regarding exception handling
I would rather catch exceptions in the ViewModel than in the view. The view model is better suited to prepare them for display. I don't know how that works in WPF though. I've yet to program an application in WPF, we're doing a lot of WinForms still.
Opinions may vary, but I think generic try/catch clauses aren't really exception handling. I think you should rather test your UI very well and include exception handling only when necessary. Which is why you unit test your view model and user test the view. However if you really stick to the principle and avoid logic in the view, you can do a lot with unit tests.