C# WPF Capture Keyboard and Mouse - c#

So I have a WPF Window that captures mouse events on an image. I am doing this by the following code:
<Image Name="imgPrimaryImage" Width="512" Height="512" RenderOptions.BitmapScalingMode="NearestNeighbor" Margin="5"
Source="{Binding Path=ImageMgr.ImageSource}"
MouseLeftButtonDown="OnMouseLeftButtonDown"
MouseMove="OnMouseMove"
MouseLeftButtonUp="OnMouseLeftButtonUp"/>
Application Functionality: While the user moves the mouse left and right it changes the size of the image so long as they have the left mouse button pressed.
Question: Is is possible to also capture keyboard events while capturing the mouse move event.
End Result: I want to be able to change the mouse speed based on CTRL and SHIFT pressed. I have the code I need to change the mouse speed, I am just wondering how I can get it so that if the user is holding CTRL while they left click and drag on the image it changes the speed.
If anyone has any insight on this (i.e. articles, literature, or advice) that would be excellent. Thank you and if there is any additional information needed please let me know.

To sum up comments if you want to check state of keyboard keys you can use Keyboard class which provides IsKeyDown method
var isShift = Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift);
var isCtrl = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl);
var isAlt = Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt);
or use its Modifiers property
var isShift = Keyboard.Modifiers.HasFlag(ModifierKeys.Shift);
var isCtrl = Keyboard.Modifiers.HasFlag(ModifierKeys.Control);
var isAlt = Keyboard.Modifiers.HasFlag(ModifierKeys.Alt);

Set boolean flags based on what keys are pressed in the key pressed event.
In the OnMouseMove record the mouse position if null. Otherwise calculate the distance traveled, and amplify it or dampen it based on the speed up or slow down flags you've set already.
To dampen or amplify, once you have the X and Y change from the last point, multiply by 2, or divide by 2... (you can choose your own numbers), now add the new YX change to the current mouse XY coordinates and set the mouse position.
Here is what the MouseMove would look like, and some of the private variables needed. In my Example you have to have Forms included as a reference. I did not include Forms in my Include statements, because it mucks up IntelliSense in WPF applications. You will still need to maintain those _speedUp and _slowDown variables with your KeyDown events
private bool entering = true;
private Point _previousPoint;
private bool _speedUp;
private bool _slowDown;
private double _speedMod = 2;
private double _slowMod = .5;
private void OnMouseMove(object sender, MouseEventArgs e)
{
Point curr = new Point(System.Windows.Forms.Cursor.Position.X, System.Windows.Forms.Cursor.Position.Y);
if (entering)
{
_previousPoint = curr;
entering = false;
}
if (_previousPoint == curr)
return; // The mouse hasn't really moved
Vector delta = curr - _previousPoint;
if (_slowDown && !_speedUp)
delta *= _slowMod;
else if (_speedUp && !_slowDown)
delta *= _speedMod;
else
{
_previousPoint = curr;
return; //no modifiers... lets not do anything
}
Point newPoint = _previousPoint + delta;
_previousPoint = newPoint;
//Set the point
System.Windows.Forms.Cursor.Position = new System.Drawing.Point((int)newPoint.X, (int)newPoint.Y);
}
EDIT: I put the key down events in my window definition, and it works just fine. Although as pointed out in the comments of this thread, using Keyboard.IsKeyDown is much simpler. I also edited the code above to not cause weird jumping issues
private void Window_KeyDown(object sender, KeyEventArgs e)
{
_slowDown = true;
if (e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl)
_slowDown = true;
else if (e.Key == Key.LeftShift || e.Key == Key.RightShift)
_speedUp = true;
}
private void Window_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl)
_slowDown = false;
else if (e.Key == Key.LeftShift || e.Key == Key.RightShift)
_speedUp = false;
}
private void Window_MouseLeave(object sender, MouseEventArgs e)
{
entering = true;
}

Related

Use Up/Down keys to scroll a ListBox when a TextBox has focus without moving cursor

