I have a class that has two ObservableCollection <TimeValue>, where TimeValue is a custom DateTime / Value batch with change notification (via INotifyPropertyChanged). I call these goals and actions.
When I snap them to a diagram, everything works fine, and I get two LineSeries. If I bind one of them to the DataGrid, the Date column and the Value column work fine. I even got the TwoWay binding I need.
However, I need to have a DataGrid that has a Date column and columns for goals and actual data. The problem is that I need to list ALL dates in the range, while some of these dates may not have the corresponding values in Targets, Actuals, or both.
So, I decided that I would make a MultiBinding that takes targets and Actuals as input, and outputs a combined TimeSeriesC with null values when any of the originals doesn't matter.
It works, but does not respond to any changes in the underlying data.
This works fine (binding to a single ObservableCollection):
<ctrls:DataGrid Grid.Row="1" Height="400" AutoGenerateColumns="False" CanUserDeleteRows="False" SelectionUnit="Cell">
<ctrls:DataGrid.ItemsSource>
<Binding Path="Targets"/>
</ctrls:DataGrid.ItemsSource>
<ctrls:DataGrid.Columns>
<ctrls:DataGridTextColumn Header="Date" Binding="{Binding Date,StringFormat={}{0:ddd, MMM d}}"/>
<ctrls:DataGridTextColumn Header="Target" Binding="{Binding Value}"/>
</ctrls:DataGrid.Columns>
This works, but only on the first initialization. No response to change notification:
<ctrls:DataGrid Grid.Row="1" Height="400" AutoGenerateColumns="False" CanUserDeleteRows="False" SelectionUnit="Cell">
<ctrls:DataGrid.ItemsSource>
<MultiBinding Converter="{StaticResource TargetActualListConverter}">
<Binding Path="Targets"/>
<Binding Path="Actuals"/>
</MultiBinding>
</ctrls:DataGrid.ItemsSource>
<ctrls:DataGrid.Columns>
<ctrls:DataGridTextColumn Header="Date" Binding="{Binding Date,StringFormat={}{0:ddd, MMM d}}"/>
<ctrls:DataGridTextColumn Header="Target" Binding="{Binding Value[0]}"/>
<ctrls:DataGridTextColumn Header="Actual" Binding="{Binding Value[1]}"/>
</ctrls:DataGrid.Columns>
And here is my IMultiValueConverter:
class TargetActualListConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
TimeSeries<double> Targets = values[0] as TimeSeries<double>;
TimeSeries<double> Actuals = values[1] as TimeSeries<double>;
DateTime[] range = TimeSeries<double>.GetDateRange(Targets, Actuals);
int count = (range[1] - range[0]).Days;
DateTime currDate = new DateTime();
TimeSeries<double?[]> combined = new TimeSeries<double?[]>();
for (int i = 0; i < count; i++)
{
currDate = range[0].AddDays(i);
double?[] vals = { Targets.Dates.Contains(currDate) ? (double?)Targets.GetValueByDate(currDate) : null, Actuals.Dates.Contains(currDate) ? (double?)Actuals.GetValueByDate(currDate) : null };
combined.Add(new TimeValue<double?[]>(currDate, vals));
}
return combined;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
TimeSeries<double?[]> combined = value as TimeSeries<double?[]>;
TimeSeries<double> Targets = new TimeSeries<double>();
TimeSeries<double> Actuals = new TimeSeries<double>();
foreach (TimeValue<double?[]> tv in combined)
{
if(tv.Value[0]!=null)
Targets.Add(new TimeValue<double>(tv.Date,(double)tv.Value[0]));
if (tv.Value[1] != null)
Actuals.Add(new TimeValue<double>(tv.Date, (double)tv.Value[1]));
}
TimeSeries<double>[] result = { Targets, Actuals };
return result;
}
}
I cannot be too far away as it displays values.
?
, , ?
!