This is one way. It's nice and simple, but a little messy. You can get rid of the storyboard on each tick, increase the local value by the tick interval and use this to set the time. Then you will have only one part of the time.
Or ... A more elegant and reusable way is to create a helper class, which is a DependencyObject. I would also just use StoryBoard with DoubleAnimation, associating Storyboard.Target with an instance of DoubleTextblockSetter. Set the duration of the storyboard to your time and set the value to your time in seconds. Here is the DoublerBlockSetterCode code.
public class DoubleTextBlockSetter : DependencyObject { private TextBlock textBlock { get; private set; } private IValueConverter converter { get; private set; } private object converterParameter { get; private set; } public DoubleTextBlockSetter( TextBlock textBlock, IValueConverter converter, object converterParameter) { this.textBlock = textBlock; this.converter = converter; this.converterParameter = converterParameter; } #region Value public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( "Value", typeof(double), typeof(DoubleTextBlockSetter), new PropertyMetadata( new PropertyChangedCallback( DoubleTextBlockSetter.ValuePropertyChanged ) ) ); private static void ValuePropertyChanged( DependencyObject obj, DependencyPropertyChangedEventArgs args) { DoubleTextBlockSetter control = obj as DoubleTextBlockSetter; if (control != null) { control.OnValuePropertyChanged(); } } public double Value { get { return (double)this.GetValue(DoubleTextBlockSetter.ValueProperty); } set { base.SetValue(DoubleTextBlockSetter.ValueProperty, value); } } protected virtual void OnValuePropertyChanged() { this.textBlock.Text = this.converter.Convert( this.Value, typeof(string), this.converterParameter, CultureInfo.CurrentCulture) as string; } #endregion }
Then you can have a format converter:
public class TicksFormatConverter : IValueConverter { TimeSpanFormatProvider formatProvider = new TimeSpanFormatProvider(); public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { long numericValue = 0; if (value is int) { numericValue = (long)(int)value; } else if (value is long) { numericValue = (long)value; } else if (value is double) { numericValue = (long)(double)value; } else throw new ArgumentException("Expecting type of int, long, or double."); string formatterString = null; if (parameter != null) { formatterString = parameter.ToString(); } else { formatterString = "{0:H:m:ss}"; } TimeSpan timespan = new TimeSpan(numericValue); return string.Format(this.formatProvider, formatterString, timespan); } public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
I almost forgot TimespanFormatProvider. There is no format provider in Silverlight, so it appears.
public class TimeSpanFormatProvider : IFormatProvider, ICustomFormatter { public object GetFormat(Type formatType) { if (formatType != typeof(ICustomFormatter)) return null; return this; } public string Format(string format, object arg, IFormatProvider formatProvider) { string formattedString; if (arg is TimeSpan) { TimeSpan ts = (TimeSpan)arg; DateTime dt = DateTime.MinValue.Add(ts); if (ts < TimeSpan.FromDays(1)) { format = format.Replace("d.", ""); format = format.Replace("d", ""); } if (ts < TimeSpan.FromHours(1)) { format = format.Replace("H:", ""); format = format.Replace("H", ""); format = format.Replace("h:", ""); format = format.Replace("h", ""); } // Uncomment of you want to minutes to disappear below 60 seconds. //if (ts < TimeSpan.FromMinutes(1)) //{ // format = format.Replace("m:", ""); // format = format.Replace("m", ""); //} if (string.IsNullOrEmpty(format)) { formattedString = string.Empty; } else { formattedString = dt.ToString(format, formatProvider); } } else throw new ArgumentNullException(); return formattedString; } }
All of these things can be reused and should live in your toolbox. I pulled it out. Then, of course, you put it all together:
Storyboard sb = new Storyboard(); DoubleAnimation da = new DoubleAnimation(); sb.Children.Add(da); DoubleTextBlockSetter textBlockSetter = new DoubleTextBlockSetter( Your_TextBlock, new TicksFormatConverter(), "{0:m:ss}"); // DateTime format Storyboard.SetTarget(da, textBlockSetter); da.From = Your_RefreshInterval_Secs * TimeSpan.TicksPerSecond; da.Duration = new Duration( new TimeSpan( Your_RefreshInterval_Secs * TimeSpan.TicksPerSecond)); sb.begin();
And that should do the trick. This is just a million lines of code. And we have not even written Hello World yet ...;) I have not compiled this, but I did Copy and paste 3 classes directly from my library. I used a lot of them. It works great. I also use these classes for other things. TickFormatConverter comes in handy when binding data. I also have one that makes seconds. Very useful. DoubleTextblockSetter allows me to animate numbers, which is very interesting. Especially if you use different types of interpolation.
Enjoy.