dotnet / wpf

WPF is a .NET Core UI framework for building Windows desktop applications.
MIT License
7.11k stars 1.17k forks source link

Add Support for Pen Scrolling #5938

Open aquinn39 opened 2 years ago

aquinn39 commented 2 years ago

In a Windows 10 update years ago (I think 1709), pen behaviour was changed so that dragging the pen scrolls (like with a touch screen) in GDI apps, UWP apps and Microsoft Edge WebViews but was not updated for WPF. As such, pen scrolling does not scroll in WPF even if touch screen scrolling is enabled via ScrollViewer.PanningMode.

leonardomelosantos commented 2 years ago

Hi! I have same problem. Please, can anybody help?

SlimeNull commented 6 months ago

I tried a solution found online, which converts Stylus-related events into corresponding Touch events. However, in this case, although scrolling is possible, buttons and text boxes under ScrollViewer cannot respond.

SlimeNull commented 6 months ago

Wait a minute, I seem to have found the best solution! First, I found this on StackOverflow: How to enable scrolling with stylus in a WPF touchscreen application?

The answer here mentioned that Stylus events can be simulated as TouchEvent, and gave sample code on codeplex. Of course, the codeplex link there has expired, and the currently available link is this: Blake.NUI - CodePlex Archive

In the BlakeNUI source code mentioned in the answer, I found the logic that simulates the mouse as a touch, but there is no special processing in it, so if such code is used directly and applied to the ScrollViewer, it will cause the Button in the ScrollViewer to Unable to respond to mouse clicks.

Then I looked at the source code of Button. Its click logic mainly depends on whether Button can capture the mouse, so my final changes to the touch simulation were as follows:

  1. When the pen is pressed, execute the logic normally, but record the current pen position
  2. When the pen moves, compare the current pen position with the initial pen position. If the difference is greater than the specified tolerance value, let StylusDevice capture the current element, which is on the ScrollViewer
  3. When the pen is raised, report Touch up, and if any element was captured, release it

This logic guarantees:

  1. When the pen does not make large movements, the pen will not capture any elements. At this time, the button can capture the mouse. All logic is just like a normal mouse click, and the button can execute Click logic.
  2. When the pen makes a large movement, the pen will capture the current element, and the underlying principle of Stylus capture is mouse capture, so this logic can release the capture state of Button, so that even if the pen is still on the button, when it is lifted , the Click logic will not be triggered

The final effect is: You can drag and scroll the contents of the ScrollViewer normally with the pen, and you can also use the pen to click on elements inside it, such as buttons.

This is the final code I wrote: EleCho.WpfSuite - StylusTouchDevice.cs A sample app: WpfPenTest.zip

aquinn39 commented 6 months ago

Wait a minute, I seem to have found the best solution!

First, I found this on StackOverflow: How to enable scrolling with stylus in a WPF touchscreen application?

The answer here mentioned that Stylus events can be simulated as TouchEvent, and gave sample code on codeplex. Of course, the codeplex link there has expired, and the currently available link is this: Blake.NUI - CodePlex Archive

In the BlakeNUI source code mentioned in the answer, I found the logic that simulates the mouse as a touch, but there is no special processing in it, so if such code is used directly and applied to the ScrollViewer, it will cause the Button in the ScrollViewer to Unable to respond to mouse clicks.

Then I looked at the source code of Button. Its click logic mainly depends on whether Button can capture the mouse, so my final changes to the touch simulation were as follows:

  1. When the pen is pressed, execute the logic normally, but record the current pen position

  2. When the pen moves, compare the current pen position with the initial pen position. If the difference is greater than the specified tolerance value, let StylusDevice capture the current element, which is on the ScrollViewer

  3. When the pen is raised, report Touch up, and if any element was captured, release it

This logic guarantees:

  1. When the pen does not make large movements, the pen will not capture any elements. At this time, the button can capture the mouse. All logic is just like a normal mouse click, and the button can execute Click logic.

  2. When the pen makes a large movement, the pen will capture the current element, and the underlying principle of Stylus capture is mouse capture, so this logic can release the capture state of Button, so that even if the pen is still on the button, when it is lifted , the Click logic will not be triggered

The final effect is:

You can drag and scroll the contents of the ScrollViewer normally with the pen, and you can also use the pen to click on elements inside it, such as buttons.

This is the final code I wrote: EleCho.WpfSuite - StylusTouchDevice.cs

A sample app: WpfPenTest.zip

I tried your demo. For the most part it works pretty well. Sometimes though, it will click the button instead of scrolling for some reason.

SlimeNull commented 6 months ago

Theoretically, this is impossible, at least I didn't encounter this situation when testing. What I encountered more often was that I clicked the button with the pen, but there was no response. This is because when clicking the button, the pen moved, and the Threshold was set to a very low value, so this operation was judged as dragging. So in the end, I also provided an option to adjust the Threshold in the encapsulated class. Can you provide information about this problem? Thanks.