視圖模型管理
本文描述了如何在運(yùn)行時(shí)檢索ViewModel實(shí)例,注意如果您在設(shè)計(jì)時(shí)使用MvvmContext組件來(lái)構(gòu)建 MVVM-applications,該組件將自動(dòng)管理ViewModels。
如果ViewModel遵循POCO概念,MVVM框架將動(dòng)態(tài)地把該ViewModel轉(zhuǎn)換為包含必要基礎(chǔ)結(jié)構(gòu)(例如,支持簡(jiǎn)化的數(shù)據(jù)綁定)的新類??蚣芘c動(dòng)態(tài)創(chuàng)建的類實(shí)例一起工作,這意味著您無(wú)法在運(yùn)行時(shí)初始訪問(wèn)這些實(shí)例,因?yàn)樗鼈兊念愋蜕形创_定。
使用以下選項(xiàng)來(lái)檢索工作的ViewMode:
- The ViewModelSource.Create method
在這種方法中,您首先創(chuàng)建一個(gè)ViewModel實(shí)例,然后調(diào)用SetViewModel方法將該實(shí)例與特定的ViewModel類型關(guān)聯(lián)起來(lái)。
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
您可以從實(shí)現(xiàn)MVVM框架特性的ViewModelBase類中繼承ViewModels,在這種情況下,是可以直接創(chuàng)建ViewModel實(shí)例。注意,此時(shí)還需調(diào)用SetViewModel方法來(lái)指定框架在需要ViewModel時(shí)應(yīng)該使用這個(gè)實(shí)例。
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)
我們不推薦這種方法,因?yàn)槟鷮⑹OCO模型提供的所有特性。
- ViewModelCreate events
這種方法被設(shè)計(jì)為與依賴注入框架(如Ninject)一起工作,下面的例子說(shuō)明了這種注入是如何與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())
在這種情況下,您需要?jiǎng)討B(tài)生成Viewmodel并將它們綁定到接口(當(dāng)將接口直接綁定到POCO時(shí),MVVM框架特性將丟失)。要獲得所需的實(shí)例,處理常規(guī)(本地)或靜態(tài)(全局)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)
靜態(tài)ViewModelCreate事件是一個(gè)weak event,如果它包含一個(gè)變量的閉包(上面示例中的" kernel "或下面 Autofac示例中的" rootContainer "范圍),其生命周期短于父容器的生命周期,那么VS垃圾收集器可能會(huì)過(guò)早地收集該變量,并且事件處理程序可能永遠(yuǎn)不會(huì)被調(diào)用。
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
要解決潛在的問(wèn)題,請(qǐng)?jiān)趫?zhí)行訂閱的對(duì)象級(jí)別將變量聲明為屬性/字段。
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