2009-06-14 4 views
4

Ich animiere ein "Rennen" auf einer Karte. Das Rennen dauert 45 Minuten, aber die Animation dauert 60 Sekunden.Verwenden von Silverlight DispatcherTimer - gibt es einen besseren Weg (DependencyProperty für Animation)?

Sie können die 2008 City2Surf race demo sehen, was ich meine.

Die ‚Rennen Uhr‘ in der oberen linken müssen „Echtzeit“, zeigen und musste mit einem System.Windows.Threading.DispatcherTimer im .xaml.cs Set-up sein, die ein bisschen wie ein Hack scheint.

Ich dachte, vielleicht gibt es eher eine DependencyProperty auf die Animation sein würde als nur StoryBoard.GetCurrentTime(), sondern habe ich

  // 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(); 

und dann die Tick Ereignishandler

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(); 
    } 

Das funktioniert hatte und scheint OK zu sein - aber meine Frage ist: Fehle ich etwas in den Silverlight Animation/Storyboard-Klassen, die das sauberer machen würden? Ich muss daran denken, zu stoppen der DispatcherTimer auch!

Oder um die Frage anders zu stellen: irgendwelche besseren Vorschläge zur 'Animation' von TextBox Inhalt (die .Text selbst, nicht die Lage/Abmessungen/etc)?

+0

Ich liebte die Visualisierung des Rennens. Gut gemacht. – caryden

Antwort

5

Das ist eine Möglichkeit. Es ist schön und einfach, aber ein bisschen chaotisch. Sie können das Storyboard loswerden und bei jedem Tick einen lokalen Wert um das Tick-Intervall erhöhen und damit Ihre Zeit bestimmen. Du hättest dann nur eine Zeiteinheit.

Oder ... Ein eleganter und wiederverwendbarer Weg wäre, eine Hilfsklasse zu erstellen, die ein DependencyObject ist. Ich würde auch ein StoryBoard mit einer DoubleAnimation verwenden und das Storyboard.Target an eine Instanz des DoubleTextblockSetters binden. Setzen Sie das Storyboard Duration auf Ihre Zeit und stellen Sie den Wert auf Ihre Zeit in Sekunden ein. Hier ist der DoublerBlockSetterCode.

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 
} 

Dann könnten Sie ein Format-Konverter haben:

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(); 
    } 
} 

Fast hätte ich vergessen die TimespanFormatProvider. In Silverlight gibt es keinen Formatanbieter für die Zeitspanne, so scheint es.

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 das Zeug ist wiederverwendbar und sollte in Ihrem Werkzeugkasten leben. Ich habe es von meinem genommen. Dann verkabeln Sie natürlich alles zusammen:

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(); 

Und das sollte den Trick tun. Und es ist nur wie eine Million Zeilen Code. Und wir haben noch nicht einmal Hello World geschrieben ...;) Ich habe das nicht kompiliert, aber ich habe die 3 Klassen direkt aus meiner Bibliothek kopieren und einfügen können. Ich habe sie ziemlich oft benutzt. Es funktioniert großartig. Ich benutze diese Klassen auch für andere Dinge. Der TickFormatConverter ist praktisch, wenn Daten gebunden werden. Ich habe auch einen, der Sekunden macht. Sehr hilfreich. Mit dem DoubleTextblockSetter kann ich Zahlen animieren, was wirklich Spaß macht. Vor allem, wenn Sie verschiedene Arten der Interpolation anwenden.

Genießen.

+0

Wow, sehr umfassende Antwort! Ich werde es versuchen und sehen, wie ich gehe ... – Conceptdev

Verwandte Themen