views:

323

answers:

2

This question is similar to another one I asked, but whereas in that case I wanted to know about forcing a binding to update from XAML or a view model, in this case I'd like to know about wrapping this up into a custom WPF FrameworkElement.

The functionality I'm after is a span of text that indicates how long ago something happened.

<TextBlock Text="It happened " />
<my:AgeTextBlock SinceTime="{Binding OccurredAtUtc}" />
<TextBlock Text=" ago" />

This would render as (for example):

It happened 1 min 13 sec ago

I have code that converts from a TimeSpan to the human-readable form shown.

In order to have the UI update every second I'm considering using a static DispatcherTimer (idea from Kent Boogaart's answer).

So here's what I have:

public class AgeTextBlock : TextBlock
{
    private static readonly DispatcherTimer _timer;

    static AgeTextBlock()
    {
        _timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
    }

    public AgeTextBlock()
    {
        _timer.Tick += ...;
    }

    // How can I unsubscribe from the Tick event to avoid a memory leak?
}

The comment indicates my problem. I can't see how I'd clean up properly with this approach. There's no Dispose method to override and remove the event handler.

What is the recommended pattern here?

+2  A: 

Just because TextBlock doesn't implement IDisposable doesn't mean you can't add the interface requirement and the implementation yourself. You can implement dispose yourself (not overriding anything from the base) and dispose of your timer there.

You will Obviously then be responsible for disposing of your AgeTextBlock class. You'll could do this in your windows closing event for example or something like that.

(Alternately, if you follow the MVVM pattern, and put your timer on the view model, you could make your view model IDisposable and just bind the view to the timer value on the view model.)

[Edit: Yeah, thinking about this, I don't really think this should be a custom control unlike Winforms, WPF doesn't have such a need for custom controls, instead I think I would expose a timer property on my view model (Possibly a pre-formatted property so you have a nice readable string) and bind that property to a normal text box - no custom control - then I would use a style or template to tweak the visual properties of the text box to look how I wanted. How does that sound?]

Simon P Stevens
Hi Simon. Thanks for the answer, but I don't feel that tracking instances for disposal is very elegant. Consider the use of this element within an ItemsControl DataTemplate for example. Originally I set about firing property changes from the view model, but I'll use this in quite a few places and it would introduce a fair amount of duplication of code. So, I'm still looking for a nice way to do this.
Drew Noakes
Regarding your edit, I respectfully disagree. My view model has a property (the time something happened) and the way I want to present it is via a string of text that indicates how long ago that was. I don't want to have to introduce information about dispatchers, string formatting, property change notification and resource deallocation into my view model just to support something that could (I hope!) be done in the UI more elegantly.
Drew Noakes
@Drew: 1) Part of the design of WPF is such that the GUI portion of the app does not contain any non-managed memory. By including the timer in the GUI code you are breaking this premise (timers contain references to unmanaged objects), so unfortunately I don't think there is an elegant way to solve this. I am very interested if you find one though, it is a problem I have come across a few times before, and like you say, I can't come up with anything particularly satisfactory. Perhaps you could utilise WPFs animation features - this is a very wild stab in the dark though.
Simon P Stevens
@Drew: 2) The edit. Obviously it depends on exactly what you are doing. The way I understand it, it would be the models responsibility to hold the original time something happened (I.E. the raw data), and the viewmodels responsibility to hold the timespan since then (I.E. the converted representation of that underlying model in a format that is better for the GUI). Of course you know your app, I don't know all your details, and you are in a far better position to judge which fits your requirements better, I'm just thinking of some alternatives.
Simon P Stevens
+4  A: 

Why not subscribe to the Tick event in the Loaded event of the control, and unsubscribe in the Unloaded event ?

Thomas Levesque