This is a migrated thread and some comments may be shown as answers.

TransitionControl with RotatorExtensions

4 Answers 55 Views
TransitionControl
This is a migrated thread and some comments may be shown as answers.
Per Thygesen
Top achievements
Rank 1
Per Thygesen asked on 26 Oct 2013, 04:52 PM
The RotatorExtensions.cs sample, do not work when used with more than one TransitionControl.
(Probably because of "private static DependencyObject element;" ???)

namespace Examples.TransitionControl.Common
{
    public static class RotatorExtensions
    {
        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.RegisterAttached("ItemsSource", typeof(IEnumerable), typeof(RotatorExtensions), new PropertyMetadata(null,  OnItemsSourceChanged));
 
        public static readonly DependencyProperty ItemChangeDelayProperty =
            DependencyProperty.RegisterAttached("ItemChangeDelay", typeof(Duration), typeof(RotatorExtensions), new PropertyMetadata(new Duration(TimeSpan.FromSeconds(0.3))));
 
        public static readonly DependencyProperty CurrentSelectedIndexProperty =
            DependencyProperty.RegisterAttached("CurrentSelectedIndex", typeof(int), typeof(RotatorExtensions), new PropertyMetadata(-1, OnCurrentSelectedIndexChanged));
 
        private static readonly DependencyProperty TimerProperty =
            DependencyProperty.RegisterAttached("Timer", typeof(DispatcherTimer), typeof(RotatorExtensions), null);
 
        public static IEnumerable GetItemsSource(DependencyObject obj)
        {
            return (IEnumerable)obj.GetValue(ItemsSourceProperty);
        }
 
        public static void SetItemsSource(DependencyObject obj, IEnumerable value)
        {
            obj.SetValue(ItemsSourceProperty, value);
        }
 
        public static Duration GetItemChangeDelay(DependencyObject obj)
        {
            return (Duration)obj.GetValue(ItemChangeDelayProperty);
        }
 
        public static void SetItemChangeDelay(DependencyObject obj, Duration value)
        {
            obj.SetValue(ItemChangeDelayProperty, value);
        }
 
        public static int GetCurrentSelectedIndex(DependencyObject obj)
        {
            return (int)obj.GetValue(CurrentSelectedIndexProperty);
        }
 
        public static void SetCurrentSelectedIndex(DependencyObject obj, int value)
        {
            obj.SetValue(CurrentSelectedIndexProperty, value);
        }
 
        private static DispatcherTimer GetTimer(DependencyObject obj)
        {
            return (DispatcherTimer)obj.GetValue(TimerProperty);
        }
 
        private static void SetTimer(DependencyObject obj, DispatcherTimer value)
        {
            obj.SetValue(TimerProperty, value);
        }
 
        private static void OnCurrentSelectedIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UpdateCurrentlySelectedItem(d);
        }
 
        private static void MoveToNextElement(DependencyObject element)
        {
            IEnumerable source = GetItemsSource(element);
            if (source != null)
            {
                IEnumerable<object> convertedSource = source.Cast<object>();
                int currentIndex = GetCurrentSelectedIndex(element);
 
                currentIndex = ++currentIndex % convertedSource.Count();
                SetCurrentSelectedIndex(element, currentIndex);
            }
        }
 
        private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FrameworkElement element = d as FrameworkElement;
            ItemsControl itemsControl = d as ItemsControl;
 
            IEnumerable oldValue = e.OldValue as IEnumerable;
            IEnumerable newValue = e.NewValue as IEnumerable;
 
