视图模型管理
本文描述了如何在运行时检索ViewModel实例,注意如果您在设计时使用MvvmContext组件来构建 MVVM-applications,该组件将自动管理ViewModels。
如果ViewModel遵循POCO概念,MVVM框架将动态地把该ViewModel转换为包含必要基础结构(例如,支持简化的)的新类。框架与动态创建的类实例一起工作,这意味着您无法在运行时初始访问这些实例,因为它们的类型尚未确定。
使用以下选项来检索工作的ViewMode:
- The ViewModelSource.Create method
在这种方法中,您首先创建一个ViewModel实例,然后调用SetViewModel方法将该实例与特定的ViewModel类型关联起来。
C#:
var mainViewModel = ViewModelSource.Create<MainViewModel>(); mvvmContext1.SetViewModel(typeof(MainViewModel), mainViewModel);
VB.NET:
Dim mainViewModel = ViewModelSource.Create(Of MainViewModel)() mvvmContext1.SetViewModel(GetType(MainViewModel), mainViewModel)
- The ViewModelBase class
您可以从实现MVVM框架特性的ViewModelBase类中继承ViewModels,在这种情况下,是可以直接创建ViewModel实例。注意,此时还需调用SetViewModel方法来指定框架在需要ViewModel时应该使用这个实例。
C#:
public class ViewModel : ViewModelBase { //. . . } var myViewModel = new ViewModel(); mvvmContext1.SetViewModel(typeof(ViewModel), myViewModel);
VB.NET:
Public Class ViewModel Inherits ViewModelBase '. . . End Class Private myViewModel = New ViewModel() mvvmContext1.SetViewModel(GetType(ViewModel), myViewModel)
我们不推荐这种方法,因为您将失去POCO模型提供的所有特性。
- ViewModelCreate events
这种方法被设计为与依赖注入框架(如)一起工作,下面的例子说明了这种注入是如何与Ninject 框架一起工作的。
C#:
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());
VB.NET:
Public Class SamuraiViewModel Private privateWeapon As IWeapon Public Property Weapon() As IWeapon Get Return privateWeapon End Get Private Set(ByVal value As IWeapon) privateWeapon = value End Set End Property Public Sub New(ByVal weapon As IWeapon) Me.Weapon = weapon End Sub Public Sub Attack() Weapon.Hit() End Sub End Class ' Bind a dependency for IWeapon kernel.Bind(Of IWeapon)().To(Of Sword)() ' Set up MVVMContext Dim fluent = mvvmContext1.OfType(Of SamuraiViewModel)() fluent.BindCommand(simpleButton1, Function(x) x.Attack())
在这种情况下,您需要动态生成Viewmodel并将它们绑定到接口(当将接口直接绑定到POCO时,MVVM框架特性将丢失)。要获得所需的实例,处理常规(本地)或静态(全局)ViewModelCreate事件如下:
C#:
// 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); };
VB.NET:
' Retrieve the live POCO ViewModel instance with the Ninject kernel 'regular event AddHandler mvvmContext1.ViewModelCreate, AddressOf 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 AddHandler MVVMContextCompositionRoot.ViewModelCreate, Sub(s,e) e.ViewModel = kernel.Get(e.RuntimeViewModelType)
静态ViewModelCreate事件是一个weak event,如果它包含一个变量的闭包(上面示例中的" kernel "或下面示例中的" rootContainer "范围),其生命周期短于父容器的生命周期,那么VS垃圾收集器可能会过早地收集该变量,并且事件处理程序可能永远不会被调用。
C#:
[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); } }; }
VB.NET:
<STAThread> Shared Sub Main() 'rootContainer is declared at the Main method level Dim rootContainer As IContainer Dim builder = New ContainerBuilder() builder.RegisterType(Of TestViewModel)() builder.RegisterType(Of [MyClass])().As(Of IService)() rootContainer = builder.Build() AddHandler MVVMContextCompositionRoot.ViewModelCreate, Sub(s, e) Using scope = rootContainer.BeginLifetimeScope(Function(b) b.RegisterType(e.RuntimeViewModelType).As(e.ViewModelType)) e.ViewModel = scope.Resolve(e.ViewModelType) End Using End Sub End Sub
要解决潜在的问题,请在执行订阅的对象级别将变量声明为属性/字段。
C#:
//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); } }; }
VB.NET:
'rootContainer is declared at the root level Private Shared Property rootContainer() As IContainer <STAThread> Shared Sub Main() Dim builder = New ContainerBuilder() builder.RegisterType(Of TestViewModel)() builder.RegisterType(Of [MyClass])().As(Of IService)() rootContainer = builder.Build() AddHandler MVVMContextCompositionRoot.ViewModelCreate, Sub(s, e) Using scope = rootContainer.BeginLifetimeScope(Function(b) b.RegisterType(e.RuntimeViewModelType).As(e.ViewModelType)) e.ViewModel = scope.Resolve(e.ViewModelType) End Using End Sub End Sub