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!