2010-09-01 88 views
10

有些東西一直困擾着我關於MVVM的內容 - 如果我使用視圖優先的方法來構建我的對象(這看起來是最常見的方法,至少在閱讀和搜索之後),我該如何獲得上下文信息輸入到視圖模型中?初始化視圖模型

我見過很多類似問題的答案,都說「使用DI容器注入模型」,但這並不能幫助我,所以我將提供一個小例子。

假設我的應用程序是PeopleEditor。它用於加載和編輯複雜的People對象。當你加載應用程序時,你會得到一個主屏幕,將一羣人加載到內存中 - 比方說,這些都可以通過我可以從我的容器中獲得的集合訪問。通過點擊一個人,你會被帶到編輯器屏幕。編輯器很複雜,所以這不是在一個屏幕中實現的簡單的主細節視圖。

所以,在主屏幕上,當我點擊一個人時,應用程序需要創建一個新的視圖和視圖模型並顯示視圖。如果我首先通過容器創建視圖模型,我可以用適當的人物對象初始化它。 這對我來說似乎很自然,所以我很難弄清爲什麼視圖優先似乎是主要模式。如何使用視圖優先方法來做到這一點?該視圖將創建視圖模型,該視圖模型可以獲取People的集合,但不知道其編輯的是哪個人。編輯清晰度:多人編輯可以同時存在,每編輯一個不同的人。

Prism 4.0 alpha的MVVM參考實現使用「狀態處理程序」,它基本上是應用程序用來在容器中存儲構造函數參數的服務。它保存狀態並調用ShowView,並且最終創建的視圖模型會導入一個狀態對象。這對我來說看起來很笨拙 - 就像它試圖假裝它真的不是那麼鬆散耦合。其他人是否有其他指導?

+0

爲什麼你不想當你說的更自然地你使用視圖模型第一種方法? marlon grech表示,視圖或視圖模型首先是個人偏好的選擇(視圖首先更容易與混合使用)。我也這麼認爲,我使用最適合我的場景的方法。所以我在我的應用程序中混淆了兩者,這取決於我想要做什麼。 – blindmeis 2010-09-01 06:36:43

+0

有趣的問題。我總是問自己關於視圖優先方法的相同問題......視圖模型 - 首先看起來更加自然,所以這就是我始終使用的方法 – 2010-09-01 08:16:50

+0

@blindmeis:謝謝你的迴應。這並不是說我不想首先使用viewmodel--事實上,這就是我現在的應用程序的結構 - 只是我很好奇如何以視圖優先的方式來完成它。我在應用程序中試圖實現的東西對我來說看起來相當普遍,而viewmodel-首先看起來很自然,答案是我無法弄清楚爲什麼更多更好地選擇「viewmodel composition」或「viewmodel-first」。 – nlawalker 2010-09-01 14:20:02

回答

3

nlawalker,

我不是專家,但我瞭解查看一和模型首先是:

  1. 視圖 - 首先:查看視圖模型的程序,您可以創建視圖,則視圖模型自動生成。
  2. Model-First:ViewModel程序查看,您可以在根應用程序中創建ViewModel對象圖,將其分配給根視圖數據上下文。然後讓視圖渲染其相關的孩子依賴於視圖模型。

不要吝嗇說模型的第一種方法是不好的,但我更喜歡查看,第一種方法,因爲視圖模型可以在代碼坐在後面,所以當一些工藝要求不具約束力的友好任務(PasswordBox,DialogConfirmation,ClosingForm等等),我可以在後面的代碼中編寫我的邏輯。

無論如何,爲了解決這個問題,我通常使用IOC和Event Aggregator的組合。它是:

  1. 對於viewmodel需要上下文信息註冊它在IOC容器中的實例,而不是它的類型。因此,即使不是它的觀點,它也是準備好了。
  2. 導航操作發生時(通過單擊人員列表項目)使用IOC容器解析器解析您的視圖。並使用指定的參數嚮導航總線發送事件。此外,這個事件將會被目標ViewModel捕獲並執行一些操作。

註冊viewmodel的實例並不是必須的。只有在由前一個視圖模型分派事件時才確保視圖模型已準備就緒。

UPDATE

但隨後與任何一種當地的情況,我需要使用的全球性設施向它發送一個事件來填充呢?

在你的情況下,上下文對象不是本地的,而是在對象調用之間傳遞的消息。顯然,在模型中,第一種方法你做:

//selectedPeople is contextual object 
myPeopleDetailVM.LoadData(selectedPeople) 

將幾乎相同的,當你通過selectedPeople到事件總線的參數。

如果考慮性能,則可以將其與WPF Routed Event System進行比較,在這種情況下,路由策略比事件總線更復雜,我認爲如果您對使用WPF路由事件足夠自信,那麼您應該使用Event Aggregator。

如果您使用內置框架事件聚合器(棱鏡,mvvmlight),您的viewmodel被事件總線污染,如果您抱怨這一點,我同意你的唯一問題。

希望有所幫助。

+0

感謝您的回覆。這幾乎是我想到的,但對我來說這似乎很奇怪 - 使用IOC創建視圖模型,很好,但是隨後使用任何類型的本地上下文填充它,我需要使用全局工具將它發送給事件?對我來說這似乎很奇怪。 – nlawalker 2010-09-01 14:23:03

+0

我已經更新了我的答案。 – ktutnik 2010-09-01 22:26:37

0

如果您使用的是棱鏡,您可以使用其導航功能輕鬆整齊地解決此問題。使用IRegionManager.RequestNavigate通過構建目標視圖的Uri來爲主對象導航到編輯視圖,以包含相應人員標識的查詢字符串參數。您可以在目標視圖模型的OnNavigatedTo()方法實現(INavigationAware成員,視圖模型應該實現此接口)中提取該id。

您可以在Prism下載附帶的「View-Swithing Navigation」示例應用程序中看到此操作。它在Quickstarts文件夾下。

,從同樣的樣品應用程式(模仿Outlook)中,這下面的代碼被用來從InboxView導航到EmailView爲了從收件箱中打開一個特定的電子郵件:

var builder = new StringBuilder(); 
builder.Append(EmailViewKey); 
var query = new UriQuery(); 
query.Add(EmailIdKey, document.Id.ToString("N")); 
builder.Append(query); 
this.regionManager.RequestNavigate(RegionNames.MainContentRegion, new Uri(builder.ToString(), UriKind.Relative)); 

而在EmailView的視圖模型EmailViewModel要打開的電子郵件從這樣的背景下導航提取:

void INavigationAware.OnNavigatedTo(NavigationContext navigationContext) 
    { 
     // todo: 15 - Orient to the right context 
     // 
     // When this view model is navigated to, it gathers the 
     // requested EmailId from the navigation context's parameters. 
     // 
     // It also captures the navigation Journal so it 
     // can offer a 'go back' command. 
     var emailId = GetRequestedEmailId(navigationContext); 
     if (emailId.HasValue) 
     { 
      this.Email = this.emailService.GetEmailDocument(emailId.Value); 
     } 

     this.navigationJournal = navigationContext.NavigationService.Journal; 
    } 

private Guid? GetRequestedEmailId(NavigationContext navigationContext) 
    { 
     var email = navigationContext.Parameters[EmailIdKey]; 
     Guid emailId; 
     if (email != null && Guid.TryParse(email, out emailId)) 
     { 
      return emailId; 
     } 

     return null; 
    }