Adding inertia for WPF touch application listbox - c#

I have a full screen WPF application built for a touch monitor, and I have some ListBoxes on the main screen.
When I flick the Listbox it scrolls fine, but when it gets to the end of the list, the entire application gets pulled down from the top of the screen, But I need the inertia just for the list box not for the entire window. How I can achieve that?

The ManipulationBoundaryFeedback event enables applications or
components to provide visual feedback when an object hits a boundary.
For example, the Window class handles the ManipulationBoundaryFeedback
event to cause the window to slightly move when its edge is
encountered.
So, a way around it is to handle ManipulationBoundaryFeedback on the ListBox, and set Handled to true:
<ListBox ManipulationBoundaryFeedback="OnManipulationBoundaryFeedback">
// ...
</ListBox>
Code-behind:
private void OnManipulationBoundaryFeedback(object sender, ManipulationBoundaryFeedbackEventArgs e)
{
e.Handled = true;
}

Related

Canceling the SpaceBar Click/Tap on uwp controls throughout the app

When a UI element ( control ) is focused in a uwp app it can be triggered with Spacebar or the Enter keys, this is not limited to Desktop but also helps in Xbox so user can navigate through the controls and press on any focused control to active its Command.
Use case
But in My use case I want only Enter key to trigger that behaviour and Spacebar should not do anything at all no matter which control is pressed on the screen.
The reason to this requirement is that I am building a MediaPlayer application and no matter which control or button is focused within the app when I press Spacebar I want to simply link it to the Play/Pause Behaviour of my media element.
Not a Duplicate
This question is not a duplicate of : UWP - Don't fire Click event when pressing space (like in Movies & TV app)
Because in the question linked above, the answer was only relevant if any of the AppBarButtons were focused so they will not do anything on pressing Space but only will be invoked with Enter. But in my use case I want to apply the same behavior even outside the MediaPlayerElement control. I have a NavigationView and MediaPlayerElement resides in one of the pages, so I want this behavior to work even when a NavigationViewItem is focused or any other control which can be focused and invoked should only be invoked with Enter and not Space.
Is there a app level solution where I can apply this behaviour at the very root control and it descends to all of its children i.e : whole app?
What I have tried
I have tried with the already answered question (linked above) and that works fine for its limited scenario. And I have also tried setting AllowFocusOnInteraction=false to every app bar button and also other extra controls I have in the style of my CustomMediaTransportControls. But this is also limited to MediaPlayerElement only and also it prevents tab navigation which is not good for accessibility.
You can do this by handling the PreviewKeyDown event higher in the visual tree hierarchy, for example in the Page.
Subscribe to the event in the Page constructor:
public MainPage()
{
this.InitializeComponent();
this.PreviewKeyDown += MainPage_PreviewKeyDown;
}
Or in XAML:
<Page ... PreviewKeyDown="MainPage_PreviewKeyDown">
And in the event handler set the KeyRoutedEventArgs to handled when the Space key was pressed:
private void MainPage_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Space)
{
e.Handled = true;
}
}
This way the key down event will never reach any control below in the hierarchy because the PreviewKeyDown event propagates the tree before the event takes place.
There are many ways could approach, You could listen the current Content PreviewKeyDown event to detect Space press.
public Scenario1()
{
this.InitializeComponent();
Window.Current.Content.PreviewKeyDown += Content_PreviewKeyDown;
}
private void Content_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
{
e.Handled = e.Key == VirtualKey.Space ? true : false;
}
You could also GLOBAL HOTKEY for your uwp app that could be used when your app's window not in foreground. For more derail you could check this blog .

UWP Xbox One page navigation when using WebView

Usually when running a UWP app on Xbox the B button on the controller is handled automatically and will return you to the previous page.
I have a page which contains a WebView, when you use the directional buttons to place the focus box around that control, the B button no longer responds. You can use the A button to take control of the WebView and display the pointer and the B button then will return focus back as above but I cannot navigate back using the B button until you move the focus box to a different control. This also happens using AdControl since this uses WebView.
I have tried to capture KeyDown:
Window.Current.CoreWindow.KeyDown += CoreWindow_KeyDown;
private void CoreWindow_KeyDown(Windows.UI.Core.CoreWindow sender, Windows.UI.Core.KeyEventArgs args)
{
Debug.WriteLine(args.VirtualKey.ToString());
}
This responds with GamePadB, GamePadX etc but not when the focus box is around the WebView.
Is there anyway I can find out when the GamePad buttons (specifically B) are pressed when the focus box is around the WebView (or AdControl) and the control isn't engaged so I can manually invoke the backstack navigation?
Since this issue happens when using the XY focus mode for the app, if your OS version is 14393 or higher, one workaround for this issue is to use the mouse mode for this page which contains the webview by setting RequiresPointer="WhenFocused" as following:
<Page RequiresPointer="WhenFocused">
...
</Page>
And set another page to XY focus mode by using the following code in the app.xaml.cs:
this.RequiresPointerMode =
Windows.UI.Xaml.ApplicationRequiresPointerMode.WhenRequested;
For more information, please try to refer to the following article:
https://msdn.microsoft.com/en-us/windows/uwp/input-and-devices/designing-for-tv#mouse-mode

Simulating a right click in wpf

