2017-03-27 153 views
0

我試圖創建一個內存遊戲,同時嚴格遵循MVVM模式來學習如何使用它。現在我在運行時創建視圖時遇到問題。C#MVVM如何動態創建視圖

我創建了以下項目結構:

  • 示範項目
  • -MemoryCardModel30
  • - 卡片
  • 視圖模型項目
  • -MainWindowViewModel
  • -CardViewModel
  • 查看項目
  • -CardView
  • StartApplication項目
  • -MainWindowView

的依賴關係如下:StartApplication項目 - >查看項目 - >視圖模型項目 - >模型項目

單擊MainWindowView上的按鈕後,該按鈕的ICommand函數在MainWindowViewModel內將從Model項目加載一個MemoryCardModel30實例。對於MemoryCardModel30實例中的每個卡,將創建一個CardViewModel。

現在我要面對的問題:如何創建CardView實例,如何將其DataContexts鏈接到CardViewModels以及如何在MainWindowView上安排/分配CardViews? ViewModel項目不能創建視圖,因爲它對View項目沒有依賴性(會創建一個循環依賴關係並打破該模式)。如何解決這個問題,同時遵循MVVM模式?

P.S .:卡片視圖需要準確定位x和y pos。這將需要一些複雜的計算,應該去相應的CardViewModel。所以我認爲一些基本的佈局如grid不夠用。

+0

您是否在使用框架,並且您的方法是「模型優先」還是「先查看」?此外,你可能能夠得到一個WrapPanel來做你想做的事...... https://msdn.microsoft.com/en-us/library/system.windows.controls.wrappanel(v=vs.110).aspx –

+0

這種事情就是爲什麼我們在我的工作中使用MV ** P ** VM:將模型鏈接到視圖,然後顯示這些視圖不是一個很適合MVVM的工作,但是是一個Presenter工作 –

+0

@BerinLoritsch不,我目前沒有使用任何框架。我想從一開始就學習這種模式。我只是在EntityFramework的上下文中「模型第一」而不熟悉MVVM。然而,在這種情況下,我開始使用ViewModel,然後是模型並保存視圖。 – user2653422

回答

4

將它們顯示在ItemsControl中。我假設MainWindowViewModel.CardsObservableCollection<CardViewModel>

<ItemsControl 
    ItemsSource="{Binding Cards}" 
    > 
    <!-- 
    This creates UI for each item. There are other ways, if you've got a collection 
    of heterogeneous item types. 
    --> 
    <ItemsControl.ItemTemplate> 
     <DataTemplate DataType="local:CardViewModel"> 
      <views:CardView /> 
     </DataTemplate> 
    </ItemsControl.ItemTemplate> 

    <!-- 
    Make it use a Canvas to be the actual container for the items, so we can control 
    their position arbitrarily, instead of the default StackPanel that just stacks 
    them up vertically. 
    --> 
    <ItemsControl.ItemsPanel> 
     <ItemsPanelTemplate> 
      <Canvas /> 
     </ItemsPanelTemplate> 
    </ItemsControl.ItemsPanel> 

    <!-- 
    The ItemsControl will put the instantiated item templates in ContentPresenters 
    that it creates. The positioning attributes have to go on the ContentPresenters, 
    because those are the direct children of the Canvas. The ContentPresenters are 
    the "item containers". You can customize them via the ItemContainerStyle property 
    of the ItemsControl. 
    --> 
    <ItemsControl.ItemContainerStyle> 
     <Style TargetType="ContentPresenter"> 
      <!-- 
      The datacontext will be CardViewModel. 

      Bind Canvas.Left and Canvas.Top to appropriate properties 
      of CardViewModel. I'll assume it's got Point Position { get; } 

      A much better, more "pure MVVM" way to do this is for the items to 
      provide some kind of abstraction, maybe row/column or something else, 
      and either place them in a Grid or UniformGrid or some other kind of 
      dynamic layout control, or else convert that abstraction into Canvas 
      coordinates with a value converter on the Binding. 

      Then you can display the same item objects in different ways at the same 
      time without locking them into one layout. 

      Don't drive yourself crazy striving for ideological purity at the expense 
      of getting code out the door, but do consider redesigning that part. 
      --> 

      <Setter Property="Canvas.Left" Value="{Binding Position.X}" /> 
      <Setter Property="Canvas.Top" Value="{Binding Position.Y}" /> 
     </Style> 
    </ItemsControl.ItemContainerStyle> 

這是在WPF/MVVM中執行它的規範方法。使用DataTemplate s創建相應類型的視圖實例。視圖模型負責將對象呈現給用戶;觀點負責他們如何顯示。您不需要或不需要任何MVVM框架。 WPF的內置DataTemplate功能非常強大。不要相信任何人認爲你需要在這個規模的兩個數量級之內的任何項目。

+1

我不會把座標放到虛擬層,但它們的抽象,這將在視圖層通過轉換器解決。 – Rekshino

+0

@Rekshino這是正確的(或至少「正確的」)方法,我會適當地更新我的答案。 –

0

我想我誤解了你的問題。我原本以爲你在問如何顯示特定視圖模型的新窗口。雖然這個答案不會特別適用於你,但我會留下它,因爲它與切線相關。它可能會幫助其他人對要搜索的內容感到困惑。


我有一個ViewManager類將視圖類型鏈接到viewmodel類型。其中一個就可以了方法是ShowViewFor處理這一任務,它需要一個視圖模型實例和:

  • 查找該視圖模型類型的視圖。
  • 創建該視圖的一個實例。
  • 將該視圖實例的DataContext設置爲傳入的視圖模型。
  • 顯示視圖。

它也確實喜歡開跟蹤的意見,顯示消息框和對話框等

一堆其他任務的ViewManager可雖然IoC容器通過一個接口,因此它可以被嘲笑彌補單元測試。

我確定有很多現有的框架可以做到這一點,但是和你一樣,我想從「根源」學習MVVM。