/// <summary> /// Handles a change to the TickTime property. /// </summary> /// <param name="dependencyObject">The DependencyObject on which the property has changed value.</param> /// <param name="dependencyPropertyChangedEventArgs">Event data that that tracks changes to the effective value of this property.</param> static void OnTickTimePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) { // IMPORTANT CONCEPT: in any virtualized panel, we need to accept that any given control can be recycled and re-realized at any time, even during an // animation which could last several seconds. To account for this in our design, we don't store the state of any animation. When a new data context is // provided for this control, we'll use the start time from the View Model and the current time and calculate how much time is left in the animation. In // this way, when a PriceControl is realized, it will pick up how far into the animation cycle we are and provide animation only for as long as the // control is realized (or until the animation is complete). PriceControl priceControl = dependencyObject as PriceControl; Duration elapsedTime = new Duration(DateTime.Now.Subtract((DateTime)dependencyPropertyChangedEventArgs.NewValue)); // This will animate the background in the currently selected background color until the time since the last tick is equal to or greater than the // PriceControl.TickDuration property. if (elapsedTime < priceControl.TickDuration) { // This is the amount of time remaining in the animation. Duration remainingDuration = priceControl.TickDuration - elapsedTime; // The most important part of this animation, to make it appear as if we've been keeping track of all the price changes in the entire virtual // space, is to calculate the current age of the tick and from that interpolate the current state of animation. Double proRata = remainingDuration.TimeSpan.TotalMilliseconds / priceControl.TickDuration.TimeSpan.TotalMilliseconds; Double from = priceControl.TickOpacity * proRata; // We will only animate this control as long as it is realized or until the interpolated animation has completed. Note that we take the time to // clean up after the animation is completed as there are likely to be many items that are animated and the lifetime of any one PriceControl is // indefinite. Note that this is a relatively slow animation and there will likely be many of them, so we've chosen a framerate that will not // overly tax the CPU. DoubleAnimation opacityAnimation = new DoubleAnimation(); opacityAnimation.Completed += priceControl.OnAnimationCompleted; opacityAnimation.Duration = remainingDuration; opacityAnimation.FillBehavior = FillBehavior.Stop; opacityAnimation.From = from; Timeline.SetDesiredFrameRate(opacityAnimation, 4); priceControl.BeginAnimation(PriceControl.MaskOpacityProperty, opacityAnimation, HandoffBehavior.SnapshotAndReplace); } }
/// <summary> /// Handles a change to the LastPrice property. /// </summary> /// <param name="dependencyObject">The DependencyObject on which the property has changed value.</param> /// <param name="dependencyPropertyChangedEventArgs">Event data that that tracks changes to the effective value of this property.</param> static void OnLastPricePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) { // This makes sure that the mask background color reflect the direction of the price change. PriceControl priceControl = dependencyObject as PriceControl; priceControl.MaskBackground = priceControl.LastPrice <priceControl.Price?priceControl.TickUpBackground : priceControl.LastPrice> priceControl.Price ? priceControl.TickDownBackground : null; }
/// <summary> /// Handles a change to the Price property. /// </summary> /// <param name="dependencyObject">The DependencyObject on which the property has changed value.</param> /// <param name="dependencyPropertyChangedEventArgs">Event data that that tracks changes to the effective value of this property.</param> static void OnPricePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) { // This will make sure that the background mask reflects the proper price change PriceControl priceControl = dependencyObject as PriceControl; priceControl.MaskBackground = priceControl.LastPrice <priceControl.Price?priceControl.TickUpBackground : priceControl.LastPrice> priceControl.Price ? priceControl.TickDownBackground : null; priceControl.Content = priceControl.Price; }