views:

38

answers:

4

Hi! I'm using a DataTemplate to show items (from a class via data binding) in a listbox. This class also contains date and time, and I'm using a converter to convert this date/time to relative time (xx minutes ago), which is then displayed in a TextBlock. So far everything is great.

The problem is that I don't know how to keep this relative time updated (they're all stuck at the generated value, e.g. "1 second ago"). I could use ListBox.Items.Refresh(), but that also re-runs the animations I've set up for the items.

Any ideas?

Thanks in advance!

A: 

Have your model implement INotifyPropertyChanged. If your two way binding is set correctly call OnPropertyChanged passing it the property name whenever a property you want updated changes. This will alert whomever is looking at changes to that property (i.e. your view hence the two way binding requirement) that the value has changed and it needs to update.

public string Name
{
    get { return m_Name; }
    set 
    {
        OnPropertyChanged(Name);
        m_Name = value; 
    }
}

Update:

Use a timer. I'm not going to steal the source from Dave so here is a link to his answer to a very similar question. In your timer_Tick method do your relative time calculation. This will update your GUI every second.

Ragepotato
But will OnPropertyChanged work if the time for each item doesn't change? (It's the item's creation time on the server)
PZ01
So the time property on the model doesn't change. I'll assume that you're using DateTime.Now for the relative calculation and you just want the calculation actively updated, is that right?
Ragepotato
Basically yes :)
PZ01
About the timer: won't a timer that rapid make a huge toll on the application's resource usage? Anyhow, the technique using the virtualized ScrollChanged (which is called every time you scroll or a visible item is added) works good enough for me. Thanks for your help :)
PZ01
It won't be that much of a "drain", it's only called once every second. And, since it appears that you are new here, could you please mark one of the proposed answers as your accepted answer. It helps others to know when questions are closed and gives good guidance to those who look to the questions in the future as to what works and what doesn't. Thanks and welcome =D
Ragepotato
A: 

You need to implement INotifyPropertyChanged or DependencyProperty for the property you're binding to in order to see updates.

DependencyProperty is the preferred method in WPF, since it will result in better performance at runtime. Here is the documentation, including an example of how to create one.

Will Eddins
A: 

Ok this might not be the most elegant solution (I'm pretty sure of it), but for now I've done this with acceptable results using the ListBox's ScrollChanged event to simply add 1 millisecond to each visible item's time, which causes the relative time to update ;) The code is called every time I add something to the listbox, and only affects the currently visible items (kind of like the VirtualizingStackPanel) :)

int VO = 0; // I think that this was protection for when a load of items are added at the beginning. Maybe you can do fine without it.
private void HomeList_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
    int v = (int)e.VerticalOffset;
    if (HomeList.Items.Count > 0 && v != VO) // Maybe you can do fine without VO.
    {
     for (int i = 0; i < e.ViewportHeight; i++)
     {
      // Add 1 millisecond to the item's time here
     }
     VO = v; // Maybe you can do fine without VO.
    }
}
PZ01
A: 

I had the same problem just now and I solved it by creating a ViewModel for those items.

public class MyItem
{
    public DateTime { get; set; }
}

public class MyItemViewModel : INotifyPropertyChanged
{
    private string relativeTime;
    public string RelativeTime
    {
        get { return relativeTime; }
        set
        {
            relativeTime = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("RelativeTime"));
        }
    }

    public DateTime Date { get; set; }

    public static implicit operator MyItemViewModel(MyItem item)
    {
        return new MyItemViewModel { Date = item.Date }
    }
}

And then updating them using a timer.

updateRelativeTimeString = new Timer(s =>
    Items.ForEach(
         item => item.RelativeTime = item.Date.ToRelativeTime()),
    null,
    0,
    5000);

Using two extension methods (IEnumerable.ForEach and DateTime.ToRelativeTime)

loraderon