我已经和MVVM合作了一段时间,这个问题(如果这是一个问题)让我一直都难过。
我有一个ItemsControl绑定到我的MainViewModel
中的一个集合视图模型
public class MainViewModel : ViewModelBase
{
public ObservableCollection<string> Names { get; set; }
}
XAML
<ItemsControl ItemsSource="{Binding Names}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<view:NameView />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
每个的DataContext属性都是string类型(直接绑定到Model),但如果我想将DataContext绑定到基于该属性的ViewModel,该怎么办呢?我将如何实例化ViewModel并将其提供给Model(字符串)。
我希望这是有道理的。
答案 0 :(得分:1)
为什么不这样做?
public ObservableCollection<NameViewModel> Names { get; set; }
这看起来有点奇怪,但AFAIK没有具体禁止一个VM了解其他人。
如果您使用DI进行VM分辨率,显然您的设计必须进行调整。例如,您可以创建一个NamesView
,它是一个UserControl,其公共DependencyProperty
类型为IEnumerable<string>
。然后,NamesView
的ViewModel绑定到此DP ...
答案 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
和Thing2
个对象填充该集合,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
中的值在运行时发生变化,则会出现更复杂的问题:您需要将ViewModels
实现为ObservableCollection<object>
属性,您可能需要去至少处理Names
集合上的集合更改事件,并在添加,删除或更改ViewModels
中的项目时更新Names
集合。