I would like to simulate/route right click on a WPF "control".
To make a long story short. I have an Adorner which should react to left click (so is hit test visible must be true) but at the same time I would like it to be "transparent" for the right clicks. (In another words i would like for a control under it to receive this click - btw right click makes Adorner disappear).
I tried to raise MouseRightButtonUp event on control directly under mouse (after Adorner disappears but it doesn't seem to work). I would like to avoid calling system functions (like mouse_event through P/Invoke). Can it be even done in wpf?
As far as I remember, I had troubles with routing events and changing Adorners IsHitTestVisible property. The main problem was, if I recall it correctly, that adorner and controls are on different branches of visual tree, so routed events spawned on the adorner won't make it to your controls.
I can't say much without you providing the code, but the simplest thing that should work would be to find a control under your mouse position and do
private void myAdorner_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
MouseButtonEventArgs revent = new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, MouseButton.Right);
revent.RoutedEvent = e.RoutedEvent;
//find you control
control.RaiseEvent(revent);
}

Receiving touch events outside of custom control

I am developing a custom user control in WPF and trying to receive all touch events OUTSIDE of my control (so I can implement "cancel touches", where the user cancels an action by tapping somewhere else). Is there any good way to do this? Obviously, if I use TouchDown or other events I will only get notified of touches inside my control.
I tried receiving PreviewTouchDown events from Application.Current.MainWindow, but besides the fact it feels kind of dirty it also doesn't work that well - as soon as I capture those events, the SurfaceListBox inside my custom control stops working (it does not recognize touches anymore).
Is there a good way to solve this? Receiving simply ALL touch events would be sufficient as well, I can then check if they are inside my control.
In WPF, events travel the tree of elements. Events starting with "Preview" are the ones that travel up to bottom, meaning from the parent to the elements it contains. In your case, TouchDown event will travel from the control in which it originates and then it will bubble up towards the parents. PreviewTouchDown will travel in the opposite direction: from the parent towards the control.
Your idea of handling PreviewTouchDown event in the Application.Current.MainWindow is a good one and should handle all PreviewTouchDown events, if this is what you want. But in your case, if you want to check if the event originates in your custom control, you should catch all the TouchDown events in your MainWindow and check their Source property, like this:
private void CommonClickHandler(object sender, RoutedEventArgs e)
{
FrameworkElement feSource = e.Source as FrameworkElement;
if(feSource.Name == "MyCustomControlName")
{
//cancel whatever you want to cancel
e.Handled=true;
}
}
Do not catch the PreviewTouchDown event as this one will first pop in the MainWindow and just later will go all the way to your control.
This msdn link should give you more information: http://msdn.microsoft.com/en-us/library/ms742806.aspx

WPF create a list of controls that can be scrolled via the mouse but still remain functional

I have a list of controls that I am displaying via a WrapPanel and it horizontally oriented.
I have implemented a "Click and Drag" scrolling technique so that the user scrolls with the mouse via clicking and dragging.
Like so:
<Canvas x:Name="ParentCanvas" PreviewMouseDown="Canvas_MouseDown" MouseMove="Canvas_MouseMove">
<WrapPanel>
<WrapPanel.RenderTransform>
<TranslateTransform />
</WrapPanel.RenderTransform>
<!-- controls are all in here ... -->
</WrapPanel>
</Canvas>
Then in the code behind:
private Point _mousePosition;
private Point _lastMousePosition;
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
_lastMousePosition = e.GetPosition(ParentCanvas);
e.Handled = true;
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
_mousePosition = e.GetPosition(ParentCanvas);
var delta = _mousePosition - _lastMousePosition;
if(e.LeftButton == MouseButtonState.Pressed && delta.X != 0)
{
var transform = ((TranslateTransform)_wrapPanel.RenderTransform).Clone();
transform.X += delta.X;
_wrapPanel.RenderTransform = transform;
_lastMousePosition = _mousePosition;
}
}
This all works fine
But what I want to do is make it so that when a users clicks to drag, the items within the WrapPanel dont respond (i.e. the user is only browsing), but when the user clicks (as in a full click) then they do respond to the click.
Just like how the iphone works, when you press and drag directly on an app, it does not open the app, but rather scrolls the screen, but when you tap the app, it starts...
I hope this makes sense.
Cheers,
Mark
I believe you'll need to capture the mouse. The problem is you'll be contending with the controls (such as Button) that will also be trying to capture the mouse.
In your MouseDown event (probably PreviewMouseDown actually) you can use e.MouseDevice.Capture(_wrapPanel, CaptureMode.Element). This should direct all mouse input to the _wrapPanel and not any subtree elements.
In your MouseUp event, you'll need to release the capture by calling e.Mousedevice.Capture(null). If no scrolling has taken place you'll want to send a "click" to the control that normally would have received the click which I'm not quite sure about. Perhaps you can use the Automation Peer classes to do this?
The trick is that certain controls will not work properly if you withhold mouse events from them. Consider a slider for example. How would the slider ever be usable inside a panel that works like this?
Another, and in my opinion better, solution is to:
Add a PreviewMouseDown handler in which you set Handled=true and record the parameters including the position and set a "maybeClick" flag (unless your "recursion" flag is set), and sets a timer.
Add a MouseMove handler that clears the "maybeClick" flag if the mouse moves more than an epsilon away from the position recorded for the PreviewMouseDown.
Add a PreviewMouseUp handler that checks the "maybeClick" flag - if true, it sets the "recursion" flag, does an InputManager.ProcessInput to re-raise the original PreviewMouseDown, and then clears the "recursion" flag.
In the timer, do the same thing as for PreviewMouseUp so the click will only be delayed a moment.
The net effect is to delay a PreviewMouseDown event until you have had time to check whether the mouse moved or not.
Some things to note about this solution:
Setting Handled=true on PreviewMouseDownEvent also stops the MouseDownEvent.
The recursive call is ignored in the PreviewMouseDown handler because the recursion flag is set

Categories

Resources