Scrolling with touch not working in WPF - c#

I have a WPF application and over a part of it I had a card which displayed Javascript. My problem was that I wasn't able to scroll with the mouse over the Javascript card, because it was swallowing the events. I solved that by hooking into the PreviewMouseWheel event (described here) sent by UIElement:
protected void AddListeners(BorderedCanvas borderedCanvas)
{
borderedCanvas.PreviewMouseWheel += PreviewMouseWheel;
}
protected void RemoveListeners(BorderedCanvas borderedCanvas)
{
borderedCanvas.PreviewMouseWheel -= PreviewMouseWheel;
}
protected static void PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
if (!e.Handled)
{
e.Handled = true;
var eventArg =
new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)
{
RoutedEvent = UIElement.MouseWheelEvent,
Source = sender
};
var parent = VisualTreeHelper.GetParent((UIElement)sender) as UIElement;
parent?.RaiseEvent(eventArg);
}
}
Now, scrolling with the mouse works perfectly fine.
However, scrolling with touch doesn't work.
So, I modified AddListeners:
protected void AddListeners(BorderedCanvas borderedCanvas)
{
borderedCanvas.PreviewMouseWheel += PreviewMouseWheel;
borderedCanvas.PreviewTouchDown += new EventHandler<TouchEventArgs>(OnTouchDown);
borderedCanvas.PreviewTouchMove += new EventHandler<TouchEventArgs>(OnTouchMove);
}
And I added these methods:
protected static void OnTouchDown(object sender, TouchEventArgs e)
{
if (!e.Handled)
{
e.Handled = true;
var eventArg =
new TouchEventArgs(e.TouchDevice, e.Timestamp)
{
RoutedEvent = UIElement.TouchDownEvent,
Source = sender
};
var parent = VisualTreeHelper.GetParent((UIElement)sender) as UIElement;
parent?.RaiseEvent(eventArg);
}
}
protected static void OnTouchMove(object sender, TouchEventArgs e)
{
if (!e.Handled)
{
e.Handled = true;
var eventArg =
new TouchEventArgs(e.TouchDevice, e.Timestamp)
{
RoutedEvent = UIElement.TouchMoveEvent,
Source = sender
};
var parent = VisualTreeHelper.GetParent((UIElement)sender) as UIElement;
parent?.RaiseEvent(eventArg);
}
}
However, scrolling with touch still doesn't work.
Any idea what I did wrong?
To me it seems that I treated the mouse scroll and the touch scroll in a similar way, but the first works and the second doesn't.

You need to set the ScrollViewer.PanningMode for it to work with the touch.
<ScrollViewer PanningMode="Both"/>
The default value for the PanningMode is None.

Related

Get and Select the ListBoxItem clicked from ListBox_PreviewMouseLeftButtonUp event

I have a ListBox that slides its ScrollViewer horizontally by dragging with the left mouse button pressed.
private ScrollViewer scrollViewer;
private Point scrollMousePoint = new Point();
private double horizontalOffset = 1;
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Border border = (Border)VisualTreeHelper.GetChild(myListBox, 0);
scrollViewer = (ScrollViewer)VisualTreeHelper.GetChild(border, 0);
// This works
}
private void myListBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
scrollMousePoint = e.GetPosition(scrollViewer);
horizontalOffset = scrollViewer.HorizontalOffset;
scrollViewer.CaptureMouse();
// This works
}
private void myListBox_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (scrollViewer.IsMouseCaptured)
scrollViewer.ScrollToHorizontalOffset(horizontalOffset + (scrollMousePoint.X - e.GetPosition(scrollViewer).X));
// This works
}
private void myListBox_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
scrollViewer.ReleaseMouseCapture();
if (scrollMousePoint == Mouse.GetPosition(scrollViewer))
{
// Click
// Here I want to get and select the ListBoxItem that was pressed.
}
}
I want to get and select the ListBoxItem that was clicked.
To determine if it is a click without movement, I compare if the position saved in the PreviewMouseLeftButtonDown and in PreviewMouseLeftButtonUp events are the same.
Here you go:
private void myListBox_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
scrollViewer.ReleaseMouseCapture();
if (scrollMousePoint == Mouse.GetPosition(scrollViewer))
{
ListBox listBox = sender as ListBox;
if (listBox != null)
{
var element = VisualTreeHelper.HitTest(listBox, scrollMousePoint).VisualHit;
if (element.GetType() != typeof(ScrollViewer))
{
while (element.GetType() != typeof(ListBoxItem))
element = VisualTreeHelper.GetParent(element);
(element as ListBoxItem).IsSelected = true;
}
}
}
}