            if (element != null)
            {
                if (oldValue != null)
                {
                    // Detach the Ad Rotator functionality.
                    element.Loaded -= OnElementLoaded;
                    element.Unloaded -= OnElementUnloaded;
 
                    // If there is a timer attached, stop it.
                    DispatcherTimer timer = GetTimer(element);
                    if (timer != null)
                    {
                        timer.Stop();
                    }
                }
 
                if (newValue != null)
                {
                    // Attach the Ad Rotator functionality.
                    element.Loaded += OnElementLoaded;
                    element.Unloaded += OnElementUnloaded;
 
                    // If the target is an ItemsControl and its ItemsSource is not set, set it.
                    if (itemsControl != null && itemsControl.ItemsSource == null && itemsControl.Items.Count == 0)
                    {
                        itemsControl.ItemsSource = newValue;
                    }
                }
            }
        }
 
        private static DependencyObject element;
 
        private static void OnElementLoaded(object sender, RoutedEventArgs args)
        {
             element = sender as DependencyObject;
 
            // Create the timer and hook-up to the events.
            DispatcherTimer timer = new DispatcherTimer();
            timer.Interval = GetItemChangeDelay(element).TimeSpan;
            SetTimer(element, timer);
 
            timer.Tick += new EventHandler(timer_Tick);        
 
            timer.Start();
 
            // Make sure the currently pointed element is selected.
            UpdateCurrentlySelectedItem(element);
        }
 
        static void timer_Tick(object sender, EventArgs e)
        {
            MoveToNextElement(element);
        }
 
        private static void OnElementUnloaded(object sender, RoutedEventArgs args)
        {
            FrameworkElement element = sender as FrameworkElement;
            if (element != null)
            {
                DispatcherTimer timer = GetTimer(element);
                if (timer != null)
                {
                    timer.Stop();
                }
            }
        }
 
        private static void UpdateCurrentlySelectedItem(DependencyObject element)
        {
            ContentControl contentControl = element as ContentControl;
 
            IEnumerable source = GetItemsSource(element);
 
            // If there is no source we shouldn't do anything.
            if (source == null) return;
 
            // Find the actual index to be selected (if outside the boundaries of the collection)
            // and find the actual element to be selected.
            IEnumerable<object> convertedSource = source.Cast<object>();
            int currentIndex = GetCurrentSelectedIndex(element);
            object elementToSelect = convertedSource.ElementAtOrDefault(currentIndex);
 
            // Update the cotnent of the ContentControl if attached to a ContentControl.
            if (contentControl != null)
            {
                contentControl.Content = elementToSelect;
            }
        }
    }
}

 

4 Answers, 1 is accepted

Sort by
0
Per Thygesen
Top achievements
Rank 1
answered on 28 Oct 2013, 09:22 AM

Yes it's buggy.

Removing the static DependencyObject element, and sending it with the timer event instead.

(There's another bug as the OnElementLoaded event can be called multiple times -> triggering multiple timers)

0
Konstantina
Telerik team
answered on 30 Oct 2013, 03:34 PM
Hi Per,

The example is designed, so that the extensions can be used by only one TransitionControl. If you give us some more details about your scenario and what are your requirements, we will be happy to give you some directions how it can be achieved in the best way using the TransitionControl.

Regards,
Konstantina
Telerik
TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WPF.
Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
Sign up for Free application insights >>
0
Per Thygesen
Top achievements
Rank 1
answered on 31 Oct 2013, 05:23 AM
OK, but there's still bug. The sample rely on OnElementLoaded to be called only once, which it doesn't...
I've provided a change supporting multiple rotators, and fixing the bug. It's not tested thoroughly, as I'm only using it for demo purposes

//private static DependencyObject element;
 
 private static void OnElementLoaded(object sender, RoutedEventArgs args)
 {
     // Note Called 3 times?
     //System.Diagnostics.Debug.WriteLine("OnElementLoaded: New Timer!");
 
     // Added
     // This used to be a static instance variable! -> Must be a bug!
     var element = sender as DependencyObject;
     DispatcherTimer timer = GetTimer(element);
     if (timer != null)
     {
         timer.Stop();
     }
     // end added
 
     // Create the timer and hook-up to the events.
     timer = new DispatcherTimer();
     timer.Tag = element; // Added: Instead of static instance variable
     timer.Interval = GetItemChangeDelay(element).TimeSpan;
     SetTimer(element, timer);
 
     timer.Tick += new EventHandler(timer_Tick);
 
     timer.Start();
 
     // Make sure the currently pointed element is selected.
     UpdateCurrentlySelectedItem(element);
 }
 
 static void timer_Tick(object sender, EventArgs e)
 {
     // Added
     var timer = sender as DispatcherTimer;
     var element = timer.Tag as DependencyObject;
     // end added
 
     MoveToNextElement(element);
 }
0
Konstantina
Telerik team
answered on 05 Nov 2013, 03:51 PM
Hi Per,

We will consider changing the example.
Thank you for your feedback.

Regards,
Konstantina
Telerik
TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WPF.
Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
Sign up for Free application insights >>
Tags
TransitionControl
Asked by
Per Thygesen
Top achievements
Rank 1
Answers by
Per Thygesen
Top achievements
Rank 1
Konstantina
Telerik team
Share this question
or