绑定到ViewModels集合的WPF无法按预期显示

绑定到ViewModels集合的WPF无法按预期显示,wpf,data-binding,itemssource,Wpf,Data Binding,Itemssource,我的同事和我一直在拼命地想弄明白为什么我们不能得到一组ViewModels来按预期进行渲染。我们创建了一个非常简单的示例来演示这个问题 基本上,我们有一个StupidPerson类,它有一个名字和一个朋友列表(也是StupidPerson的)。在MainViewModel中,我们创建根StupidPerson,并向他的朋友添加四个其他StupidPerson。主窗口仅显示使用StupidPersonViewModel的源StupidPerson StupidPersonView模型拥有所有的功能

我的同事和我一直在拼命地想弄明白为什么我们不能得到一组ViewModels来按预期进行渲染。我们创建了一个非常简单的示例来演示这个问题

基本上,我们有一个StupidPerson类,它有一个名字和一个朋友列表(也是StupidPerson的)。在MainViewModel中,我们创建根StupidPerson,并向他的朋友添加四个其他StupidPerson。主窗口仅显示使用StupidPersonViewModel的源StupidPerson

StupidPersonView模型拥有所有的功能,StupidPersonView背后的代码甚至实现了DependencyProperty。StupidPersonView将ItemsControl的ItemsSource绑定到StupidPersonViewModel的StupidFriends属性

为了尝试所有不同的可能性,我们确实把事情复杂化了。我希望从下面的XAML中看到的是“Name:Fred”,后面是“Friends:”,后面是四个“Name:XXXX”和空的“Friends:”列表。然而,我得到的是4个空的StupidPerson

现在的情况是,XAML magic没有使用我在MainViewModel中创建的StupidPersonViewModel(绑定到ItemsSource),而是新建了四个空的StupidPersonViewModel,并将它们用于要渲染的项目。它显然绑定到我创建的列表,因为它只渲染4个空的ViewModels

完全困惑

<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>

显示以下内容:

这个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类主视图模型
{
公共StupidPersonViewModel源{get;set;}
公共主视图模型()
{
Source=new StupidPersonViewModel{Person=new StupidPerson{Name=“Fred”};
Source.Person.StupidFriends.Add(新的StupidPerson{Name=“Bob”});
Source.Person.StupidFriends.Add(新的StupidPerson{Name=“Greg”});
Source.Person.StupidFriends.Add(新的StupidPerson{Name=“Frank”});
Source.Person.StupidFriends.Add(新的StupidPerson{Name=“Tommy”});
}
}
公共类StupidPersonViewModel:INotifyPropertyChanged
{
[CanBeNull]
公共字符串名称=>$“名称:{this.Person?.Name}”;
个人;
[CanBeNull]
公众人物
{
获取{返回this.person;}
设置
{
这个人=价值;
此.RaiseProperty已更改(此人的姓名);
this.StupidFriends=新的ObservableCollection();
foreach(值为var friend.StupidFriends)
{
this.StupidFriends.Add(新的StupidPersonViewModel{Person=friend});
}
this.RaisePropertyChanged(Name of(this.Name));
this.RaisePropertyChanged(this.StupidFriends的名字);
}
}
私有void RaisePropertyChanged(字符串属性)
{
本协议项下的不动产变更(不动产);
}
私人可观察收集的朋友;
公众可观察收集愚蠢的朋友
{
获取{返回this.stupidFriends;}
设置
{
this.stupidFriends=值;
this.RaisePropertyChanged(this.StupidFriends的名字);
}
}
//公共StupidPersonViewModel()
//{
//}
//公共StupidPerson视图模型(StupidPerson-person)
//{
//这个人=人;
//}
公共事件属性更改事件处理程序属性更改;
[NotifyPropertyChangedInvocator]
受保护的虚拟void OnPropertyChanged([CallerMemberName]字符串propertyName=null)
{
this.PropertyChanged?.Invoke(this,newpropertychangedeventargs(propertyName));
}
}

UserControl实现中的一个常见错误是显式地将其
DataContext
属性设置为预期视图模型的实例,就像您通过

<UserControl.DataContext>
    <local:StupidPersonViewModel />
</UserControl.DataContext>

这样做可以有效地防止UserControl从其父控件继承DataContext,如中所预期的那样

<ItemsControl.ItemTemplate>
    <DataTemplate>
        <local:StupidPersonView />
    </DataTemplate>
</ItemsControl.ItemTemplate>

其中继承的DataContext是
ItemsSource
集合的元素

这里的继承意味着



所以不要显式地设置UserControl的DataContext。从未。任何告诉您这一点的博客或在线教程都是完全错误的。

用户控件实现中的一个常见错误是显式地将其
DataContext
属性设置为预期视图模型的实例,就像您通过

<UserControl.DataContext>
    <local:StupidPersonViewModel />
</UserControl.DataContext>

这样做可以有效地防止UserControl从其父控件继承DataContext,如中所预期的那样

<ItemsControl.ItemTemplate>
    <DataTemplate>
        <local:StupidPersonView />
    </DataTemplate>
</ItemsControl.ItemTemplate>

其中继承的DataContext是
ItemsSource
集合的元素

这里的继承意味着



所以不要显式地设置UserControl的DataContext。从未。任何博客或在线教程告诉您这一点都是完全错误的。

您是否应该首先创建StupidPerson Fred和Fred的朋友,然后创建viewmodel并分配Person属性,因为您在StupidPerson viewmodel中的Person设置器中有逻辑来创建StupidPerson列表中的ViewModels?您是否应该首先创建StupidPerson Fred和Fred的朋友,然后创建viewmodel并指定Person属性,因为您在StupidP中的Person setter中有逻辑