How to keep a control within its parent control while dragging it?

So I have a control named UI_TileInformation which needs to be kept inside its parent container (a canvas), while allowing the user to drag it freely. This solution was helpful in getting it to function, it now drags perfectly fine. However, it can be dragged outside of the bounds of its container. This solution was no help as I do not see where the _transform variable comes from.
The control has the following default variables:
protected bool isDragging;
private Point clickPosition;
As well as these event handlers:
this.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(Control_MouseLeftButtonDown);
this.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(Control_MouseLeftButtonUp);
this.PreviewMouseMove += new MouseEventHandler(Control_MouseMove);
I am fairly new to c# and WPF but I figured out that those event handlers would only work with when previewed (PreviewMouseMove as opposed to MouseMove)
As for the code that makes it drag, it is similar to the first link above:
private void Control_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
isDragging = true;
var draggableControl = sender as UI_TileInformation;
clickPosition = e.GetPosition(this);
draggableControl.CaptureMouse();
}
private void Control_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
isDragging = false;
var draggableControl = sender as UI_TileInformation;
draggableControl.ReleaseMouseCapture();
}
private void Control_MouseMove(object sender, MouseEventArgs e)
{
var draggableControl = sender as UI_TileInformation;
if (isDragging && draggableControl != null)
{
Point positionInUIElement = e.GetPosition(this);
var transform = draggableControl.RenderTransform as TranslateTransform;
if (transform == null)
{
transform = new TranslateTransform();
draggableControl.RenderTransform = transform;
}
transform.X = positionInCanvas.X - clickPosition.X;
transform.Y = positionInCanvas.Y - clickPosition.Y;
}
}
How can I get my control to stay inside the canvas?

WPF DragMove() causes issues

