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.
source share