0

I've got a complex back-end process that runs for a long period of time. All the vital stats are exposed as properties with INotifyPropertyChanged events raised when they're set.

I wanted to add an "Elapsed time" which is proving far more challenging than anticipated.

I exposed a ProcessStartedAt DateTime and threw together a converter to subtract the bound time from DateTime.Now. This works perfectly (once). After that, since the property isn't changing, the binding is never updated and the converter isn't called again.

I don't want the back-end to have a timer dedicated to updating an "Elapsed time" property. It violates the separation of concerns principle. I'd be happier with a timer in the UI but am unsure how to force a binding to refresh without updating the property value.

Even better, is there a way I can tell the binding to refresh at regular intervals?

<TextBlock Text="{Binding Path=ProcessStartedAt,
                          Converter={StaticResource ElapsedTime}}"/>
Basic
  • 26,321
  • 24
  • 115
  • 201
  • How about notifying that the property changes every second? – Moti Azu Nov 11 '14 at 13:33
  • Well, technically, it doesn't. The result of the conversion changes every second... – Basic Nov 11 '14 at 13:37
  • The result technically changes every millisecond, but you can notify in any interval you want to... or maybe I'm not getting what this property do. What does Elapsed time represent? – Moti Azu Nov 11 '14 at 13:43
  • When the back-end process starts, it sets the `ProcessStartedAt` to `DateTime.Now`. Elapsed Time in the UI should represent the difference between the `ProcessStartedAt` and `Now`. You're right, it's every millisecond (or even femtosecond if we're being pedantic) but one update per second is enough for my needs. What I was trying to avoid is logic for driving the UI being in the back-end project. Also, it feels a little hacky as the property hasn't actually changed. The back-end is exposing enough info that the UI should be able to derive the elapsed time. I just need to convince it to do so – Basic Nov 11 '14 at 13:50
  • Well one of the guidelines for properties is for them to be updated only upon set, so the use of properties here is hacky anyway. You really have a property that updates all the time (implicitly though), so notifying when a relevant enuough (one second for example) is pretty legitimate on my terms. I don't think the view should be the one to decide that interval, it really sounds like a view-model thing to me, especially when I think of another view that might consume the same property may arise. – Moti Azu Nov 11 '14 at 13:58
  • Oh I see people talked about this already http://stackoverflow.com/questions/6123998/binding-to-time-dependent-properties – Moti Azu Nov 11 '14 at 14:03

3 Answers3

2

No need for converter, just notify delaying on the getter:

public string RemainingTime
{
    get {
           Task.Delay(1000).
           ContinueWith(w => OnPropertyChanged(() => RemainingTime));
           return (CurrentServerTime - SelectedDeliveryTime).ToString("mm\\:ss");
        }
}

The binding will be recalled because of the OnPropertyChange

Paulo
  • 172
  • 1
  • 5
1

I would throw the timer inside the ValueConverter class and raise an event to call the Convert method each time the interval is hit.

This keeps that ValueConverter focused on its responsibility.

Scott Nimrod
  • 11,206
  • 11
  • 54
  • 118
  • This sounds promising. I can easily add the timer, but which event do I need to raise to cause Wpf to refresh the binding? – Basic Nov 11 '14 at 13:51
  • Actually, an event is probably overkill. Just have a while loop and sleep call. After the sleep call, call your convert method. – Scott Nimrod Nov 11 '14 at 13:54
  • I would create a class who's soul responsibility is to sleep and notify its subscribers when it awakes. Within the converter's constructor, I would subscribe to the object's timeInterval event. When the converter is notified of the event, it would call its Convert method. – Scott Nimrod Nov 11 '14 at 13:58
  • I think we're talking at cross purposes. The bit I don't know is _how_ to notify the converter. I don't care if it's an event, method call or anything else. How can I get a reference to the converter (if I'm not doing this _in_ the converter) and tell wpf that it needs to call the `Convert()` method again? – Basic Nov 11 '14 at 14:46
  • Pass the converter into the constructor of the TimeInterval object. Class TimeInterval { public TimeInterval(ValueConverter valueConverter){ ... } } – Scott Nimrod Nov 11 '14 at 14:52
  • var myTimeInterval = new TimeInterval(this); – Scott Nimrod Nov 11 '14 at 14:53
  • Sorry Scott, I'm still missing something... The Converter is instantiated by the framework when specified in the binding. I can instantiate a timer class from inside the converter (or reference a Singleton and register `this` with it), so can work around that but what _exactly_ does the timer do on tick? Even with a reference to the converter, I can't see a way to make WPF update invoke the `Convert()` method and update the Textblock? – Basic Nov 11 '14 at 14:58
  • Call Convert from the TimeInterval object. You passed an instance of the ValueConverter to the TimeInterval object's constructor. Within the TimeIntervalObject you can call the Convert method because you now have access to the converter. – Scott Nimrod Nov 11 '14 at 15:02
  • I've just tried it. I can call `Convert()` ok (with some fudged values which includes me reading the value from the viewmodel, passing it in, setting the targettype to string, a `parameter` of `null` and a local globalisation). It does return a converted string, but it doesn't update the text in the window? – Basic Nov 11 '14 at 15:06
  • Okay. Then consider adding an event to your TimeInterval class and update your view-model property each time a time-interval event is raised. Meaning have your view-model subscribe to the time-interval object's event and update the property in your view-model each time that event is raised. This will force a "property changed event". – Scott Nimrod Nov 11 '14 at 15:15
0

'I've got a complex back-end process that runs for a long period of time.' and 'I don't want the back-end to have a timer dedicated to updating an "Elapsed time" property.' seems a little counterproductive.

Trying to not update the view with your view-model seems to violate some MVC principles. Suppose you find a way to do so, the following engineer/developer might not.

Maybe you should find a way to timely change your view-model class timely instead of trying to 'block' updates in a timely manner. In that way you wouldn't have to concern about how things are updated. Instead, you would 'apply' changes to your view-model in a way that are easy to understand, change in the future and also be able to update technology when updates do framework are applied.

EDIT

I guess the only way is to set a DispatcherTimer, subscribe to it and call the OnPropertyChanged. Not related to your question, but you can also put a Delay to your Binding if needed.

rodrigogq
  • 1,943
  • 1
  • 16
  • 25
  • I'm not trying to block updates? I want the visual representation derived from a value to change over time. That seems entirely something view-related to me. I've got no problem with updating a view model regularly. As I said, `"I'd be happier with a timer in the UI"`and yeah, using the timer to update the model makes as much sense as refreshing the binding, but is there really no way to get the framework to invoke the converter automatically at regular intervals? – Basic Nov 11 '14 at 13:45
  • Also, can you explain why not wanting the back-end to worry about UI issues is counterproductive? – Basic Nov 11 '14 at 13:52
  • I thought you were trying to block notifications to occur because your back-end process was too slow or something. I based my answer on that assumption. Sorry if I got it wrong. Then, I guess the only way is to set a `DispatcherTimer`, subscribe to it and call the `OnPropertyChanged`. Not related to your question, but you can also put a `Delay` to your Binding if needed. – rodrigogq Nov 11 '14 at 14:00