I'm trying to figure out if there's an elegant solution to the problem I've been faced with.
So basically, I designed a borderless loading splash screen which is completely movable via dragging. I find that this happens if the splash screen gets hidden via Hide(), then displays a window via ShowDialog() with the owner set to the splash screen. Things get extremely buggy, but only if you're in mid-drag (with left mouse button down). You become unable to click or move anything, even Visual Studio becomes unresponsive unless you explicitly alt-tab out of the application.
Considering I know when I'm going to spawn the window, I was thinking maybe there'd be a way to cancel the DragMove operation, but I'm having no luck. What I've figured out is that DragMove is synchronous, so I'd guess it'd have to be cancelled from a different thread or in an event callback.
Edit:
public partial class Window_Movable : Window
{
public Window_Movable()
{
InitializeComponent();
}
public Boolean CanMove { get; set; } = true;
private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (CanMove == false)
return;
Task.Factory.StartNew(() => {
System.Threading.Thread.Sleep(1000);
Dispatcher.Invoke(() => {
Hide();
new Window_Movable() {
Title = "MOVABLE 2",
CanMove = false,
Owner = this
}.ShowDialog();
});
});
DragMove();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine("DING");
}
}
I had the same problem and found out that DragMove() method is a problem.
https://groups.google.com/forum/#!topic/wpf-disciples/7OcuXrf2whc
To solve I decided to refuse from using it and implement moving logic.
I've combined some solutions from
https://www.codeproject.com/Questions/284995/DragMove-problem-help-pls
and
C# WPF Move the window
private bool _inDrag;
private Point _anchorPoint;
private bool _iscaptured;
private void AppWindowWindowOnMouseMove(object sender, MouseEventArgs e)
{
if (!_inDrag)
return;
if (!_iscaptured)
{
CaptureMouse();
_iscaptured = true;
}
var mousePosition = e.GetPosition(this);
var mousePositionAbs = new Point
{
X = Convert.ToInt16(_appWindowWindow.Left) + mousePosition.X,
Y = Convert.ToInt16(_appWindowWindow.Top) + mousePosition.Y
};
_appWindowWindow.Left = _appWindowWindow.Left + (mousePositionAbs.X - _anchorPoint.X);
_appWindowWindow.Top = _appWindowWindow.Top + (mousePositionAbs.Y - _anchorPoint.Y);
_anchorPoint = mousePositionAbs;
}
private void AppWindowWindowOnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_inDrag)
{
_inDrag = false;
_iscaptured = false;
ReleaseMouseCapture();
}
}
private void AppWindowWindowOnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_anchorPoint = e.GetPosition(this);
_anchorPoint.Y = Convert.ToInt16(_appWindowWindow.Top) + _anchorPoint.Y;
_anchorPoint.X = Convert.ToInt16(_appWindowWindow.Left) + _anchorPoint.X;
_inDrag = true;
}
I've spend all yesterday evening and find a more hacky but more functional solution. Which support Maximized state and not require manual coordinate calculation.
private bool _mRestoreForDragMove;
private void OnAppWindowWindowOnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 2)
{
if (_appWindowWindow.ResizeMode != ResizeMode.CanResize &&
_appWindowWindow.ResizeMode != ResizeMode.CanResizeWithGrip)
{
return;
}
_appWindowWindow.WindowState = _appWindowWindow.WindowState == WindowState.Maximized
? WindowState.Normal
: WindowState.Maximized;
}
else
{
_mRestoreForDragMove = _appWindowWindow.WindowState == WindowState.Maximized;
SafeDragMoveCall(e);
}
}
private void SafeDragMoveCall(MouseEventArgs e)
{
Task.Delay(100).ContinueWith(_ =>
{
Dispatcher.BeginInvoke((Action)
delegate
{
if (Mouse.LeftButton == MouseButtonState.Pressed)
{
_appWindowWindow.DragMove();
RaiseEvent(new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, MouseButton.Left)
{
RoutedEvent = MouseLeftButtonUpEvent
});
}
});
});
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (_mRestoreForDragMove)
{
_mRestoreForDragMove = false;
var point = PointToScreen(e.MouseDevice.GetPosition(this));
_appWindowWindow.Left = point.X - (_appWindowWindow.RestoreBounds.Width * 0.5);
_appWindowWindow.Top = point.Y;
_appWindowWindow.WindowState = WindowState.Normal;
_appWindowWindow.DragMove();
SafeDragMoveCall(e);
}
}
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_mRestoreForDragMove = false;
}
The thing is to send LeftMouseButtonUp event after a short delay to avoid blocking from DragMove()
Sources for last solve:
DragMove() and Maximize
C# WPF - DragMove and click
And one more completely different solution. To make a window movable you can use CaptionHeight of WindowChrome.
i.e.
var windowChrome =
WindowChrome.GetWindowChrome(appWindow.Window);
windowChrome.CaptionHeight = MainWindowToolbar.Height;
WindowChrome.SetWindowChrome(appWindow.Window, windowChrome);
but you should also set attached property WindowChrome.IsHitTestVisibleInChrome="True" for all controls in the window.

WinRT, C#, canvas and children inertial scrolling