I have a TextBox that a user can type a search term into and a ListBox that displays results. There is also a button will display some information based on the item selected on click.
I'm trying to scroll through the listbox using the up and down arrow keys so the user doesn't have to click the item, then the button. At that point I might as well just rely on the double click event to do the work since they are already on the item. However, I'm trying to make this more "keyboard only friendly".
The following code works, but with one minor flaw:
private void txtSearchTerm_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Down && Results.SelectedIndex < (Results.Items.Count - 1))
{
Results.SelectedIndex++;
}
else if (e.KeyCode == Keys.Up && Results.SelectedIndex > 0)
{
Results.SelectedIndex--;
}
}
With this code, the cursor still moves left and right along with the selected item changing. I want it to remain where it is (not forcing it to the end). I didn't have any luck with the txtSearchTerm.Select(...) event, but I guess I could have missed something...
There is a TextChanged event, but it only calls to a search function I wrote that populates the list box as the user types, so I will leave that code out for simplicity.
Am I missing something or overlooking some method to make this TextBox/ListBox combo function how I'm intending?
Quick note: If you've ever used UltraEdit, I'm trying to mimic the behavior of that configuration window, basically.
You should use e.Handled = true; to cancel using the key that you processed:
private void txtSearchTerm_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Down)
{
if (Results.SelectedIndex < (Results.Items.Count - 1))
Results.SelectedIndex++;
e.Handled = true;
}
else if (e.KeyCode == Keys.Up)
{
if (Results.SelectedIndex > 0)
Results.SelectedIndex--;
e.Handled = true;
}
}
I set e.Handled = true; if the key is Keys.Down or Keys.Up regardless of the SelectedIndex to completely disable moving caret using those keys.

How can I get my cursor out of the textbox?

I have a pretty complex visual studio form where I have like 2 textboxes, and some other stuff I need for my project, but my problem is - I want to use arrow keys to do tasks (bind them like short-cuts) and I can do that only when I don't edit my textboxes or else I get stuck in them and even if I try to reset my cursor (click on the form) it stays in the textbox and I can only jump in between characters, not use my arrows like I binded them. So my two questions are -
How can I reset my cursor if I have previously selected a textbox?
How can I unbind my default arrow keys so they don't jump between characters and buttons?
Try adding a key event on the textboxes that catches the arrow key strokes and moves focus accordingly.
void inputField_KeyDown(object sender, KeyEventArgs e)
{
if ((e.KeyCode == Keys.Left) || (e.KeyCode == Keys.Right) || (e.KeyCode == Keys.Up) || (e.KeyCode == Keys.Down))
{
//nextControl.Focus();
}
}
Just be aware that this will then remove the ability to navigate between the characters in a textbox. I think that the numpad arrows might still work inside the textbox since they have a different keycodes, but I can't be sure right now.
Hi and Welcome to stackoverflow, I'm not sure i have a complete answere but I have a few thougth on the subject.
Hard to answeer without some code, to se how you turns off the curser i think?
why does it have to in the first place? seems to overcomlicate it abit ?
the way i whould go about this, it's to egnore the the muse curser all together at first, and make a on KeyDown event on the textbox that needs the to be able to shift to the next like this
private void yourControl_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Down || e.KeyData == Keys.Up)
{
// shift to next textbox maybe, to keep thing simple ?
}
}
though this will act wierd if you use a mulitlines textbox or other control that allready have these keys bind.
you can allso add a field boolean as a switch to enable the keys, that gets set on the textsboxes activation (thats what happens then you click it to write text) and it can be reused, then it look like this
class yourProgram
{
private bool fieldIskeysMode = false;
private void yourControl_KeyDown(object sender, KeyEventArgs e)
{
if (fieldIskeysMode) // if True
{
if (e.KeyData == Keys.Down || e.KeyData == Keys.Up)
{
// shift to next textbox maybe, to keep thing simple ?
}
if (e.KeyData == Keys.Escape) fieldIskeysMode = false; // one line set to falls on esc key
}
}
private void yourControl_Activate(object sender, KeyEventArgs e)
{
if (!fieldIskeysMode) // if not true
{
fieldIskeysMode = true;
}
}
}
hope this helps.

WPF how to get drag and drop to work with control key?

So I have drag and drop somewhat working in WPF. it works fine with the shift key.
However, if I use the ctrl key to start a drag operation the dodraganddrop method runs fine. But while I hold the ctrl key the cursor changes to a Drop Not Allowed Symbol and the I cannot drop the items until I let go of the control key. Why is this and how can I change it? I thought the cursor was determined by setting DragEventArgs.Effects to a DragDropEffect, but I have set it to DragDropEffectsMove and I still get the no drop allowed cursor.
private void NameListView_MouseMove(object sender, MouseEventArgs e)
{
// Get the current mouse position
Point mousePos = e.GetPosition(null);
Vector diff = NameListViewMouseDownStartPoint - mousePos;
if (e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)
)
{
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift) || Keyboard.IsKeyDown(Key.LeftCtrl)
|| Keyboard.IsKeyDown(Key.RightCtrl))
{
DataObject do1 = new DataObject("AddedItemsFormat", _GraphViewerViewModel.SelectedAddedItems);
DragDrop.DoDragDrop(NameListView, do1, DragDropEffects.Move);
}
}
}
private void NameListView_DragOver(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.Move;
}
// Moves the item to the location of the insertion mark.
private void NameListView_DragDrop(object sender, DragEventArgs e)
{
//Drop Code. This code will not run unless the user first lets go of the control key so he can drop here
}
Holding the Ctrl key on the keyboard during a drag and drop operation relates to copying the item and therefore you need to allow the DragDropEffects.Copy enumeration also. You can allow both operations by setting this in the DoDragDrop method:
DragDrop.DoDragDrop(NameListView, do1, DragDropEffects.Copy | DragDropEffects.Move);
You could also use this:
DragDrop.DoDragDrop(NameListView, do1, DragDropEffects.All);

