2

I am developing a WPF for touch enabled device. I am facing a strange problem. My XAML structure is

 <ScrollViewer>
     <StackPanel orientation="Horizontal">
         <!--  Control goes here -->
      </StackPanel>
  <ScrollViewer>

Now

  1. To enable scrolling on touch I have to set PannigMode to HorizontalOnly
  2. To receive manipulation events I have to set PannigMode to None

Problem is I have to have these two functionality simultaneously.

Is there any work around so that the scrollviewer is scrolled on touch and also the manipulationcompleted event fires.

Please help.

Andras Sebo
  • 1,120
  • 8
  • 19
gmtek
  • 741
  • 3
  • 7
  • 25

1 Answers1

1

I had the same issue. You have at least two options

  • use MS Surface Toolkit
  • fix ScrollViewer

I've chosen the second one. Simply create a custom control, inherit from ScrollViewer, in Generic.xaml you only need to put a <ContentPresenter />, nothing more. The real job is in the code behind, but also not so complecated. I had to check whether the user touched a button or just wanted to scroll. The trick is to check what is on the touch points and turn on / off the panning mode.

And here is the code:

namespace Demo.Controls
{
   public class ScrollViewerWithTouch : ScrollViewer
   {
    private PanningMode panningMode;


    private bool panningModeSet;

      static ScrollViewerWithTouch()
      {
         DefaultStyleKeyProperty.OverrideMetadata(typeof(ScrollViewerWithTouch), new FrameworkPropertyMetadata(typeof(ScrollViewerWithTouch)));
      }

      protected override void OnManipulationCompleted(ManipulationCompletedEventArgs e)
      {
         base.OnManipulationCompleted(e);

         // set it back
         this.PanningMode = this.panningMode;
      }

      protected override void OnManipulationStarted(ManipulationStartedEventArgs e)
      {
         // figure out what has the user touched
         var result = VisualTreeHelper.HitTest(this, e.ManipulationOrigin);
         if (result != null && result.VisualHit != null)
         {
            var hasButtonParent = this.HasButtonParent(result.VisualHit);

            // if user touched a button then turn off panning mode, let style bubble down, in other case let it scroll
            this.PanningMode = hasButtonParent ? PanningMode.None : this.panningMode;
         }
         base.OnManipulationStarted(e);
      }

      protected override void OnTouchDown(TouchEventArgs e)
      {
         // store panning mode or set it back to it's original state. OnManipulationCompleted does not do it every time, so we need to set it once more.
         if (this.panningModeSet == false)
         {
            this.panningMode = this.PanningMode;
            this.panningModeSet = true;
         }
         else
         {
            this.PanningMode = this.panningMode;
         }

         base.OnTouchDown(e);         
      }

      private bool HasButtonParent(DependencyObject obj)
      {
         var parent = VisualTreeHelper.GetParent(obj);

         if ((parent != null) && (parent is ButtonBase) == false)
         {
            return HasButtonParent(parent);
         }

         return parent != null;
      }
   }
}
Andras Sebo
  • 1,120
  • 8
  • 19