Использование Silverlight DispatcherTimer - есть ли лучший способ (DependencyProperty на анимации)? - PullRequest
4 голосов
/ 14 июня 2009

Я анимирую «расу» на карте. Гонка занимает 45 минут, но анимация длится 60 секунд.

Вы можете посмотреть демонстрацию 20082 * City4Surf *, чтобы понять, что я имею в виду.

«Гоночные часы» в верхнем левом углу должны показывать «реальное время», и их нужно было настроить в .xaml.cs с System.Windows.Threading.DispatcherTimer, что кажется чем-то вроде хака,

Я подумал, что, возможно, в анимации будет DependencyProperty, а не просто StoryBoard.GetCurrentTime(), но вместо этого мне пришлось

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

Это работает и, кажется, работает нормально - но мой вопрос: я что-то упускаю в классах анимации / раскадровки Silverlight, которые бы делали это более аккуратно? Я должен помнить остановить DispatcherTimer тоже!

Или поставить вопрос по-другому: есть ли лучшие предложения по «анимации» контента TextBox (сам .Text, а не местоположение / размеры / и т. Д.)?

1 Ответ

5 голосов
/ 17 июня 2009

Это один из способов. Это красиво и просто, но немного грязно. Вы можете избавиться от раскадровки и на каждом тике увеличивать локальное значение на интервал между тиками и использовать его для установки своего времени. Тогда у вас будет только один тайм.

Или ... Более элегантным и многократно используемым способом было бы создание вспомогательного класса, который является объектом 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. В 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();

И это должно сработать. Это всего лишь миллион строк кода. И мы еще даже не написали Hello World ...;) Я не скомпилировал это, но я скопировал и вставил 3 класса прямо из моей библиотеки. Я использовал их довольно много. Работает отлично. Я также использую эти классы для других вещей. TickFormatConverter пригодится при привязке данных. У меня также есть тот, который делает секунды. Очень полезно. DoubleTextblockSetter позволяет мне анимировать числа, что действительно весело. Особенно, когда вы применяете различные типы интерполяции.

Наслаждайтесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...