Based on the ideas of Sacha Barber: Attached VM Behaviours
The main idea is getting rid of those big viewmodel code files
- separation of viewmodel properties/commands and the viewmodel logic: logic is separated into viewmodel behaviors
- a viewmodel can (and should) have many viewmodel behaviors
- scales very well with number of properties/commands
- separation of logic into smaller units makes unit testing easier
- smaller units increases code maintainability
This project incorporates VM behaviors with PRISM, DI (Unity) and RX - and with ease of use.
####Technical details
- every viewmodel get its own container (child container of main container)
- viewmodel and behaviors are resolved by DI: use constructor for own DI purposes
- IDisposable is implemented such that dispose of viewmodel disposes the container and behaviors
####Example code viewmodel: Don't worry about the constructor - it's due to DI and is handled automagically.
[UsedImplicitly]
public class MainPageViewModel : ViewModel<MainPageViewModel>
{
public MainPageViewModel(Func<MainPageViewModel, ViewModelBehaviorsController<MainPageViewModel>> controllerFactory)
: base(controllerFactory)
{
ShowTextCommand = Text.Select(str => !string.IsNullOrWhiteSpace(str)).ToReactiveCommand();
// determistic start of behaviors
BehaviorsController.Start();
}
public ReactiveProperty<string> Text { get; } = new ReactiveProperty<string>();
public ReactiveCommand ShowTextCommand { get; }
}
####Example code viewmodel behavior:
Show a message box with text from Text
propery when ShowTextCommand is invoked.
[UsedImplicitly]
public class ShowTextBehavior : ViewModelBehavior<MainPageViewModel>
{
protected override void OnStart()
{
// subscribe to command - add subscription to dispose list
RegisterDisposable(ViewModel.ShowTextCommand.Subscribe(_ => ShowMessageDialog()));
}
private async void ShowMessageDialog()
{
// ViewModel: property available on a behavior
var dialog = new MessageDialog(ViewModel.Text.Value, "The message is");
await dialog.ShowAsync();
}
}
####Example code: register viewmodel and behavior with DI container (App.xaml.cs)
protected override void ConfigureContainer()
{
base.ConfigureContainer(); // must be called to initialize PRISM
/* ### container configurator:
* on request of page (PRISM)
* -> create viewmodel container / child container
* -> run the container configurator
* -> resolve viewmodel, viewmodel behaviors and all other registered dependencies
* -> viewmodel returned to PRISM
*/
RegisterViewModelContainerConfigurator<MainPageViewModel>(c =>
{
c.RegisterViewModelBehavior<ShowTextBehavior>(); // generics guarantee only behaviors for MainPageViewModel can be registered
// c.ViewModelContainer: reference to the viewmodel container - do custom registrations here
});
}
####Links
- PRISM
- Unity (dependency injection)
- Reactive eXtensions (RX)
- ReactiveProperty: aids use of RX in MVVM pattern
####Notes Code is annotated with attributes for ReSharper - JetBrains.Annotations