2009-06-14 8 views
4

私はマップ上で「レース」をアニメーション化しています。レースは45分かかりますが、アニメーションは60秒間実行されます。Silverlight DispatcherTimerの使用 - より良い方法がありますか(アニメーションのDependencyProperty)ですか?

2008 City2Surf race demoを見ると、私の言うことを知ることができます。左上にある

「レースクロックは」「リアルタイム」を示し、はハックのビットを思わSystem.Windows.Threading.DispatcherTimer.xaml.csにアップが設定されなければならなかったしなければなりません。

私は多分いうだけでStoryBoard.GetCurrentTime()よりも、アニメーションにしたDependencyPropertyがあるだろうと思ったが、代わりに私が

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

に持っていたし、その後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(); 
    } 

これは動作し、 OKを実行するようだ - しかし、私の質問は:私はこれをよりうまくやるはずのSilverlightアニメーション/ストーリーボードクラスで何かを逃していますか?私はを覚えておく必要があります DispatcherTimerも停止!

質問を別の方法で入力してください:TextBoxのコンテンツ(.Text自体、場所/寸法などではありません)の「アニメーション」についてのより良い提案はありますか?

+0

私はレースの視覚化が大好きでした。よくやった。 – caryden

答えて

5

これは一方的な方法です。それは素敵でシンプルですが、少し面倒です。あなたはストーリーボードを取り除くことができ、各ティックごとにティック間隔でローカル値をインクリメントし、それを使って時間を設定します。あなたは1つのタイムピースしか持たないでしょう。

...より洗練された再利用可能な方法は、DependencyObjectであるヘルパークラスを作成することです。また、StoryboardをDoubleAnimationで使用して、Storyboard.TargetをDoubleTextblockSetterのインスタンスにバインドします。ストーリーボードの期間をあなたの時間に設定し、値をあなたの時間(秒)に設定します。ここに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 
} 

その後、フォーマットコンバータを持っているかもしれません:

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

私はほとんどTimespanFormatProviderを忘れてしまいました。 timespanのフォーマットプロバイダはSilverlightにはありませんので、表示されます。

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

これらのものはすべて再利用可能であり、あなたのツールボックスに保存する必要があります。私はそれを私から引っ張った。その後、もちろん、あなたはそれをすべて一緒配線:

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

そして、それはトリックを行う必要があります。それは100万行のコードのようなものです。私はまだHello Worldを書いたことはありません...;)私はそれをコンパイルしませんでしたが、私は3つのクラスを私のライブラリから直接コピー&ペーストしました。私はそれらをかなり使ってきました。それは素晴らしい作品です。私はまた、これらのクラスを他のものに使用します。 TickFormatConverterは、データバインディングの際に便利です。私もSecondsをする人がいる。非常に便利。 DoubleTextblockSetterを使用すると、数字をアニメーション化することができます。これは本当に面白いです。特に異なるタイプの補間を適用する場合。

お楽しみください。

+0

すごく包括的な答え!私はそれを試して、私がどうやって行くのか見てみましょう... – Conceptdev

関連する問題