Reputation: 3
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();
}
}
Upvotes: 0
Views: 950
Reputation: 37060
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
.
Upvotes: 2
Reputation: 758
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}
Upvotes: 0