0
votes

The ItemsSource of a DataGrid is bound to an ObservableCollection. Two of the DataGridColumns are bound to a DateTime property in the collection while a third column is used to display the date difference between them using an IMultiValue Converter. The behaviour I'm trying to understand and resolve is found in Scenario 2.

Scenario 1: No Issue

View is opened and the DataGrid already contains records because the collection is NOT empty. If a new object is added to the collection, it gets displayed in the DataGrid and the last column displays the date difference value correctly.

Scenario 2: Has Issue

View is opened but DataGrid contains no records because the collection is empty. If a new object is added to the collection, it gets displayed in the DataGrid but the last column (containing the converter) is empty. However, if the view is then closed and re-opened, the date difference displays correcty in the DataGrid.

I would like the date difference value to display in the DataGridcolumn when an object is first added to an empty collection. What am I missing?

Object Class

public class Evaluation
{

    public int ID { get; set; }
    public DateTime BirthDate { get; set; }
    public DateTime TestDate { get; set; }
}

ViewModel

public class EvaluationViewModel : ViewModelBase  
{

private ObservableCollection<Evaluation> evaluations;

public class EvaluationViewModel()
{
    evaluations = Utility.Convert<Evaluation>(db.evaluationRepository.GetAllById(Subject.ID));
    TestView = (CollectionView)new CollectionViewSource { Source = Evaluations }.View;
    TestView.SortDescriptions.Add(new SortDescription("TestDate", ListSortDirection.Ascending));
}



    public ObservableCollection<Evaluation> Evaluations
    {
        get { return evaluations; }

    }

    public CollectionView TestView { get; set; }

}

View

public class Evaluation
{
    public int ID { get; set; }
    public DateTime BirthDate { get; set; }
    public DateTime TestDate { get; set; }
}


<Window.Resources>
    <converters:DateDiffMonthMultiConverter x:Key="DateConverter"/>
</Window.Resources>

    <DataGrid ItemsSource="{Binding TestView}">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Path=ID}" Visibility="Hidden"/>
            <DataGridTextColumn Header="Birth Date" Binding="{Binding BirthDate}"/>
            <DataGridTextColumn Header="Test Date" Binding="{Binding TestDate}"/>
            <DataGridTemplateColumn Header="Age When Tested">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock>
                            <TextBlock.Text>
                                <MultiBinding Converter="{StaticResource DateConverter}">
                                    <Binding Path="BirthDate"/>
                                    <Binding Path="TestDate"/>
                                </MultiBinding>
                            </TextBlock.Text>
                        </TextBlock>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>

Converter

public class DateDiffMonthMultiConverter : IMultiValueConverter
{        
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {            
        string result = string.Empty;
        if(values[0] is DateTime && values[1] is DateTime)
        {
            DateTime start = (DateTime)values[1];
            DateTime end = (DateTime)values[0];
            TimeSpan ts = start - end;
            double avgDaysPerMonth = 30.4;
            double months = (double)ts.Days / avgDaysPerMonth;
            string suffix = months > 1 ? "mths" : "mth";
            result = string.Format("{0} {1}", months.ToString("0.0"), suffix);                 
        }           
        return result;
    }

    public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
    {
        return null;
    }
}
2

2 Answers

0
votes

I tried your code (adding an item to the ObservableCollection after 2 seconds), and it's working for me. Here is my code:

MainWindow.xaml.cs

public MainWindow()
{
    InitializeComponent();
    DataContext = new EvaluationViewModel();
    Loaded += MainWindow_Loaded;

}

private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    Task.Factory.StartNew(() => Thread.Sleep(2000))
    .ContinueWith((t) =>
    {
        (DataContext as EvaluationViewModel).Evaluations.Add(
      new Evaluation() { ID = 2, BirthDate = DateTime.Now.AddYears(-22), TestDate = DateTime.Now });
    }, TaskScheduler.FromCurrentSynchronizationContext());
}

ViewModel

public EvaluationViewModel()
{
    Evaluations = new ObservableCollection<Evaluation>();
    TestView = (CollectionView)new CollectionViewSource { Source = Evaluations }.View;
    TestView.SortDescriptions.Add(new SortDescription("TestDate", ListSortDirection.Ascending));
}

public ObservableCollection<Evaluation> Evaluations { get; }
public CollectionView TestView { get; set; }
0
votes

As it turns out the converter was not the issue, but instead one of the values used by the converter is a DependencyProperty (my bad for not recognizing this until now) and was throwing a DependencyProperty.UnsetValue error. I was able to resolve the issue by using the CreateNew() method when adding a new entity to the collection, so the navigation property was known at the time of loading the object into the DataGrid.