Slide to next panorama item programmatically (set panorama SelectedIndex)

In window phone development (this post focus on Window Phone 7), there is a big problem that Microsoft doesn't allow me to change the SelectedIndex property of Panorama and control it programatically. Therefore, what will we do if we need to slide a panorama item to the next item or previous item. In the best way, re-develop a new panorama control as same as panorama control of Microsoft will allow you to custom anything you like in your control. However, in this section, we discuss about how to slide the Microsoft's panorama control.

At first, we must allow to change the SelectedIndex of it, we can set a new value for it directly because it's protected. We can use another way when we want to change:

  1. pan.SetValue(Panorama.SelectedItemProperty, pan.Items[newIndex]);
Hide/show line number

Yes, this code set the value for the dependance property SelectedItemProperty, which will let panorama control slide to the item at newIndex when it first load, and first load only. It is the problem, because of we can set only when it first loaded, if we change the selected item after loading, nothing occur. Though, we can set the selected item and let the panorama control update it, which is the solution in this post:

  1. (pan.Items[curIndex] as PanoramaItem).Visibility = Visibility.Collapsed;
  2. pan.SetValue(Panorama.SelectedItemProperty, pan.Items[(curIndex + 1) % pan.Items.Count]);
  3. pan.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
  4. (pan.Items[curIndex] as PanoramaItem).Visibility = Visibility.Visible;
Hide/show line number

We have changed one of panorama items and let panorama control measure again, after that, we have reseted the item that we had changed. The solution work well for me. But, it change the selected item imediately without any effect, what should we do now to preserve the effects? If it is neccessary, you must create a simple effect for replacing native effects. This is my function, it let panorama control do slide to the next right item.

  1. private void slidePanorama(Panorama pan)
  2. {
  3.     FrameworkElement panWrapper = VisualTreeHelper.GetChild(pan, 0) as FrameworkElement;
  4.     FrameworkElement panTitle = VisualTreeHelper.GetChild(panWrapper, 1) as FrameworkElement;
  5.     //Get the panorama layer to calculate all panorama items size
  6.     FrameworkElement panLayer = VisualTreeHelper.GetChild(panWrapper, 2) as FrameworkElement;
  7.     //Get the title presenter to calculate the title size
  8.     FrameworkElement panTitlePresenter = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(panTitle, 0) as FrameworkElement, 1) as FrameworkElement;
  10.     //Current panorama item index
  11.     int curIndex = pan.SelectedIndex;
  13.     //Get the next of next panorama item
  14.     FrameworkElement third = VisualTreeHelper.GetChild(pan.Items[(curIndex + 2) % pan.Items.Count] as PanoramaItem, 0) as FrameworkElement;
  16.     //Be sure the RenderTransform is TranslateTransform
  17.     if (!(pan.RenderTransform is TranslateTransform)
  18.         || !(panTitle.RenderTransform is TranslateTransform))
  19.     {
  20.         pan.RenderTransform = new TranslateTransform();
  21.         panTitle.RenderTransform = new TranslateTransform();
  22.     }
  24.     //Increase width of panorama to let it render the next slide (if not, default panorama is 480px and the null area appear if we transform it)
  25.     pan.Width = 960;
  27.     //Animate panorama control to the right
  28.     Storyboard sb = new Storyboard();
  29.     DoubleAnimation a = new DoubleAnimation();
  30.     a.From = 0;
  31.     a.To = -(pan.Items[curIndex] as PanoramaItem).ActualWidth; //Animate the x transform to a width of one item
  32.     a.Duration = new Duration(TimeSpan.FromMilliseconds(700));
  33.     a.EasingFunction = new CircleEase(); //This is default panorama easing effect
  34.     sb.Children.Add(a);
  35.     Storyboard.SetTarget(a, pan.RenderTransform);
  36.     Storyboard.SetTargetProperty(a, new PropertyPath(TranslateTransform.XProperty));
  38.     //Animate panorama title separately
  39.     DoubleAnimation aTitle = new DoubleAnimation();
  40.     aTitle.From = 0;
  41.     aTitle.To = (panLayer.ActualWidth - panTitlePresenter.ActualWidth) / (pan.Items.Count - 1) * 1.5; //Calculate where should the title animate to
  42.     aTitle.Duration = a.Duration;
  43.     aTitle.EasingFunction = a.EasingFunction; //This is default panorama easing effect
  44.     sb.Children.Add(aTitle);
  45.     Storyboard.SetTarget(aTitle, panTitle.RenderTransform);
  46.     Storyboard.SetTargetProperty(aTitle, new PropertyPath(TranslateTransform.XProperty));
  48.     //Start the effect
  49.     sb.Begin();
  51.     //After effect completed, we change the selected item
  52.     a.Completed += (obj, args) =>
  53.     {
  54.         //Reset panorama width
  55.         pan.Width = 480;
  56.         //Change the selected item
  57.         (pan.Items[curIndex] as PanoramaItem).Visibility = Visibility.Collapsed;
  58.         pan.SetValue(Panorama.SelectedItemProperty, pan.Items[(curIndex + 1) % pan.Items.Count]);
  59.         pan.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
  60.         (pan.Items[curIndex] as PanoramaItem).Visibility = Visibility.Visible;
  61.         //Reset panorama render transform
  62.         (pan.RenderTransform as TranslateTransform).X = 0;
  63.         //Reset title render transform
  64.         (panTitle.RenderTransform as TranslateTransform).X = 0;
  66.         //Because of the next of next item will be load after we change the selected index to next item
  67.         //I do not want it appear immediately without any effect, so I create a custom effect for it
  68.         if (!(third.RenderTransform is TranslateTransform))
  69.         {
  70.             third.RenderTransform = new TranslateTransform();
  71.         }
  72.         Storyboard sb2 = new Storyboard();
  73.         DoubleAnimation aThird = new DoubleAnimation() { From = 100, To = 0, Duration = new Duration(TimeSpan.FromMilliseconds(300)) };
  75.         sb2.Children.Add(aThird);
  76.         Storyboard.SetTarget(aThird, third.RenderTransform);
  77.         Storyboard.SetTargetProperty(aThird, new PropertyPath(TranslateTransform.XProperty));
  78.         sb2.Begin();
  79.     };
  80. }
Hide/show line number

The idea is use render transform to create the sliding effect, when the effect is completed, the position of panorama item is the same as when user slided, at that time, we update the selected item. If we need to to slide repeatedly, we can listen to sb2.Completed event or sb.Completed event if we don't use sb2.

Note: Line 41 calculate the position of title, it work right for me, but I think, it is not a general expression, anyone can help me fix it please contact me by the link at the end of website.

The function above only slide to the right, the left sliding function will be update here lately. The idea is we update the selected item for first and set the render transform to the old item, we animate the transform to be 0 (XProperty from the item width to 0). Why? Because the panorama item only render 2 item at the same time, the currrent and the next on the right.