我的同事和我一直在拼命想要理解为什么我们无法按预期呈现ViewModel的集合。我们创建了一个非常简单的例子来说明问题。
基本上,我们有一个StupidPerson类,它有一个名字和一个朋友列表(也是StupidPerson的)。在MainViewModel中,我们创建了根StupidPerson并将其他四个StupidPerson添加给他的朋友。 MainWindow只使用StupidPersonViewModel显示源StupidPerson。
StupidPersonViewModel具有所有的功能和口哨,StupidPersonView背后的代码甚至实现了DependencyProperty。 StupidPersonView将ItemsControl的ItemsSource绑定到StupidPersonViewModel的StupidFriends属性。
为了尝试所有不同的可能性,我们肯定会过于复杂化。我期望从下面的XAML看到的是“姓名:Fred”,其次是“Friends:”,然后是四个“Name:XXXX”和空的“Friends:”列表。但是,我得到的是4个空的StupidPerson。
发生的事情是,不是使用我在MainViewModel中创建的StupidPersonViewModel,它们绑定到ItemsSource,XAML魔法正在新建四个空的StupidPersonViewModel并使用它们来渲染项目。它显然与我创建的列表绑定,因为它只呈现4个空的ViewModel。
完全莫名其妙。
<UserControl x:Class="StupidXaml.StupidPersonView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:StupidXaml"
mc:Ignorable="d"
d:DesignHeight="300"
Background="White" Width="509.016">
<UserControl.DataContext>
<local:StupidPersonViewModel />
</UserControl.DataContext>
<StackPanel>
<Label Content="{Binding Name}" />
<Label Content="Friends:" />
<ItemsControl Margin="10,0,0,0" ItemsSource="{Binding StupidFriends}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:StupidPersonView />
</DataTemplate>
<!--<DataTemplate DataType="local:StupidPersonViewModel">
<StackPanel Orientation="Horizontal">
--><!-- Proves that binding is a StupidPersonViewModel --><!--
<Label Content="{Binding}"></Label>
--><!-- Both of these work! --><!--
<Label Content="{Binding Name}"></Label>
<Label Content="{Binding Person.Name}"></Label>
--><!-- But none of these work. How is this possible!? -->
<!-- DataContext binding -->
<!--<local:StupidPersonView DataContext="{Binding}" />
<local:StupidPersonView DataContext="{Binding DataContext, ElementName=item}" />-->
<!-- Dependency Property binding -->
<!--<local:StupidPersonView Person="{Binding Person}" />
<local:StupidPersonView Person="{Binding DataContext.Person, ElementName=item}" />
<local:StupidPersonView Person="{Binding DataContext.Person, ElementName=item, BindsDirectlyToSource=True}" x:Name="self" />--><!--
</StackPanel>
</DataTemplate>-->
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
显示: simplest attempt
和这个XAML
<UserControl x:Class="StupidXaml.StupidPersonView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:StupidXaml"
mc:Ignorable="d"
d:DesignHeight="300"
Background="White" Width="509.016">
<UserControl.DataContext>
<local:StupidPersonViewModel />
</UserControl.DataContext>
<StackPanel>
<Label Content="{Binding Name}" />
<Label Content="Friends:" />
<ItemsControl Margin="10,0,0,0" ItemsSource="{Binding StupidFriends}">
<ItemsControl.ItemTemplate>
<!--<DataTemplate>
<local:StupidPersonView />
</DataTemplate>-->
<DataTemplate DataType="local:StupidPersonViewModel">
<StackPanel Orientation="Horizontal">
<!--Proves that binding is a StupidPersonViewModel-->
<Label Content="{Binding}"></Label>
<!--Both of these work!-->
<Label Content="{Binding Name}"></Label>
<Label Content="{Binding Person.Name}"></Label>
<!--But none of these work. How is this possible!?-->
<!--DataContext binding-->
<local:StupidPersonView DataContext="{Binding}" />
<local:StupidPersonView DataContext="{Binding DataContext, ElementName=item}" />
<!--Dependency Property binding-->
<local:StupidPersonView Person="{Binding Person}" />
<local:StupidPersonView Person="{Binding DataContext.Person, ElementName=item}" />
<local:StupidPersonView Person="{Binding DataContext.Person, ElementName=item, BindsDirectlyToSource=True}" x:Name="self" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
public class MainViewModel
{
public StupidPersonViewModel Source { get; set; }
public MainViewModel()
{
Source = new StupidPersonViewModel { Person = new StupidPerson { Name = "Fred" } };
Source.Person.StupidFriends.Add(new StupidPerson { Name = "Bob" });
Source.Person.StupidFriends.Add(new StupidPerson { Name = "Greg" });
Source.Person.StupidFriends.Add(new StupidPerson { Name = "Frank" });
Source.Person.StupidFriends.Add(new StupidPerson { Name = "Tommy" });
}
}
public class StupidPersonViewModel : INotifyPropertyChanged
{
[CanBeNull]
public string Name => $"Name: {this.Person?.Name}";
private StupidPerson person;
[CanBeNull]
public StupidPerson Person
{
get { return this.person; }
set
{
this.person = value;
this.RaisePropertyChanged(nameof(this.Person));
this.StupidFriends = new ObservableCollection<StupidPersonViewModel>();
foreach (var friend in value.StupidFriends)
{
this.StupidFriends.Add(new StupidPersonViewModel { Person = friend });
}
this.RaisePropertyChanged(nameof(this.Name));
this.RaisePropertyChanged(nameof(this.StupidFriends));
}
}
private void RaisePropertyChanged(string property)
{
this.OnPropertyChanged(property);
}
private ObservableCollection<StupidPersonViewModel> stupidFriends;
public ObservableCollection<StupidPersonViewModel> StupidFriends
{
get { return this.stupidFriends; }
set
{
this.stupidFriends = value;
this.RaisePropertyChanged(nameof(this.StupidFriends));
}
}
//public StupidPersonViewModel()
//{
//}
//public StupidPersonViewModel(StupidPerson person)
//{
// this.Person = person;
//}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
答案 0 :(得分:2)
UserControl实现中常见的错误是将其DataContext
属性显式设置为预期视图模型的实例,就像
<UserControl.DataContext>
<local:StupidPersonViewModel />
</UserControl.DataContext>
这样做可以有效防止UserControl从其父控件继承DataContext,如
中所期望的那样<ItemsControl.ItemTemplate>
<DataTemplate>
<local:StupidPersonView />
</DataTemplate>
</ItemsControl.ItemTemplate>
其中继承的DataContext是ItemsSource
集合的元素。
这里的继承意味着Dependency Property Value Inheritance。
所以,不要明确设置UserControl的DataContext。决不。任何博客或在线教程都告诉你这是完全错误的。