Using Silverlight DispatcherTimer - is there a better way (DependencyProperty on Animation)?

I draw a β€œrace” on the map. The race takes 45 minutes, but the animation lasts 60 seconds.

You can watch the 2008 City2Surf race to see what I mean.

The "racing clock" in the upper left should show "real time" and should be configured in .xaml.cs with System.Windows.Threading.DispatcherTimer , which seems a bit hacked .

I thought maybe the animation will have DependencyProperty, not just StoryBoard.GetCurrentTime() , but instead I had to

  // SET UP AND START TIMER, before StoryBoard.Begin() dt = new System.Windows.Threading.DispatcherTimer(); dt.Interval = new TimeSpan(0, 0, 0, 0, 100); // 0.1 second dt.Tick +=new EventHandler(dt_Tick); winTimeRatio = (realWinTime.TotalSeconds * 1.0) / animWinTime.TotalSeconds; dt.Start(); 

and then the Tick event handler

  void dt_Tick(object sender, EventArgs e) { var sb = LayoutRoot.Resources["Timeline"] as Storyboard; TimeSpan ts = sb.GetCurrentTime(); TimeSpan toDisplay = new TimeSpan(0,0, Convert.ToInt32(ts.TotalSeconds * winTimeRatio)); RaceTimeText.Text = toDisplay.ToString(); } 

This works and seems to do OK - but my question is: did I miss something in the Silverlight / storyboard classes that would do it more neatly? I must remember to stop DispatcherTimer too!

Or put the question in another way: any better suggestions for the "animation" of the TextBox content ( .Text itself, rather than location / dimensions / etc)?

+4
source share
1 answer

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.

+5
source

Source: https://habr.com/ru/post/1286188/


All Articles