简体   繁体   中英

Databinding a user control within an itemscontrol

I have a UserControl that has a ViewModel containing an instance of my own custom class, call it Person. This ViewModel is set as the DataContext of the control. I am wanting to use this control in my main window which has, in its ViewModel a List of type Person called People. I am calling the control in my xaml like this:

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

Properties within the control are bound in this manner.

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

When I run I am getting the correct number of Controls for the amount of People in the list but no details are populated. I know I am doing something simple wrong but can't find it. Please help.

EDIT:

My UserControl xaml looks like this:

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

UserControl ViewModel:

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();
        }

etc....

MainViewModel:

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

    public MainViewModel()
    {
        PopulatePeople();
    }
}

Now I understand why you were creating the PersonDetailsViewModel in the UserControl XAML, and what the problem was with the command.

The simplest way to fix this is to make MainViewModel.People a collection of PersonDetailsViewModel instead of Person . That's what I'd do.

But if you want to leave MainViewModel.People as it is, while still using PersonDetailsViewModel inside your UserControl , that makes sense to me. You could do this:

.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
    };
}

Now it will inherit a Person for its DataContext , but internally it will use a PersonDetailsViewModel based on that Person .

Not sure if that's what you are trying to do. If you only wish to display some carac of every Person present in your People List you could do :

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

That will bind you Text on the Name property of each element of your list, so on the name of each Person of your list. As your ItemsSource is already bound to People, the Binding of your Items is already on each Person of your List, so you should call your properties directly as {Binding Name} and not {Binding Person.Name}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM