Search code examples
wpfdatetimeitemscontrolivalueconvertergroupstyle

GroupStyle HeaderTemplate in an ItemsControl does not update correctly


I am making a layout for a message inbox, and am currently working on separating messages automatically based on days/weeks/months/years. More specifically I have a header that says "Today" for all messages recieved on the current date, "Yesterday" for yesterday, "3+ days ago" for anything from 3 to 6 days ago. "Last week" for anything 7-13 days ago to name some examples. You get the idea.

All of this works good so far, except one thing. If I leave the application on overnight my messages from today will be labelled as "Today", yet all older headers will not change. So yesterday is also labelled "Today", 2 days ago is "Yesterday" and so on. They are still grouped as they should, it's just the heading that won't update. It feels like it's missing some kind of OnPropertyChanged functionality, but how would that work in it's current state?

How my GroupStyle is set up:

<ItemsControl>
  <ItemsControl.Resources>
    <CollectionViewSource x:Key="MessageList" Source="{Binding Messages}">
      <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="MessageDate" />
      </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>
  </ItemsControl.Resources>
  <ItemsControl.ItemsSource>
    <Binding Source="{StaticResource MessageList}"/>
  </ItemsControl.ItemsSource>
  <ItemsControl.GroupStyle>
   <GroupStyle>
     <GroupStyle.HeaderTemplate>
       <DataTemplate>
         <StackPanel Margin="0 0 0 15">
           <TextBlock Text="{Binding Path=Items[0].MessageDate, Converter={StaticResource DateTimeToStringConverter}}"/>
           <Path Data="m 0 0 100 0"/>
          </StackPanel>
        </DataTemplate>
      </GroupStyle.HeaderTemplate>
    </GroupStyle>
  </ItemsControl.GroupStyle>
</ItemsControl>

My Converter (to change from DateTime to a string to present)

public class DateTimeToStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if ((DateTime)value == DateTime.Now.Date)
        {
            return "Today";
        }
        else if ((DateTime)value == DateTime.Now.AddDays(-1).Date)
        {
            return "Yesterday";
        }
        else if ((DateTime)value == DateTime.Now.AddDays(-2).Date)
        {
            return "2 Days Ago";
        }
        return "3+ Days Ago";
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

Can also add that it still works if I restart the application, but that I can't leave it on overnight without it malfunctioning is too annoying to overlook.

More context if needed from my older question


Solution

  • You need to raise the PropertyChanged event for the databound MessageDate property by midnight each day if you want this to work. The converter won't be invoked again until this event is raised.

    You could use a task scheduling framework such as Quartz.NET or FluentScheduler to run the code that raises the event in your view model at a certain time.