MVVM - STARIONGROUP/COMET-IME-Community-Edition GitHub Wiki
Introduction
CDP4-COMET implements it's own MVVM framework which is built on top of the popular ReactiveUI framework. The Managed Extensibility Framewok (MEF) is for Dependecny Injection. The COMET Message Bus is used to notify the framework of changes, primarily changes on model classes.
In order to work with the CDP4-COMET MVVM framework it is neccesary to have a basic understanding of the MVVM design pattern, MEF and the ReactiveUI framework. Readers are invited to perform their own research on these topics.
The CDP4-COMET MVVM framework is based on the view-model first approach. In this approach, the View-Model is unaware of the View and the View is not responsible for creating or binding a View-Model to its DataContext. A View is constructed by the Navigation Service and the DataContext is set to the View-Model.
ViewModel Base Classes
A view-model is used to represent a Thing from the CDP4-COMET Data Model. View-Model classes in the CDP4-COMET MVVM framework need to derive from one the view-model base classes.
ViewModelBase
The ViewModelBase is the base class for all view-model classes that represent a Thing. It derives from the ReactiveObject and implements the IViewModelBase interface.
The arguments of the view-model constructor are the Thing
that is represented and the ISession
the Thing
belongs to. Each change to the thing, that originates from processing changes on a data-source and is sent via the CDP4-COMET Message Bus, is processed by the ObjectChangeEventHandler
method that updates the RevisionNumber
property. The ObjectChangeEventHandler
method is virtual and can hence be overriden by derived classes. These overrides are used to update the properties of these derived classes. The IsBusy
boolean property can be set to true or false and is used in the GUI to display an hour-glass or so-called loading
dialog.
DialogViewModelBase
The DialogViewModelBase class derives from ViewModelBase and is used to represent a dialog that provides the user with a means to Create
, Edit
, or Inspect
a Thing
instance. A Thing
cannot be updated, only a clone or copy of a Thing
can be updated. Dialog view-models can be stacked, allowing a user to perform CRUD on multiple objects in one transaction. Commands, in the form of ReactiveCommands are used to bind to the buttons on the corresponding View.
BrowserViewModelBase
The BrowserViewModelBase class derives from ViewModelBase and is used to represent a browser that displays a list or treeview of Things
that are contained by the Thing
the view-model represents. From this view-model existing Things
can be selected and then be inspected, edited or deleted. Browsers usually also support adding new Things
. Context menus are loaded and displayed to the user based on right-mouse-click interactions.
RowViewModelBase
The RowViewModelBase class derives from ViewModelBase and is used to represent a row in a list or treeview. Rows can be highlighted using the HighlightEvent class. A row view-model can support in-line editing as well as navigation to a dialog. Drag-n-Drop operations can be started from a row view-model.
DisposableReactiveList<T>
The DisposableReactiveList inherits ReactiveList.
This subclass should be used when the generic type implements the IDisposable
interface.
The purpose of the class is to force the developer to consider disposal of the items in the ReactiveList<T>
.
For example the RequirementsBrowser:
This is a treeview control that is built up via MVVM, according to properties in RequirementBrowserViewModel
and it's child ViewModels.
Property RequirementBrowserViewModel.ReqSpecificationRows
contains items of type RequirementsSpecificationRowViewModel
which is implements IDisposable
.
During the lifetime of the RequirementsBrowser view, instances of RequirementsSpecificationRowViewModel
can be added and removed from the RequirementBrowserViewModel.ReqSpecificationRows
property to reflect changes in the model, or to filter the UI.
When an instance of RequirementsSpecificationRowViewModel
is removed from the RequirementBrowserViewModel.ReqSpecificationRows
property, the instance of RequirementsSpecificationRowViewModel
could somewhere in the code base be used in other properties/objects, for example in a Reactive Subscription.
This way the instance has an active reference and therefore it will not be finalized/garbage collected.
If the Reactive Subscription is based on a CDPMessageBus
event, the Reactive Subscription will execute code when the event CDPMessageBus
event is thrown.
This might not be what you want and as a developer you should be aware of the choice you have here and have the tools to handle this decision in a understandable/readable way.
This is where DisposableReactiveList<T>
comes in.
This class overrides all default methods from ReactiveList<T>
that are about removal of items and makes the obsolete, in a way that the code does not compile when you erroneously use it, or throws a NotSupportedException
when it is being called (by Reflection).
Every overridden method is replaced by a [OriginalMethodName]AndDispose and a [OriginalMethodName]WithoutDispose method, which is well understandable/readable in de source code.
So please use a DisposableReactiveList<T>
for every generic type that implements IDisposable, even if it is not necessary (yet).
Forgetting to Dispose an instance could lead to Out-Of-Memory exceptions.