ViewModel Management

  • 4 minutes to read

This article describes how to retrieve ViewModel instances at runtime. Note that if you utilize the MvvmContext component to build your MVVM-applications at design-time, the component manages ViewModels automatically.

If a ViewModel follows the POCO concept, the MVVM Framework dynamically transforms this ViewModel to a new class containing the necessary infrastructure (to support, for instance, simplified data bindings). The Framework works with dynamically created class instances, which means you cannot initially access these instances at runtime because their types have not been determined.

Use the following options to retrieve a working ViewMode:

The ViewModelSource.Create method

In this approach, you create a ViewModel instance first, then call the SetViewModel method to associate this instance with the specific ViewModel type.


var mainViewModel = ViewModelSource.Create<MainViewModel>();
mvvmContext1.SetViewModel(typeof(MainViewModel), mainViewModel);

The ViewModelBase class

You can inherit ViewModels from the ViewModelBase class that implements MVVM Framework features. In this case, you can create ViewModel instances directly. Note that you also need to call the SetViewModel method to specify that the Framework should use this instance when the ViewModel is required.


public class ViewModel : ViewModelBase {
    //. . .
}
var myViewModel = new ViewModel();
mvvmContext1.SetViewModel(typeof(ViewModel), myViewModel);

We do not recommend this approach because you lose all the features POCO models provide.

ViewModelCreate events

This approach is designed to work with dependency injection frameworks (such as Ninject). The example below illustrates how this injection works with the Ninject framework.


public class SamuraiViewModel {
    public IWeapon Weapon { get; private set; }
    public SamuraiViewModel(IWeapon weapon) {
        this.Weapon = weapon;
    }
    public void Attack() {
        Weapon.Hit();
    }
}

// Bind a dependency for IWeapon
kernel.Bind<IWeapon>().To<Sword>();

// Set up MVVMContext
var fluent = mvvmContext1.OfType<SamuraiViewModel>();
fluent.BindCommand(simpleButton1, x => x.Attack());

In this scenario, you need dynamically generated ViewModels and bind them to interfaces (MVVM framework features are lost when binding interfaces to POCO directly). To obtain the required instances, handle the regular (local) or static (global) ViewModelCreate event as follows:


// Retrieve the live POCO ViewModel instance with the Ninject kernel
//regular event
mvvmContext1.ViewModelCreate += MVVMContext_ViewModelCreate;
void MVVMContext_ViewModelCreate(object sender, DevExpress.Utils.MVVM.ViewModelCreateEventArgs e) {
    // kernel.Bind<SamuraiViewModel>().To(e.RuntimeViewModelType);
    // e.ViewModel = kernel.Get<SamuraiViewModel>();
    e.ViewModel = kernel.Get(e.RuntimeViewModelType);
}
//static event
MVVMContextCompositionRoot.ViewModelCreate += (s,e)=> {
    e.ViewModel = kernel.Get(e.RuntimeViewModelType);
};

The static ViewModelCreate event is a weak event. If it contains a closure to a variable ("kernel" in the example above or the "rootContainer" scope in the Autofac sample below) whose lifetime is shorter than the parent container's lifetime, the VS Garbage Collector can prematurely collect this variable and the event handler may never be invoked.


[STAThread]
static void Main() {
    //rootContainer is declared at the Main method level
    IContainer rootContainer;
    var builder = new ContainerBuilder();
    builder.RegisterType<TestViewModel>();
    builder.RegisterType<MyClass>().As<IService>();
    rootContainer = builder.Build();
    MVVMContextCompositionRoot.ViewModelCreate += (s, e) => {
        using (var scope = rootContainer.BeginLifetimeScope(
            b => b.RegisterType(e.RuntimeViewModelType).As(e.ViewModelType))) {
            e.ViewModel = scope.Resolve(e.ViewModelType);
        }
    };
}

To fix the potential issue, declare variables as properties/fields at the level of the object from which a subscription is performed.


//rootContainer is declared at the root level
private static IContainer rootContainer { get; set; }
    [STAThread]
    static void Main()
    {  
        var builder = new ContainerBuilder();
        builder.RegisterType<TestViewModel>();
        builder.RegisterType<MyClass>().As<IService>();
        rootContainer = builder.Build();
        MVVMContextCompositionRoot.ViewModelCreate += (s, e) =>
        {
            using (var scope = rootContainer.BeginLifetimeScope(
                b => b.RegisterType(e.RuntimeViewModelType).As(e.ViewModelType))) {
                    e.ViewModel = scope.Resolve(e.ViewModelType);
                }
        };
}