TransitionControl with RotatorExtensions

5 posts, 0 answers
  1. Per Thygesen
    Per Thygesen avatar
    18 posts
    Member since:
    Dec 2005

    Posted 26 Oct 2013 Link to this post

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

     

  2. Per Thygesen
    Per Thygesen avatar
    18 posts
    Member since:
    Dec 2005

    Posted 28 Oct 2013 Link to this post

    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)

  3. UI for WPF is Visual Studio 2017 Ready
  4. Konstantina
    Admin
    Konstantina avatar
    2332 posts

    Posted 30 Oct 2013 Link to this post

    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 >>
  5. Per Thygesen
    Per Thygesen avatar
    18 posts
    Member since:
    Dec 2005

    Posted 31 Oct 2013 Link to this post

    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);
     }
  6. Konstantina
    Admin
    Konstantina avatar
    2332 posts

    Posted 05 Nov 2013 Link to this post

    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 >>
Back to Top
UI for WPF is Visual Studio 2017 Ready