Implement MVVM for WinForm Application

I am trying to implement an MVVM template (Model View ViewModel) for my WinForms application. I am using C # 2005.

My application has MainForm (View) with 2 multi-line text fields and 3 buttons. The purpose of the 1st text field is to show a working comment about what the application does when the button is clicked. I keep adding lines to the TextBox to update the user what is happening. The purpose of the second text field is to update the user about any error conditions, conflicts, duplicate values; in short, everything the user needs to view. It classifies each message as INFO, WARNING, or ERROR. Each of the three buttons performs an action and saves the update of 2 text fields.

I created the MainFormViewModel class.

1st question: When a user clicks a button in MainForm, I have to clear the contents of the two text fields and disable the button so that it cannot be pressed again until the first operation is completed. Should I do this updating the text box and button directly in MainForm, or should I somehow use MainFormViewModel?

Second question: Clicking the button calls the method of the MainFormViewModel class. Before calling the method and after calling the method, I want to display a message in the 1st text box, something like "Operation A start / finished". I do this by calling a generic class that has a log method for writing messages to a TextBox or file, or both. Again, is it okay to do this directly from MainForm? I call this logging method at the beginning and end of an event handler.

Third question: How to distribute error messages from ViewModel back to View? I created my own exception class "TbtException". So should I write 2 catch blocks in each and every button, one for TbtException and another for the Exception genetic class?

Thanks.

+6
source share
1 answer

You should only perform operations in the view with respect to the state of the ViewModel. For instance. you should not assume that the view model is computed at the click of a button, but you must add state to the view model, which says that it does something longer, and then recognizes this state in the view. You should not disable or enable buttons in the view, as you wish, but only if you want these buttons to be changed. This can lead to the fact that you have a property that indicates which item in the list is currently selected, so the user interface does not invoke the list item SelectedItem, as well as the viewmodel. And when the user clicks the Delete button, the view model removes the selected item from its list, and the view is automatically updated through state changes in the event form.

Here I would name a view model for your view. It provides messages through an observable collection to which a view can bind (i.e., register event handlers, since binding is not supported in WinForms). The text box at any time displays only the contents of the collection. It has actions to clean up those collections that your opinion may provoke. A view can also trigger actions of the base model, but it should only be updated through the viewmodel! The view should never register any event handlers for events opened by the base model. If you ever want to do this, you must include this event in the view model and open it for viewing. Sometimes this may seem like β€œjust another level of indirection,” so it may be redundant for very simple applications like 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 throw exceptions in the ViewModel than in the view. The view model is better suited to preparing for display. I do not know how this works in WPF. I have not programmed the application in WPF yet, we are still doing a lot of WinForms.

Opinions may vary, but I think that general try / catch statements are not exception handling. I think you better check your user interface and enable exception handling only when necessary. That's why you unit test the model of your view and the user checks the view. However, if you really adhere to the principle and avoid logic in the view, you can do a lot with unit tests.

+3
source

Source: https://habr.com/ru/post/907717/


All Articles