Thursday 19 May 2011

WPF/Silverlight Content Ticker/News Ticker Control

WPF includes the Canvas control that allows you to place elements using exact coordinates. To position an element on the Canvas, you set the attached Canvas.Left and Canvas.Top properties. Canvas.Left sets the number of units between the left edge of your element and the left edge of the Canvas. Canvas.Top sets the number of units between the top of your element and the top of the Canvas. Although the Canvas control has a definite visible area determined by the Height and Width properties of the control, it allows child controls to be virtually placed at any coordinates. We can use this particular feature of Canvas control to achieve the ticking/sliding effect by continuously changing the coordinates of the content.
Demo application screenshot.
ContentTicker control derives from WPF ContentControl. The ControlTemplate is defined to place the content in a Canvas control. Also a double animation is defined that is started on loading of the control. The animation target property is set to the attached property (Canvas.Left) of the content.

    public class ContentTicker : ContentControl
    {
        Storyboard _ContentTickerStoryboard = null;
        Canvas _ContentControl = null;
        ContentPresenter _Content = null;

        static ContentTicker()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ContentTicker), new FrameworkPropertyMetadata(typeof(ContentTicker)));
        }

        public ContentTicker()
        {
            this.Loaded += new RoutedEventHandler(ContentTicker_Loaded);
        }

        ..
    }


        
            
                
                    
                        
                            
                        
                    
                
            
            
        
    

The ContentTicker control defines two additional dependency properties for defining the rate (speed) of ticking and direction (east or west). The rate is used to calculate the duration of the animation, the time required to complete one cycle of the animation.

        public double Rate
        {
            get { return (double)GetValue(RateProperty); }
            set { SetValue(RateProperty, value); }
        }

        public static readonly DependencyProperty RateProperty =
            DependencyProperty.Register("Rate", typeof(double), typeof(ContentTicker), new UIPropertyMetadata(60.0));

        public TickerDirection Direction
        {
            get { return (TickerDirection)GetValue(DirectionProperty); }
            set { SetValue(DirectionProperty, value); }
        }

        public static readonly DependencyProperty DirectionProperty =
            DependencyProperty.Register("Direction", typeof(TickerDirection), typeof(ContentTicker), new UIPropertyMetadata(TickerDirection.West));

The Canvas size is adjusted while loading the control and Content is vertically aligned in the canvas according to the user specified settings. Further the animation parameters are adjusted according to the Width of Canvas and Content controls. The animation is dependent on the Width of the Canvas and the content so the animation details are updated every time the size of these are changed.

        void UpdateAnimationDetails(double holderLength, double contentLength)
        {
            DoubleAnimation animation = 
                _ContentTickerStoryboard.Children.First() as DoubleAnimation;
            if (animation != null)
            {
                bool start = false;
                if (IsStarted)
                {
                    Stop();
                    start = true;
                }

                double from = 0, to = 0, time = 0;
                switch (Direction)
                {
                    case TickerDirection.West:
                        from = holderLength;
                        to = -1 * contentLength;
                        time = from / Rate;
                        break;
                    case TickerDirection.East:
                        from = -1 * contentLength;
                        to = holderLength;
                        time = to / Rate;
                        break;
                }

                animation.From = from;
                animation.To = to;
                TimeSpan newDuration = TimeSpan.FromSeconds(time);
                animation.Duration = new Duration(newDuration);

                if (start)
                {
                    TimeSpan? oldDuration = null;
                    if (animation.Duration.HasTimeSpan)
                        oldDuration = animation.Duration.TimeSpan;
                    TimeSpan? currentTime = _ContentTickerStoryboard.GetCurrentTime(_ContentControl);
                    int? iteration = _ContentTickerStoryboard.GetCurrentIteration(_ContentControl);
                    TimeSpan? offset = 
                        TimeSpan.FromSeconds(
                        currentTime.HasValue ? 
                        currentTime.Value.TotalSeconds % (oldDuration.HasValue ? oldDuration.Value.TotalSeconds : 1.0) : 
                        0.0);
                    
                    Start();

                    if (offset.HasValue &&
                        offset.Value != TimeSpan.Zero &&
                        offset.Value < newDuration)
                        _ContentTickerStoryboard.SeekAlignedToLastTick(_ContentControl, offset.Value, TimeSeekOrigin.BeginTime);
                }
            }
        }

The ContentTicker control is generic control to slide the content. It can be used as a news ticker, thumbnails slider, RSS feed slider etc., depending on the requirement. The Start/Stop and Pause/Resume methods can be used to dynamically change the behavior of the sliding content. The demo application uses the control as a text ticker and provides the interfaces to change the speed, content and direction at run time.

Download Source

3 comments:

  1. It looks good but when we click on the form the marque pauses... How do we can fix it?

    ReplyDelete
  2. Hi,

    Have you a sample project of your ticker for Silverlight/C# ?

    I tried convert to SL but there are problems with :

    DefaultStyleKeyProperty.OverrideMetadata(typeof(ContentTicker), new FrameworkPropertyMetadata(typeof(ContentTicker)));
    _ContentTickerStoryboard.Begin(_ContentControl, true);
    _ContentTickerStoryboard.Pause(_ContentControl);
    _ContentTickerStoryboard.Resume(_ContentControl);
    _ContentTickerStoryboard.Stop(_ContentControl);


    Thanks in advance,

    ReplyDelete
  3. Hey you've posted this great code without a license! Can we use this openly?

    ReplyDelete