WPF Fast Click Events become a Drag-Event

I have the problem, that fast clicks become a drag-event.
I use the preventMouseButtonDown event to get the start position:
private void previewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_pointStartDrag = e.GetPosition(null);
}
The previewMouseMove-event delivers the actual position. I compare the two points to trigger my drag-event.
Vector d = _pointStartDrag - e.GetPosition(null);
if( |d.x| > minDx || |d.y| > minDy)
//here is my drag-event
Whatever minDx/y is set, it is reached by fast clicking two different poinst on my gui.
I don't know what went wrong. Any ideas?
I just answered this problem a day or two ago, but I can't find the question now, so I'll just do it again. You can use the SystemParameters.MinimumHorizontalDragDistance and SystemParameters.MinimumVerticalDragDistance properties for this purpose:
private bool IsConfirmedDrag(Point point)
{
bool horizontalMovement = Math.Abs(point.X - dragStartPosition.X) >
SystemParameters.MinimumHorizontalDragDistance;
bool verticalMovement = Math.Abs(point.Y - dragStartPosition.Y) >
SystemParameters.MinimumVerticalDragDistance;
return (horizontalMovement | verticalMovement);
}
It is used like this:
private void DragSourcePreviewMouseMove(object sender, MouseEventArgs e)
{
if (isMouseDown && IsConfirmedDrag(e.GetPosition(sender as ListBox)))
{
isMouseDown = false;
...
// Start Drag operation
}
}

Capture mouse movement event only when Left Click is pressed

I need to update a control only whenever the mouse moves over it with the left mouse button pressed. I would normally simply check for the e.Button property, but it is unavailable in the MouseEnter event.
void MyControl_MouseEnter(object sender, EventArgs e)
{
// MouseEventArgs needed to check this
// if (e.Button == MouseButtons.Left)
// {
// update MyControl
// }
}
How would you accomplish this?
Use the static Control.MouseButtons property. For example:
private void panel1_MouseEnter(object sender, EventArgs e) {
if (Control.MouseButtons == MouseButtons.Left) {
// etc...
}
}
This is very difficult to get going, whatever the user clicks on to get the mouse button pressed is going to capture the mouse, preventing the control's MouseEnter event from firing. It is also UI that's completely undiscoverable to the user. Do consider a better mouse trap.
Here's one (crude) way to do it. It will change the form's title text to whatever mouse button was pressed as you dragged your mouse on to button1. You can reference Control.MouseButtons to see which button is in a pressed state. Here's some more info on MSDN.
public partial class Form1 : Form
{
MouseButtons _buttons;
public Form1()
{
InitializeComponent();
}
private void button1_MouseMove(object sender, MouseEventArgs e)
{
if (_buttons != System.Windows.Forms.MouseButtons.None)
{
this.Text = _buttons.ToString();
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
_buttons = Control.MouseButtons;
}
}
I found the answer in another question here on SO:
How can I detect a held down mouse button over a PictureBox?
You will need to use a message filter. Implement the PreFilterMessage of the IMessageFilter interface, and assign an instance using Application.AddMessageFilter.
You will have to interpret windows messages yourself... that is not kind of difficult, but it will require some work.
The implementation may look like this:
if (m.Msg == 0x200)
{
int x, y;
x = m.LParam.ToInt32() & 0xFFFF;
y = m.LParam.ToInt32() >> 16;
if ((m.WParam.ToInt32() & 2) != 0)
{
// here, the left mouse button is pressed, and you can use the coords
// and see if the mouse is over the control you want.
}
}
I just implemented something like this today, tested only in Chrome but works rather nicely. Basic concept is that you capture mousemove only between mousedown and mouseup, as follows:
var image = document.getElementById('my_image');
image.addEventListener('mousedown', function(e) {
e.currentTarget.addEventListener('mousemove', doMyStuff);
});
image.addEventListener('mouseup', function(e) {
e.currentTarget.removeEventListener('mousemove', doMyStuff);
});
function doMyStuff(e) {
// do what you want, the mouse is moving while the user is holding the button down
}
Private Sub Button1_MouseDown(sender As Object, e As MouseEventArgs) Handles
Button1.MouseDown
If Control.MouseButtons = MouseButtons.Left Then
Label1.Text = "Left"
ElseIf Control.MouseButtons = MouseButtons.Right Then
Label1.Text = "Right"
ElseIf Control.MouseButtons = MouseButtons.Middle Then
Label1.Text = "Middle"
Else
Label1.Text = "lelse"
End If
End Sub

Categories