Situation: WinRT application, canvas on a main page. The canvas has a number of children. When user taps on canvas and moves pointer, I’m trying to scroll them. All works fine, but I don’t know how to emulate inertial scrolling.
The code:
private GestureRecognizer gr = new GestureRecognizer();
public MainPage()
{
this.InitializeComponent();
gr.GestureSettings = GestureSettings.ManipulationTranslateInertia;
gr.AutoProcessInertia = true;
}
I’ve subscribed to some canvas events:
//Pressed
private void Canvas_PointerPressed(object sender, PointerRoutedEventArgs e)
{
if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Touch)
{
var _ps = e.GetIntermediatePoints(cnvMain);
if (_ps != null && _ps.Count > 0)
{
gr.ProcessDownEvent(_ps[0]);
e.Handled = true;
Debug.WriteLine("Pressed");
}
initialPoint = e.GetCurrentPoint(cnvMain).Position.X;
}
}
//Released
private void Canvas_PointerReleased(object sender, PointerRoutedEventArgs e)
{
if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Touch)
{
var _ps = e.GetIntermediatePoints(cnvMain);
if (_ps != null && _ps.Count > 0)
{
gr.ProcessUpEvent(_ps[0]);
e.Handled = true;
Debug.WriteLine("Released");
}
}
// Moved
private void Canvas_PointerMoved(object sender, PointerRoutedEventArgs e)
{
if (gr.IsActive || gr.IsInertial)
{
gr.ProcessMoveEvents(e.GetIntermediatePoints(null));
// Here is my code for translation of children
e.Handled = true;
}
}
So, I can translate canvas children, but there is no inertia. How can I enable it?
Unfortunately, I can't use something like GridView or ListView in this app because of specific data.
You should use the GestureRecognizer with ManipulationInertiaStarting. This should give you enough information to implement inertial scrolling.

Auto highlight text in a textbox control

