2011-06-30 47 views
1

我一直在使用MVVM一段時間,而這個問題(如果它是一個問題)讓我一直難住。如何基於DataTemplate的DataContext對象創建ViewModel?

我有一個綁定到一個集合在我MainViewModel

視圖模型

public class MainViewModel : ViewModelBase 
{ 
    public ObservableCollection<string> Names { get; set; } 
} 

XAML一個ItemsControl

<ItemsControl ItemsSource="{Binding Names}"> 
    <ItemsControl.ItemTemplate> 
    <DataTemplate> 
     <view:NameView /> 
    </DataTemplate> 
    </ItemsControl.ItemTemplate> 
</ItemsControl> 

每個DataContext屬性是字符串類型(直接綁定到模型的),但是如果我想將DataContext綁定到基於該屬性的ViewModel,該怎麼辦?我將如何去實例化ViewModel併爲其提供Model(字符串)。

我希望這是有道理的。

回答

1

爲什麼不做以下幾點?

public ObservableCollection<NameViewModel> Names { get; set; } 

這似乎有點奇怪,但據我所知沒有一個針對VM明確禁止明知他人。

在您使用DI進行虛擬機解析的情況下,顯然您的設計必須進行調整。例如,您可以創建一個NamesView,這是一個UserControl,其公鑰爲DependencyProperty,其類型爲IEnumerable<string>。然後,NamesView的ViewModel綁定到這個DP ...

+0

當你說「沒有明確禁止一個虛擬機瞭解其他虛擬機時,它會響起我的一些警告,否則就會出現這種情況。這是什麼本質上是「兒童ViewModels」的正常模式?我的意思是,我認爲ViewModels不應該彼此瞭解。此外,它不需要MainViewModel來了解其他人,因爲ChildViewModel僅支持來自DataTemplate的新模型 – mortware

+0

@Mortick:這取決於你希望如何純粹。我從來沒有讀過關於破壞虛擬機之間的依賴關係的任何信息,但我看到有人試圖使用消息總線來做到這一點。坦率地說,當努力爭取純潔時,你最終會把自己送到古拉格身邊。如果通過允許VM A瞭解VM B,方便,易於維護並保持較小的代碼庫,那麼就讓它成爲。 – Will

+1

我會比威爾走得更遠:那不是純粹的追求,這是貨運崇拜的節目。虛擬機需要獨立於視圖進行測試。這是該模式的主要驅動因素。虛擬機之間的依賴關係與類之間的任何其他依賴關係類似:它們使測試更具挑戰性,但往往是完全合理的。 –

1

我想你在問關於數據模板匹配。

如果你想結合來選擇它在運行時使用,以呈現對象的數據模板,最簡單的方法是通過將模板的資源字典,並設置其DataType屬性,如:

<ItemsControl ItemsSource="{Binding Things}"> 
    <ItemsControl.Resources> 
    <DataTemplate DataType="{x:Type Thing1}"> 
     ... 
    </DataTemplate> 
    <DataTemplate DataType="{x:Type Thing2}"> 
     ... 
    </DataTemplate> 
    </ItemsControl.Resources> 
</ItemsControl> 

現在如果您的視圖模型,而不是一個Names財產,有Things屬性:

public ObservableCollection<object> Things { get; set; } 

你可以Thing1和填充集合10個對象和ItemsControl將呈現每個與適當的模板。

如果要根據屬性的選擇不同的模板,還有幾種方法可以完成此操作。一個是編寫一個DataTemplateSelector,它可以非常精確地控制選擇哪個模板,但要求您實際編碼(以及測試和記錄)某些內容。

另一種是使用樣式來顯示和隱藏基於觸發器的內容。這實際上並沒有選擇不同的模板本身,但它完成了或多或少的相同的事情。將它放在您的項目模板中,當Name爲「Thing1」時將顯示一組內容,而當Name爲「Thing2」時將顯示另一組內容。

關於這種方法的非常好處是,不像模板選擇,這是動態的:如果在運行時的Name屬性值更改(和您的視圖模型實現了產權變更通知),因此將有什麼出現在風景。

<StackPanel> 
    <ContentControl> 
     <ContentControl.Style> 
     <Style TargetType="ContentControl"> 
      <Setter Property="Visibility" Value="Collapsed"/> 
      <Style.Triggers> 
       <DataTrigger Binding="{Binding Name}" Value="Thing1"> 
        <Setter Property="Visibility" Value="Visible"/> 
       </DataTrigger> 
      </Style.Triggers> 
     </Style> 
     </ContentControl.Style> 
     <!-- content to display when Name = Thing1 goes here --> 
    </ContentControl> 
    <ContentControl> 
     <ContentControl.Style> 
     <Style TargetType="ContentControl"> 
      <Setter Property="Visibility" Value="Collapsed"/> 
      <Style.Triggers> 
       <DataTrigger Binding="{Binding Name}" Value="Thing2"> 
        <Setter Property="Visibility" Value="Visible"/> 
       </DataTrigger> 
      </Style.Triggers> 
     </Style> 
     </ContentControl.Style> 
     <!-- content to display when Name = Thing2 goes here --> 
    </ContentControl> 
</StackPanel> 

最後,如果你正在試圖做的基礎上,Names集合中的字符串值在您的收藏實際上是創造不同的視圖模型是什麼,你做的是,在視圖模型。你可以這樣做:

public IEnumerable<object> ViewModels 
{ 
    get 
    { 
     foreach (string name in Names) 
     { 
     switch (name) 
     { 
      case "Thing1": yield return new Thing1ViewModel(name); 
      case "Thing2": yield return new Thing2ViewModel(name); 
      default: throw new InvalidOperationException(); 
     } 
     } 
    } 
} 

如果數值在運行時Names變化,你有一個實質上更復雜的問題:你需要實現ViewModelsObservableCollection<object>屬性,您可能需要只要去處理Names集合上的集合更改事件,並在添加,刪除或更改Names中的項目時更新ViewModels集合。

+0

謝謝你的回答,它讓我的眼睛睜開了我的其他一些問題。可悲的是,你的回答並沒有解決我上面描述的問題。 – mortware

+0

在我的辯護中,這不是我讀過的最不明確的描述。以上是你似乎要問的幾個問題的一個很好的答案。 –

相關問題