J'ai une classe qui a deux ObservableCollection < TimeValue>, où TimeValue est un appariement DateTime/Valeur personnalisé avec notification de changement (via INotifyPropertyChanged). J'appelle ces cibles et réels.WPF DataGrid - Combinaison de TimeSeries avec MultiBinding, perte de notification de modification. Pourquoi?
Lorsque je les lie à un graphique, tout fonctionne parfaitement, et j'ai deux LineSeries. Si je lie l'un d'entre eux à un DataGrid, avec une colonne pour "Date" et une colonne pour "Value", fonctionne parfaitement à nouveau. Je reçois même la liaison TwoWay dont j'ai besoin.
Cependant, j'ai besoin d'un DataGrid avec une colonne "Date" et une colonne pour les cibles et les réels. Le problème est que j'ai besoin de lister TOUTES les dates dans une plage, alors que certaines de ces dates peuvent ne pas avoir de valeurs correspondantes dans les cibles, les réels, ou les deux. Donc, j'ai décidé de faire un MultiBinding qui prend les cibles et les réels en entrée, et produit une TimeSeriesC combinée, avec des valeurs nulles quand l'un des originaux n'a aucune valeur.
Cela fonctionne, mais ne répond à aucun changement dans les données sous-jacentes.
Cela fonctionne bien (se liant à un ObservableCollection):
<ctrls:DataGrid Grid.Row="1" Height="400" AutoGenerateColumns="False" CanUserDeleteRows="False" SelectionUnit="Cell">
<ctrls:DataGrid.ItemsSource>
<Binding Path="Targets"/>
<!--<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}"/>
<!--<ctrls:DataGridTextColumn Header="Target" Binding="{Binding Value[0]}"/>
<ctrls:DataGridTextColumn Header="Actual" Binding="{Binding Value[1]}"/>-->
</ctrls:DataGrid.Columns>
Cela fonctionne, mais seulement lors de la première initialisation. Aucune réponse au changement de notification:
<ctrls:DataGrid Grid.Row="1" Height="400" AutoGenerateColumns="False" CanUserDeleteRows="False" SelectionUnit="Cell">
<ctrls:DataGrid.ItemsSource>
<!--<Binding Path="Targets"/>-->
<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}"/>-->
<ctrls:DataGridTextColumn Header="Target" Binding="{Binding Value[0]}"/>
<ctrls:DataGridTextColumn Header="Actual" Binding="{Binding Value[1]}"/>
</ctrls:DataGrid.Columns>
Et voici mon 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);//Get min and max Dates
int count = (range[1] - range[0]).Days;//total number of 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;
}
}
Je ne peux pas être trop loin, car il affiche les valeurs.
Qu'est-ce que je fais mal? Ou, alternativement, y a-t-il une manière plus facile de faire ceci?
Merci à tous!
Merci rmoore. Malheureusement, la configuration que j'ai ne répond même pas aux événements CollectionChanged. J'ai essayé w/CompositeCollections, mais depuis la collection que je veux afficher n'est même pas de la même taille que les deux que j'ai entré, je ne vois pas comment je le ferais fonctionner. Bizarrement, la méthode Convert n'est appelée qu'une seule fois (par valeur), lors de l'affichage initial, et plus jamais. – AdrianoFerrari
Dans ce cas, le moyen le plus simple de résoudre ceci sans en savoir plus serait de créer une sorte de classe wrapper qui peut fusionner une cible et/ou un réel. Ensuite, si vous créez une seule ObservableCollection de ces wrappers, il devrait être beaucoup plus facile de lier. – rmoore
Je suppose que je vais devoir le faire de cette façon. Je posterai ici si je réussis à le faire comme je le souhaitais, car cela aurait été plus simple. Merci encore rmoore. Y at-il de toute façon pour vous attribuer des points sans marquer cela comme étant répondu? – AdrianoFerrari