2009-06-14 3 views
4

J'anime une «course» sur une carte. La course dure 45 minutes, mais l'animation dure 60 secondes.Utilisation de Silverlight DispatcherTimer - existe-t-il un meilleur moyen (DependencyProperty on Animation)?

Vous pouvez regarder le 2008 City2Surf race demo pour voir ce que je veux dire.

La « horloge de course » dans le coin supérieur gauche doit montrer « temps réel », et a dû être mis en place dans le .xaml.cs avec un System.Windows.Threading.DispatcherTimer qui semble un peu un hack.

Je pensais que peut-être qu'il y aurait un DependencyProperty sur l'animation plutôt que StoryBoard.GetCurrentTime(), mais j'ai dû

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

et le gestionnaire d'événements Tick

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

Cela fonctionne et semble fonctionner correctement - mais ma question est la suivante: ai-je raté quelque chose dans les classes d'animation/storyboard de Silverlight qui le ferait plus proprement? Je dois rappeler à arrêter le DispatcherTimer aussi!

Ou pour mettre la question d'une autre façon: de meilleures suggestions sur 'animation' du contenu TextBox (le .Text lui-même, pas l'emplacement/dimensions/etc)?

+0

J'ai adoré la visualisation de la course. Bien joué. – caryden

Répondre

5

C'est un moyen. C'est gentil et simple, mais un peu brouillon. Vous pouvez vous débarrasser du storyboard et à chaque tick, incrémenter une valeur locale par l'intervalle de tick et l'utiliser pour définir votre temps. Vous auriez alors seulement une pièce de temps. Ou ... Une manière plus élégante et réutilisable serait de créer une classe d'aide qui est un DependencyObject. Je voudrais également utiliser un StoryBoard avec un DoubleAnimation et lier le Storyboard.Target à une instance de DoubleTextblockSetter. Définissez la durée du storyboard sur votre temps et définissez la valeur sur votre temps en secondes. Voici le 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 
} 

alors vous pourriez avoir un convertisseur de format:

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

J'ai presque oublié le TimespanFormatProvider. Il n'y a pas de fournisseur de format pour la période dans Silverlight, donc il apparaît.

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

Tout ce matériel est réutilisable et devrait se trouver dans votre boîte à outils. Je l'ai tiré du mien. Ensuite, bien sûr, vous branchez tous ensemble:

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

Et cela devrait faire l'affaire. C'est juste comme un million de lignes de code. Et nous n'avons même pas encore écrit Hello World ...;) Je n'ai pas compilé cela, mais j'ai copié et collé les 3 classes directement depuis ma bibliothèque. Je les ai beaucoup utilisés. Cela fonctionne très bien. J'utilise aussi ces classes pour d'autres choses. Le TickFormatConverter est pratique lors de la liaison de données. J'en ai aussi un qui fait des secondes. Très utile. Le DoubleTextblockSetter me permet d'animer des nombres, ce qui est vraiment amusant. Surtout quand vous appliquez différents types d'interpolation.

Profitez-en.

+0

Wow réponse très complète! Je vais essayer et voir comment je vais ... – Conceptdev

Questions connexes