How do you auto highlight text in a textbox control when the control gains focus.
In Windows Forms and WPF:
textbox.SelectionStart = 0;
textbox.SelectionLength = textbox.Text.Length;
If you want to do it for your whole WPF application you can do the following:
- In the file App.xaml.cs
protected override void OnStartup(StartupEventArgs e)
{
//works for tab into textbox
EventManager.RegisterClassHandler(typeof(TextBox),
TextBox.GotFocusEvent,
new RoutedEventHandler(TextBox_GotFocus));
//works for click textbox
EventManager.RegisterClassHandler(typeof(Window),
Window.GotMouseCaptureEvent,
new RoutedEventHandler(Window_MouseCapture));
base.OnStartup(e);
}
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
(sender as TextBox).SelectAll();
}
private void Window_MouseCapture(object sender, RoutedEventArgs e)
{
var textBox = e.OriginalSource as TextBox;
if (textBox != null)
textBox.SelectAll();
}
In ASP.NET:
textbox.Attributes.Add("onfocus","this.select();");
It is very easy to achieve with built in method SelectAll
Simply cou can write this:
txtTextBox.Focus();
txtTextBox.SelectAll();
And everything in textBox will be selected :)
If your intention is to get the text in the textbox highlighted on a mouse click you can make it simple by adding:
this.textBox1.Click += new System.EventHandler(textBox1_Click);
in:
partial class Form1
{
private void InitializeComponent()
{
}
}
where textBox1 is the name of the relevant textbox located in Form1
And then create the method definition:
void textBox1_Click(object sender, System.EventArgs e)
{
textBox1.SelectAll();
}
in:
public partial class Form1 : Form
{
}
I think the easiest way is using TextBox.SelectAll like in an Enter event:
private void TextBox_Enter(object sender, EventArgs e)
{
((TextBox)sender).SelectAll();
}
Here's the code I've been using. It requires adding the attached property to each textbox you wish to auto select. Seeing as I don't want every textbox in my application to do this, this was the best solution to me.
public class AutoSelectAll
{
public static bool GetIsEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsEnabledProperty);
}
public static void SetIsEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsEnabledProperty, value);
}
static void ue_Loaded(object sender, RoutedEventArgs e)
{
var ue = sender as FrameworkElement;
if (ue == null)
return;
ue.GotFocus += ue_GotFocus;
ue.GotMouseCapture += ue_GotMouseCapture;
}
private static void ue_Unloaded(object sender, RoutedEventArgs e)
{
var ue = sender as FrameworkElement;
if (ue == null)
return;
//ue.Unloaded -= ue_Unloaded;
ue.GotFocus -= ue_GotFocus;
ue.GotMouseCapture -= ue_GotMouseCapture;
}
static void ue_GotFocus(object sender, RoutedEventArgs e)
{
if (sender is TextBox)
{
(sender as TextBox).SelectAll();
}
e.Handled = true;
}
static void ue_GotMouseCapture(object sender, MouseEventArgs e)
{
if (sender is TextBox)
{
(sender as TextBox).SelectAll();
}
e.Handled = true;
}
public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached("IsEnabled", typeof(bool),
typeof(AutoSelectAll), new UIPropertyMetadata(false, IsEnabledChanged));
static void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ue = d as FrameworkElement;
if (ue == null)
return;
if ((bool)e.NewValue)
{
ue.Unloaded += ue_Unloaded;
ue.Loaded += ue_Loaded;
}
}
}
The main change I made here was adding a loaded event to many of the examples I've seen. This allows the code to continue working after it's unloaded (ie. a tab is changed). Also I included code to make sure the text gets selected if you click on the textbox with the mouse, and not just keyboard focus it. Note: If you actually click on the text in the textbox, the cursor is inserted between the letters as it should.
You can use this by including the following tag in your xaml.
<TextBox
Text="{Binding Property}"
Library:AutoSelectAll.IsEnabled="True" />
If you need to do this for a large number of textboxes (in Silverlight or WPF), then you can use the technique used in the blog post: http://dnchannel.blogspot.com/2010/01/silverlight-3-auto-select-text-in.html. It uses Attached Properties and Routed Events.
You can use this, pithy. :D
TextBox1.Focus();
TextBox1.Select(0, TextBox1.Text.Length);
If you wanted to only select all the text when the user first clicks in the box, and then let them click in the middle of the text if they want, this is the code I ended up using.
Just handling the FocusEnter event doesn't work, because the Click event comes afterwards, and overrides the selection if you SelectAll() in the Focus event.
private bool isFirstTimeEntering;
private void textBox_Enter(object sender, EventArgs e)
{
isFirstTimeEntering = true;
}
private void textBox_Click(object sender, EventArgs e)
{
switch (isFirstTimeEntering)
{
case true:
isFirstTimeEntering = false;
break;
case false:
return;
}
textBox.SelectAll();
textBox.SelectionStart = 0;
textBox.SelectionLength = textBox.Text.Length;
}
if you want to select all on "On_Enter Event" this won't Help you achieving your goal.
Try using "On_Click Event"
private void textBox_Click(object sender, EventArgs e)
{
textBox.Focus();
textBox.SelectAll();
}
On events "Enter" (for example: press Tab key) or "First Click" all text will be selected. dotNET 4.0
public static class TbHelper
{
// Method for use
public static void SelectAllTextOnEnter(TextBox Tb)
{
Tb.Enter += new EventHandler(Tb_Enter);
Tb.Click += new EventHandler(Tb_Click);
}
private static TextBox LastTb;
private static void Tb_Enter(object sender, EventArgs e)
{
var Tb = (TextBox)sender;
Tb.SelectAll();
LastTb = Tb;
}
private static void Tb_Click(object sender, EventArgs e)
{
var Tb = (TextBox)sender;
if (LastTb == Tb)
{
Tb.SelectAll();
LastTb = null;
}
}
}
I don't know why nobody mentioned that but you can also do this, it works for me
textbox.Select(0, textbox.Text.Length)
textBoxX1.Focus();
this.ActiveControl = textBoxX1;
textBoxX1.SelectAll();
In window form c#. If you use Enter event it will not work. try to use MouseUp event
bool FlagEntered;
private void textBox1_MouseUp(object sender, MouseEventArgs e)
{
if ((sender as TextBox).SelectedText == "" && !FlagEntered)
{
(sender as TextBox).SelectAll();
FlagEntered = true;
}
}
private void textBox1_Leave(object sender, EventArgs e)
{
FlagEntered = false;
}
textbox.Focus();
textbox.SelectionStart = 0;
textbox.SelectionLength = textbox.Text.Length;

Categories

Resources