chincheta73 chincheta73 - 1 month ago 16
C# Question

Binding to a nested Property in DataGrid row tooltip WPF

I canĀ“t get this working. I have a view) which contains a DataGrid populated with items of an observable collection (MyDataCollection). Every item of MyDataCollection has different properties (Name, Description,..., Logs). Logs is an observable collection itself of Log items. Every Log item has different properties (Date, Person,...).

My data grid populated with items of MyDataCollection has a tooltip per row set. Like this:

<DataGrid ItemsSource="{Binding MyDataCollection}">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="ToolTip">
<Setter.Value>
<Border>
<Grid Margin="5" MaxWidth="400">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
...
</Grid.RowDefinitions>

...
<DataGrid x:Name="LogsGrid" Grid.Row="6" ItemsSource="{Binding PlacementTarget.DataContext.Logs, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ToolTip}}">
<DataGrid.Columns>
<DataGridTextColumn Header="Date"
Binding="{Binding Date}"
/>
<DataGridTextColumn Header="Person"
Binding="{Binding Person.FullName}"
/>

</DataGrid.Columns>
</DataGrid>
</Grid>
</Border>
</Setter.Value>
</Setter>
</Style>
</DataGrid.RowStyle>
</DataGrid>


I can see the tooltip, and I can see the datagrid in the tooltip with the Headers "Date" and "Person" but the grid content is empty. Looks like the binding is not set correct. Can anyone give me a hand? Thanks

Update 1 :
MyDataColletion contains objects of my custom class "Car". Here the definition of Car:

public class Car : INotifyPropertyChanged
{
public string name;
public string description;
public Contact assignedTo;
public ObservableCollection<Log> logs = new ObservableCollection<Log>();
public string Name
{
get
{
return this.name;
}
set
{
if (this.name != value)
{
this.name = value;
NotifyPropertyChanged("Name");
}
}
}
public string Description
{
get
{
return this.description;
}
set
{
if (this.description != value)
{
this.description = value;
NotifyPropertyChanged("Description");
}
}
}
public Contact AssignedTo
{
get
{
return this.assignedTo;
}
set
{
if (this.assignedTo != value)
{
this.assignedTo = value;
NotifyPropertyChanged("AssignedTo");
}
}
}

public ObservableCollection<Log> Logs
{
get
{
return this.logs;
}
private set //TODO : Check if this is correct
{
if (this.logs != value)
{
this.logs = value;
NotifyPropertyChanged("Logs");
}
}
}

public Car()
{
// TODO: Delete this: (only here for testing)
Contact c = new Contact();
c.Name = "Test";
c.LastName = "Test";
for (int i = 0; i < 4; i++)
AddLog(DateTime.Now, c, new TimeSpan(2, 0, 0));
}
public void AddLog(DateTime date, Contact person, TimeSpan time)
{
Log newLog = new Log(date, person, time);
Logs.Add(newLog);
}

public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}


And my Log Class is:

public class Log : INotifyPropertyChanged
{
DateTime date;
Contact person;
TimeSpan time;
public DateTime Date
{
get
{
return this.date;
}
set
{
if (this.date != value)
{
this.date = value;
NotifyPropertyChanged("Date");
}
}
}
public Contact Person
{
get
{
return this.person;
}
set
{
if (this.person != value)
{
this.person = value;
NotifyPropertyChanged("Person");
}
}
}
public TimeSpan Time
{
get
{
return this.time;
}
set
{
if (this.time != value)
{
this.time = value;
NotifyPropertyChanged("Time");
}
}
}

public Log(DateTime date, Contact person, TimeSpan time)
{
this.date = date;
this.person = person;
this.time = time;
}

public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}

Answer

The only thing I can find in your code that doesn't work perfectly for me is Mode=TwoWay in the binding on LogsGrid.ItemsSource. That throws an exception for me, because it's telling the Binding that you want LogsGrid to write new values for ItemsSource back to the Binding source -- in this case, to the viewmodel's Logs property. Not only is that not what you want, but it's actually impossible since Logs has a private setter (and furthermore, DataGrid doesn't do that anyway). Hence the exception.

UpdateSourceTrigger=PropertyChanged is another one that serves no purpose, though harmlessly this time: That tells it when to write new Logs collections back to Car.Logs. But again, DataGrid can't do that. It doesn't create new values for the property. A TextBox will assign new Text values to the source property, that's what a TextBox is for. But DataGrid doesn't do that.

When I get rid of those two things, it works fine for me:

<DataGrid x:Name="LogsGrid" Grid.Row="6" ItemsSource="{Binding Logs}">
    <!-- etc. -->