Skip to content

view models

Dan Lorenz edited this page Nov 13, 2023 · 12 revisions

Shield MVVM - View Models

Shield MVVM allows for View Model to View Model navigation by building on top of MAUI's built in navigation system. In order for everything to work, developers need to to use IPageViewModel (or IDialogViewModel to show Dialogs) interface to define all their View Models. However, there are PageViewModelBase and DialogViewModelBase classes that can be used instead in order to not have to implement everything from scratch.

For both Pages and Dialogs, there are three different classes that take different generic parameters depending on how a View Model is being navigated to.

Common View Model Behavior

There is a common base class that all pages and dialogs inherit from named BaseViewModelBase. This, in turn, inherits from ObservableObject from the MVVM Community Toolkit. This gives developers access to helper methods that automatically raise property change events like SetProperty while also implementing the INotifyPropertyChang(ed/ing) interfaces. BaseViewModelBase has a few important behaviors to take advantage of.

InitializeAsync

This method will always run exactly once while navigating (or popping up). The intent is to load as much data as possible here as the user would be seeing some sort of loading mechanism while the page/dialog is doing this longest setup call.

OnViewCreated

This method will also only run exactly once while navigating (or popping up), but it will immediately run as soon as the View Model is tied directly to the page/dialog. There may not be much use for this sort of method, but it exists just in case.

OnViewDestroying

This method will only run once after the page or dialog has been closed. Developers can use this method to tear down anything in the View Model.

RaisePropertyChanged

This optional method simply calls MVVM Community Toolkit's OnPropertyChanged, but developers can override this as needed.

IsBusy

This property is set to true right before InitializeAsync is called. This can be used to show some sort of loading indicator to the user. Developers can also set this to true/false directly when needed as well. When IsBusy changes, the IsBusyChanging event will fire.

Page View Model

Common Page View Model Behavior

All Page View Models inherit from BasePageViewModelBase as Page View Models have some common functionality that Dialogs do not.

OnViewAppearing

This method will run every time the page the View Model is bound to appears on the screen. This method will be called at least once after the page is first shown to the user. Note that showing a popup dialog will not cause this event to fire once it closes.

OnViewDisappearing

This method will run every time the page the View Model is bound to disappears from the screen. Note that showing a popup dialog will NOT cause this event to fire.

IsInitializeCalledBeforePageIsCreated

This property, if true, will ensure the InitializeAsync method is called before the View Model is bound to the page. This allows the View Model to load data ahead of time and, in turn, allow the page to do One time bindings (instead of full bounds) to properties that wouldn't change after their initial sets. However, if a developer wants InitializeAsync to only run after the View Model is bound to the page, then this property needs to be overridden to return false. The main difference determines whether the original page or the new page is showing to the user while the loading indicator is running.

GoBackAsync

When finished with the work on this page/View Model, simply call GoBackAsync to send the user to the previous page.

Basic View Model

If no parameters need to be sent and no result needs to be returned, then a developer should use PageViewModelBase. This class takes no generic parameters.

Parameter View Model

If a parameter needs to be sent in from the previous View Model but no data needs to be returned, then a developer should use PageViewModelBase< TParameter >. This class takes a generic parameter that can be anything. In turn, this parameter will be sent to the Prepare method in order to store this value before InitializeAsync runs.

Prepare

This method is called and sends in the parameter data provided from the previous View Model. It is called before InitializeAsync. Data should NOT be loaded here, just stored.

Result View Model

If a result needs to be sent back to the caller, then a developer should use PageViewModelBase<TParameter, TResult>. Since there are no constraints on these types, TParameter will always need to be specified if a result needs to be sent back. If a TParameter isn't actually required, please use the provided "EmptyArgs" class as the type for TParameter. It can either be newed up or called via a static Create method.

Prepare

This method is called and sends in the parameter data provided from the previous View Model. It is called before InitializeAsync. Data should NOT be loaded here, just stored.

GoBackAsync

This method takes the TResult as a parameter, so its slightly different than the other two ViewModel types.

Dialog View Model

In order to show dialogs, MAUI Community Toolkit is used.

Common Page View Model Behavior

All Dialog View Models inherit from BaseDialogViewModelBase as Dialog View Models have some common functionality that Pages do not.

CloseCommand

A built in command is available to bind directly to in order to close the dialog. This will call the CloseDialog method.

CloseDialog

This method will simply invoke the Close event. The NavigationService is listening for this event in order to call MAUI Community Toolkit's Popup Close method on the developers behalf.

Close Event

Since a dialog is shown as a temporary item, there is no navigate back for it. Instead, there is a Close event that must fire in order for the NavigationService to close the Popup directly.

Basic View Model

If no parameters need to be sent and no result needs to be returned, then a developer should use DialogViewModelBase. This class takes no generic parameters.

Parameter View Model

If a parameter needs to be sent in from the previous View Model but no data needs to be returned, then a developer should use DialogViewModelBase< TParameter >. This class takes a generic parameter that can be anything. In turn, this parameter will be sent to the Prepare method in order to store this value before InitializeAsync runs.

Prepare

This method is called and sends in the parameter data provided from the previous View Model. It is called before InitializeAsync. Data should NOT be loaded here, just stored.

Result View Model

If a result needs to be sent back to the caller, then a developer should use DialogViewModelBase<TParameter, TResult>. Since there are no constraints on these types, TParameter will always need to be specified if a result needs to be sent back. If a TParameter isn't actually required, please use the provided "EmptyArgs" class as the type for TParameter. It can either be newed up or called via a static Create method.

Prepare

This method is called and sends in the parameter data provided from the previous View Model. It is called before InitializeAsync. Data should NOT be loaded here, just stored.

Result

To send the result of the dialog back to the caller, a developer needs to set the Result property with the proper value. If there are different buttons/commands that cause the result to be set differently, then you can manually call CloseDialog after setting the Result property and the result will be returned as expected. However, if the built in CloseCommand is used, then the developer must override the SetResultAsync method.

SetResultAsync

Right before returning any result for the dialog, SetResultAsync will run. The developer has one final chance to properly set the Result property before being fully returned. This method will need to call the Result set property for the data to be returned. This allows flexibility in how the result ultimately gets returned.

Advanced Scenarios

Sometimes, a developer may define a View Model for a specific page that could be re-used, but there are a lot of differences in how the View Model itself is built up or various text changes. Instead of having to create a new XAML page that would be a copy and paste of the original page or a complex parameter arg class, a developer can create a new View Model that inherits the original. Then the various specific methods, properties, etc. can be overridden as needed and other View Models can navigate directly to this one. Shield MVVM will automatically know that this inherited View Model should be tied to the base class's XAML page and will do so automatically. However, the TParameter/TResult class generic types would need to match the base class's type UNLESS a different IViewModel< TParameter > or IViewModel<TParameter, TResult> is implemented on the new View Model. It is highly recommended that this new TParameter inherits from the base TParameter or wraps it so all the Prepare methods can be called correctly as NavigationService will only ever call one Prepare method based on the generic arguments passed in. An example of this is shown in the sample code in the AlternateAboutPageViewModel class.

Clone this wiki locally