Making a Slider work nice with the Pivot [or Panorama] controls in Windows Phone 7

So in my first app, I had this great UI built that heavily utilized the Slider control since ComboBox is…well…not an option. Luckily slider works brilliantly for what I want it to do. So I built my app and had it pretty much done when I thought up some cool new features that I wanted to expose. I ultimately decided to use a Pivot control to navigate between the features and thus ran into this problem when the user is dragging on my sliders, they are also dragging the Pivot control around! Bad news!

I solved this problem by detecting when the mouse button was up and down and setting the parent Pivot and the parent Pivot Item control to be IsHitTestVisible=False.

private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    this.parentPivot.IsHitTestVisible = true;
    this.parentPivotItem.IsHitTestVisible = true;
    Debug.WriteLine("MouseUp");
}

private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    this.parentPivot.IsHitTestVisible = false;
    this.parentPivotItem.IsHitTestVisible = false;
    Debug.WriteLine("MouseDown");
}

This worked great but I noticed something weird happening. Sometimes it would stay IsHitTestVisible=False even after the mouse came up. This is because every so often the MouseDown event would fire on the slider but the MouseUp would fire on a control other than the slider. This resulted in the entire app freezing and becoming unusable. Not good!

So I solved it by adding a listener to the Pivot’s parent control. This resulted in the next time a mouse up was detected it would set things back to normal and remove the handler from the Pivot’s parent control.

private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    this.parentPivot.IsHitTestVisible = false;
    this.parentPivotItem.IsHitTestVisible = false;
    Debug.WriteLine("MouseDown");

    pivotParent.MouseLeftButtonUp += new MouseButtonEventHandler(pivotParent_MouseLeftButtonUp);
}

private void pivotParent_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    this.parentPivot.IsHitTestVisible = true;
    this.parentPivotItem.IsHitTestVisible = true;
    pivotParent.MouseLeftButtonUp -= new MouseButtonEventHandler(pivotParent_MouseLeftButtonUp);
}

After I wired up the events to about 7 Sliders (just kidding, I was really on my second Slider) I thought, “Gee, wouldn’t it be great to have this as a behavior that I could apply to any Slider inside of a Pivot?” I answered myself, “Why, yes!”.

Turns out the behavior implementation was super easy. Just needed to wire up a couple events when the behavior was attached.

public class PivotFriendlySliderBehavior : Behavior<Slider>
    {
        Pivot parentPivot;
        PivotItem parentPivotItem;
        FrameworkElement pivotParent;

        protected override void OnAttached()
        {
            base.OnAttached();


            this.AssociatedObject.Loaded += new RoutedEventHandler(AssociatedObject_Loaded);
            this.AssociatedObject.MouseLeftButtonDown += new MouseButtonEventHandler(OnMouseLeftButtonDown);
            this.AssociatedObject.MouseLeftButtonUp += new MouseButtonEventHandler(OnMouseLeftButtonUp);

            this.AssociatedObject.AddHandler(Slider.MouseLeftButtonUpEvent, new MouseButtonEventHandler(OnMouseLeftButtonUp), true);
            this.AssociatedObject.AddHandler(Slider.MouseLeftButtonDownEvent, new MouseButtonEventHandler(OnMouseLeftButtonDown), true);
        }

    }

And then get a couple references after the Slider had loaded.

private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
    // NOTE: I am using an ASA 4.1 extension method to find parent of the AssociatedObject.
    parentPivot = this.AssociatedObject.FindParent<Pivot>();
    parentPivotItem = this.AssociatedObject.FindParent<PivotItem>();
    // get the parent of the pivot
    pivotParent = parentPivot.FindParent<FrameworkElement>();
}

That’s it! Now, I could drag and drop this behavior onto any Slider and it works beautifully inside of a Pivot.

Happy coding!

Advertisement

4 thoughts on “Making a Slider work nice with the Pivot [or Panorama] controls in Windows Phone 7

  1. The UX police will come after you for putting a slider in the pivot and panorama controls 🙂 You are basically mixing horizontal sliding gestures up between two controls. The user is used to that a horizontal swipe in pivot/panorama swipes to the next page, but suddenly part of the page doesn’t do that anymore. This breaks the UX that the user is used to, and is why the UX police has a warrant out for you 🙂

    Like

  2. Use next instead asa 4.1

    public static TParent GetParent(this DependencyObject obj) where TParent : DependencyObject
    {
    DependencyObject currentParent = VisualTreeHelper.GetParent(obj);

    while (currentParent != null)
    {
    if (currentParent is TParent)
    {
    return (TParent)currentParent;
    }

    currentParent = VisualTreeHelper.GetParent(currentParent);
    }

    return default(TParent);
    }

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s