\ ScrollViewer slider in touch interface not working properly

In WPF, I have the following XAML:

<ScrollViewer Canvas.Left="2266" Canvas.Top="428" Height="378" Name="scrollViewer1" Width="728" PanningMode="VerticalOnly" PanningRatio="2"> <Canvas Height="1732.593" Width="507.667"> <Slider Height="40.668" x:Name="slider1" Width="507.667" Style="{DynamicResource SliderStyle1}" Canvas.Left="15" Canvas.Top="150" /> </Slider> </Canvas> </ScrollViewer> 

This is a ScrollViewer containing a slider. I use the following on the touch screen, and I use panning even to scroll the ScrollViewer vertically. When PanningMode = "VerticalOnly" is set, the slider stops working!

I assume that ScollViewer consumes the touch / slide event and processes it before the slider works (but I think I'm wrong on this front).

Is there any workaround for this?

+6
source share
4 answers

I just solved this problem in our application.

What happens is that the ScrollViewer captures the TouchDevice in its PreviewTouchMove handler, which “steals” TouchDevice from other controls and prevents them from receiving any PreviewTouchMove or TouchMove events.

To get around this, you need to implement a Thumb user control that captures the TouchDevice in the PreviewTouchDown event and keeps a reference to it until the PreviewTouchUp event occurs. The control can then “steal” the capture back into the LostTouchCapture handler when necessary. Here is a short code:

 public class CustomThumb : Thumb { private TouchDevice currentDevice = null; protected override void OnPreviewTouchDown(TouchEventArgs e) { // Release any previous capture ReleaseCurrentDevice(); // Capture the new touch CaptureCurrentDevice(e); } protected override void OnPreviewTouchUp(TouchEventArgs e) { ReleaseCurrentDevice(); } protected override void OnLostTouchCapture(TouchEventArgs e) { // Only re-capture if the reference is not null // This way we avoid re-capturing after calling ReleaseCurrentDevice() if (currentDevice != null) { CaptureCurrentDevice(e); } } private void ReleaseCurrentDevice() { if (currentDevice != null) { // Set the reference to null so that we don't re-capture in the OnLostTouchCapture() method var temp = currentDevice; currentDevice = null; ReleaseTouchCapture(temp); } } private void CaptureCurrentDevice(TouchEventArgs e) { bool gotTouch = CaptureTouch(e.TouchDevice); if (gotTouch) { currentDevice = e.TouchDevice; } } } 

You will then need to reformat the slider to use CustomThumb instead of the default Thumb control.

+10
source

I struggled with a similar problem. There was one workaround (none of the others worked for me): I created a custom thumb, and then I used it in scroll style in xaml as a PART_Track finger.

 public class DragableThumb : Thumb { double m_originalOffset; double m_originalDistance; int m_touchID; /// <summary> /// Get the parent scrollviewer, if any /// </summary> /// <returns>Scroll viewer or null</returns> ScrollViewer GetScrollViewer() { if (TemplatedParent is ScrollBar && ((ScrollBar)TemplatedParent).TemplatedParent is ScrollViewer) { return ((ScrollViewer)((ScrollBar)TemplatedParent).TemplatedParent); } return null; } /// <summary> /// Begin thumb drag /// </summary> /// <param name="e">Event arguments</param> protected override void OnTouchDown(TouchEventArgs e) { ScrollViewer scrollViewer; base.OnTouchDown(e); m_touchID = e.TouchDevice.Id; if ((scrollViewer = GetScrollViewer()) != null) { m_originalOffset = scrollViewer.HorizontalOffset; m_originalDistance = e.GetTouchPoint(scrollViewer).Position.X; } } /// <summary> /// Handle thumb delta /// </summary> /// <param name="e">Event arguments</param> protected override void OnTouchMove(TouchEventArgs e) { ScrollViewer scrollViewer; double actualDistance; base.OnTouchMove(e); if ((scrollViewer = GetScrollViewer()) != null && m_touchID == e.TouchDevice.Id) { actualDistance = e.GetTouchPoint(scrollViewer).Position.X; scrollViewer.ScrollToHorizontalOffset(m_originalOffset + (actualDistance - m_originalDistance) * scrollViewer.ExtentWidth / scrollViewer.ActualWidth); } } } 
+2
source

The following worked for me. For a long time I was looking for something that would work. I adapted this for touch from How to make WPF Slider Thumb follow the cursor from anywhere . This is a much simpler fix and avoids creating a custom slider / thumb control.

  <Slider TouchMove="OnTouchMove" IsMoveToPointEnabled="True"/> 

To do this, set IsMoveToPointEnable to true.

  private void Slider_OnTouchMove(object sender, TouchEventArgs e) { Slider slider = (Slider)sender; TouchPoint point = e.GetTouchPoint (slider ); double d = 1.0 / slider.ActualWidth * point.Position.X; int p = int(slider.Maximum * d); slider.Value = p; } 
0
source

It is beautiful and simple and works for me - although it is worth wrapping in a general function and expanding to handle the minimum value of the slider, since it may not be zero. What a pain to do though. There are many things in WPF that are cool, but many simple things require extra steps that can really be detrimental to performance.

0
source

Source: https://habr.com/ru/post/903004/


All Articles