在 itemscontrol 中对用户控件进行数据绑定

Databinding a user control within an itemscontrol

我有一个 UserControl,它有一个 ViewModel,其中包含我自己的自定义 class 实例,称它为 Person。此 ViewModel 被设置为控件的 DataContext。我想在我的主 window 中使用此控件,它在其 ViewModel 中有一个名为 People 的 Person 类型的列表。我在 xaml 中这样调用控件:

<ItemsControl ItemsSource="{Binding People}">
    <ItemsControl.ItemsTemplate>
        <DataTemplate>
            <userControls:PersonDetails />
        </DataTemplate>
    </ItemsControl.ItemsTemplate>
</ItemsControl>

控件中的属性以这种方式绑定。

<TextBlock Text="{Binding Person.Name}" />

当我 运行 时,我得到了列表中人数的正确数量的控件,但没有填充任何详细信息。我知道我在做一些简单的错误,但找不到。请帮忙。

编辑:

我的用户控件 xaml 看起来像这样:

<UserControl x:Class="AddressBook.UserControls.PersonDetails"
         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:AddressBook.UserControls"
         xmlns:vm="clr-namespace:AddressBook.ViewModels"
         xmlns:enums="clr-namespace:AddressBook.Models"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="1000">    

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="40" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="1*" />
    </Grid.ColumnDefinitions>

    <TextBlock Style="{StaticResource PersonDetailHeader}" Text="Person Name:" />

    <TextBlock Style="{StaticResource PersonDetailValue}" Grid.Column="1" Text="{Binding Person.Name,
               UpdateSourceTrigger=PropertyChanged}" />

    <TextBlock Style="{StaticResource PersonDetailHeader}" VerticalAlignment="Center" Grid.Row="1" Text="Street Address:" />

    <TextBlock Style="{StaticResource PersonDetailValue}" Grid.Row="1" Grid.Column="1"
               Text="{Binding Person.StreetAddress, UpdateSourceTrigger=PropertyChanged}" />

    <TextBlock Style="{StaticResource PersonDetailHeader}" Grid.Row="2" Text="Town:" />

    <TextBlock Style="{StaticResource PeronDetailValue}" Grid.Row="2" Grid.Column="1"
               Text="{Binding Person.Town, UpdateSourceTrigger=PropertyChanged}" />

    <TextBlock Style="{StaticResource PersonDetailHeader}" Grid.Row="3" Text="County:" />

    <TextBlock Style="{StaticResource PersonDetailValue}" Grid.Row="3" Grid.Column="1"
               Text="{Binding Person.County, UpdateSourceTrigger=PropertyChanged}" />

    <TextBlock Style="{StaticResource PersonDetailHeader}" Grid.Row="4" Text="Postcode:" />

    <TextBlock Style="{StaticResource PersonDetailValue}" Grid.Row="4" Grid.Column="1"
               Text="{Binding Person.Postcode, UpdateSourceTrigger=PropertyChanged}" />

    <TextBlock Style="{StaticResource PersonDetailHeader}" Grid.Row="5" Text="Phone:" />

    <TextBlock Style="{StaticResource PersonDetailValue}" Grid.Row="5" Grid.Column="1"
               Text="{Binding Person.Phone, UpdateSourceTrigger=PropertyChanged}" />

    <StackPanel Grid.Row="7" Grid.ColumnSpan="2" HorizontalAlignment="Center" Orientation="Horizontal">
        <Button Style="{StaticResource ButtonStyle}" Content="Click" Command="{Binding ButtonClickCommand}" />

    </StackPanel>
</Grid>

用户控件视图模型:

public class PersonDetailsViewModel
{
    public Person Person { get; set; }

    public Command ButtonClickCommand { get; } 

    public PersonDetailsViewModel()
    {
        ButtonClickCommand = new Command(ButtonClick);
    }

    private void ButtonClick(object obj)
    {
        throw new NotImplementedException();
    }

UserControl.xaml.cs:

public partial class PersonDetails : UserControl
{
    public PersonDetails()
    {
        InitializeComponent();
    }
}

Person.cs

public class Person : BaseModel
{
    public string Name
    {
        get { return name; }
        set
        {
            name = value;

            NotifyPropertyChanged();
        }

等....

主视图模型:

public class MainViewModel : BaseViewModel
{
    public ObservableCollection<Person> People { get; }
        = new ObservableCollection<Person>();

    public MainViewModel()
    {
        PopulatePeople();
    }
}

不确定这是否是您想要做的。 如果您只想显示人员列表中每个人的一些 carac,您可以这样做:

     <Grid>
            <ItemsControl ItemsSource="{Binding People}">
                    <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                    <Grid Margin="0,0,0,5">
                                            <Grid.ColumnDefinitions>
                                                    <ColumnDefinition Width="*" />
                                                    <ColumnDefinition Width="100" />
                                            </Grid.ColumnDefinitions>
                                            <TextBlock Text="{Binding Name}" />
                                            <TextBlock Grid.Column="1" Text="{Binding Age}" />
                                    </Grid>
                            </DataTemplate>
                    </ItemsControl.ItemTemplate>
            </ItemsControl>
    </Grid>

这将绑定您列表中每个元素的名称 属性 上的文本,以及您列表中每个人的名称。 由于您的 ItemsSource 已经绑定到 People,因此您的 Items 的 Binding 已经在您列表中的每个 Person 上,因此您应该将您的属性直接称为 {Binding Name} 而不是 {Binding Person.Name}

现在我明白你为什么要在 UserControl XAML 中创建 PersonDetailsViewModel,以及命令有什么问题。

解决此问题的最简单方法是使 MainViewModel.People 成为 PersonDetailsViewModel 的集合,而不是 Person。那就是我要做的。

但是如果你想保持 MainViewModel.People 不变,同时在你的 UserControl 中仍然使用 PersonDetailsViewModel,这对我来说很有意义。你可以这样做:

.xaml

<UserControl 
     x:Class="AddressBook.UserControls.PersonDetails"
     DataContextChanged="PersonDetails_DataContextChanged"

     ...etc...

     >

    <!-- Give the Grid a name... -->
    <Grid x:Name="OuterGrid">

.xaml.cs

public PersonDetails()
{
    InitializeComponent();
}

private void PersonDetails_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    //  ...so we can give the Grid a different DataContext
    OuterGrid.DataContext = new PersonDetailsViewModel { 
        Person = (Person)this.DataContext
    };
}

现在它将为其 DataContext 继承 Person,但在内部它将使用基于 PersonPersonDetailsViewModel