I have a program with two WPF treeviews that allow dragging and dropping between the two. The problem is, it can be annoying to open / close items on the treeviews because moving the mouse just one pixel while holding the left mouse button triggers the drag / drop functionality. Is there some way to specify how far the mouse should move before it's considered a drag / drop?
There's a system parameter for this. If you have
Point down = {where mouse down event happened}
Point current = {position in the MouseMove eventargs}
then the mouse has moved the minimum drag distance if
Math.Abs(current.X - down.X) >= SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(current.Y - down.Y) >= SystemParameters.MinimumVerticalDragDistance
Just build a little buffer into your code that determines when the drag starts.
flag mouse down
on mouse move - check for mouse down.. if yes, check to see if its moved farther than whatever buffer you specify (3 pixels is probably good)
if it has, start the drag.
Following this article for Drag and Drop implementation, you would have to handle 2 mouse events in order to delay the dragging until the mouse has moved a certain distance. First, add a handler for PreviewMouseDown which stores the initial mouse position relative to your control. Don't use the MouseDown event because it is a bubbling event and may have been handled by a child control before reaching your control.
public class DraggableControl : UserControl
{
private Point? _initialMousePosition;
public DraggableControl()
{
PreviewMouseDown += OnPreviewMouseDown;
}
private void OnPreviewMouseDown(object sender, MouseButtonEventArgs e) {
_initialMousePosition = e.GetPosition(this);
}
Additionally, handle MouseMove to check the moved distance and eventually initiate the drag operation:
...
public DraggableControl()
{
...
MouseMove += OnMouseMove;
}
...
private void OnMouseMove(object sender, MouseEventArgs e)
{
// Calculate distance between inital and updated mouse position
var movedDistance = (_initialMousePosition - e.GetPosition(this)).Length;
if (movedDistance > yourThreshold)
{
DragDrop.DoDragDrop(...);
}
}
}
Related
I thought I would improve my program by adding granular scrolling, hence I am studying up on the mouse wheel delta.
I have a new Logitech mouse with smooth scrolling enabled. Testing the scrolling on Chrome and I barely have to touch the scroll wheel and the page scrolls up one pixel. Nice!
I have a test program in c# with the MouseWheel event connected to a form with a few labels. Nothing fancy:
private void MainForm_MouseWheel(object sender, MouseEventArgs e)
{
int lines = e.Delta / 120;
if (lines > MaxLines)
MaxLines = lines;
else if (lines < MinLines)
MinLines = lines;
deltaResult.Text = e.Delta.ToString();
linesResult.Text = lines.ToString();
minLinesResult.Text = MinLines.ToString();
maxLinesResult.Text = MaxLines.ToString();
}
Trouble is this event only gets called when the delta reaches 120 (+/-). I understand that the mouse should be firing more events with lower value deltas, which it apparently works for Chrome, but for the test code, I have to move the scroll wheel considerably further before the event is fired.
I have come to the conclusion that either granular scrolling is not possible in C#; or that there is another event I should be calling; or it is something that needs to be enabled within the program; or that there is another method somewhere adding up the Deltas until they reach 120 and then firing the MouseWheel event.
My specific question:
How do I get the MouseWheel event to fire with only a minute rotation of the mouse wheel such as Chrome is obviously doing? If this is not possible, what event should I be calling?
I try to automate tests of a drag and drop behavior in a WPF application. One custom control is dragged on another:
Drag and drop behavior implemented in the usual WPF way:
<UserControl ...
MouseMove="ToolboxModule_OnMouseMove">
private void ToolboxModule_OnMouseMove(object sender, MouseEventArgs e)
{
base.OnMouseMove(e);
var data = new DataObject();
data.SetData("ModuleDescription", DataContext);
if (e.LeftButton == MouseButtonState.Pressed)
DragDrop.DoDragDrop(this, data, DragDropEffects.Copy);
}
<UserControl ...
Drop="WorkspaceView_OnDrop" AllowDrop="True">
private void WorkspaceView_OnDrop(object sender, DragEventArgs e)
{
var dropped = e.Data.GetData("ModuleDescription");
var viewModel = (WorkspaceViewModel)DataContext;
if (viewModel.ChainVm.AddModuleCommand.CanExecute(dropped))
viewModel.ChainVm.AddModuleCommand.Execute(dropped);
}
But when I try to automate this with WinAppDriver, the drag and drop does not work. Cursor shape is not changed and nothing happens.
I've tried several approaches:
Actions Drag and Drop
var moduleControl = mainWindow.GetToolboxModuleControl(moduleName);
var actions = new Actions(_session);
actions.DragAndDrop(moduleControl, mainWindow.WorkspaceControl).Perform();
Actions click and hold
var moduleControl = mainWindow.GetToolboxModuleControl(moduleName);
var actions = new Actions(_session);
actions.ClickAndHold(moduleControl).MoveByOffset(200, 0).Release().Perform();
Driver mouse operations (from example)
_session.Mouse.MouseMove(moduleControl.Coordinates, 50, 50);
_session.Mouse.MouseDown(null);
_session.Mouse.MouseMove(mainWindow.WorkspaceControl.Coordinates, 100, 100);
_session.Mouse.MouseUp(null);
Driver mouse operations with delays
_session.Mouse.MouseMove(moduleControl.Coordinates, 50, 50);
Thread.Sleep(1000);
_session.Mouse.MouseDown(null);
Thread.Sleep(1000);
_session.Mouse.MouseMove(mainWindow.WorkspaceControl.Coordinates, 100, 100);
Thread.Sleep(1000);
_session.Mouse.MouseUp(null);
Nothing works. Any ideas what could be wrong and how to fix it?
When I try to move the app window by dragging it's title bar via WinAppDriver, it successfully moves the window. So the dragging operations technically work, but not in the case of dragging a control within the window.
Found the answer.
WinAppDriver doesn't move the mouse, but "teleport" it. It means mouse cursor is not dragged all the way to the target with finite speed, it is being jumped from start to end position without any intermediate positions.
In this particular case it causes the problem. What actually happens:
Cursor teleported to the center of first element. MouseMove and
other mouse movement events are firing on first element.
DragDrop.DoDragDrop method is not executed, because left mouse
button is not pressed.
Left mouse button is pressed. MouseDown, Click and other related event are firing on first element. DragDrop.DoDragDrop method is
not executed, because there is no mouse movement.
Cursor jumped to second element without touching the first. MouseMove and other mouse movement events are firing on second
element only. Since no MouseMove event is firing on first element
with left mouse button pressed, the drag and drop process never
starts.
Solutions are simple: move the cursor within first element boundaries before jump to the second or change the event, where drag and drop process starts (MouseDown instead of MouseMove, for example). I chose the latter:
<UserControl ...
MouseDown="ToolboxModule_OnMouseDown">
private void ToolboxModule_OnMouseDown(object sender, MouseButtonEventArgs e)
{
base.OnMouseDown(e);
var data = new DataObject();
data.SetData("ModuleDescription", DataContext);
DragDrop.DoDragDrop(this, data, DragDropEffects.Copy);
}
I made Event for Grid in WPF C#.
The MouseMove Event.
I want to Trigger MouseMove event When mouse Left button is Pressed and Keep the Event even when mouse is Out of Grid Or even out of the main Window.
When Button is Pressed Keep the Mousemove event for the Grid All over the screen Until Button is Releasd.
consider this is Mouse move event Method for the Grid
private void Grid_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed) // Only When Left button is Pressed.
{
// Perform operations And Keep it Until mouse Button is Released.
}
}
The Goal is to Rotate 3D model When User Hold Left button and to rotate Model while he is moving the mouse until button releases.
This is for To make the Program and Rotation Eeasier for the user. Especialy performing Long rotations cause mouse get out of the grid.
I tried to use while but it fails and you know its because of single threaded.
so the way im thinking is to somehow expand a new Grid all over the screen when button is pressed inside the original Grid and to keep it until release.
And of course Dummy Grid witch is Hidden.
What are you trying to do is to work with the stream of events. As far as I understand your flow supposed to be the following:
Left mouse button pressed
Mouse moved1 (rotate model)
Mouse moved2 (rotate model)
...
N. Left mouse up (stop rotation)
There is an interesting concept which is called Reactive Programming.
http://rxwiki.wikidot.com/101samples
There is a library for C# (Reactive-Extensions)
Your code could look like this one:
// create event streams for mouse down/up/move using reflection
var mouseDown = from evt in Observable.FromEvent<MouseButtonEventArgs>(image, "MouseDown")
select evt.EventArgs.GetPosition(this);
var mouseUp = from evt in Observable.FromEvent<MouseButtonEventArgs>(image, "MouseUp")
select evt.EventArgs.GetPosition(this);
var mouseMove = from evt in Observable.FromEvent<MouseEventArgs>(image, "MouseMove")
select evt.EventArgs.GetPosition(this);
// between mouse down and mouse up events
// keep taking pairs of mouse move events and return the change in X, Y positions
// from one mouse move event to the next as a new stream
var q = from start in mouseDown
from pos in mouseMove.StartWith(start).TakeUntil(mouseUp)
.Let(mm => mm.Zip(mm.Skip(1), (prev, cur) =>
new { X = cur.X - prev.X, Y = cur.Y - prev.Y }))
select pos;
// subscribe to the stream of position changes and modify the Canvas.Left and Canvas.Top
// property of the image to achieve drag and drop effect!
q.ObserveOnDispatcher().Subscribe(value =>
{
//rotate your model here. The new mouse coordinates
//are stored in value object
RotateModel(value.X, value.Y);
});
Actually building a stream of mouse events is a very classical example of using RX.
http://theburningmonk.com/2010/02/linq-over-events-playing-with-the-rx-framework/
You can subscribe on this stream of events in the Windows constructor, so you don't depend on the Grid, and you don't have to draw fake Grid!
Some nice links to start with:
The Rx Framework by example
Rx. Introduction
I have a program which uses the mouse's .X position (relative to the form boundaries) to change the .Left value of a button Object.
The problem is that I have this button over the top of other objects like Picture Boxes, Buttons, TrackBar's etc. and when I hove over these other elements, the button stops tracking the mouse's .X position.
How can I make the button track the mouse movement regardless of the mouse doing other stuff on the form too? (I also need to interact with the other elements at the same time too).
My Code:
/* i create the mousemove tracking event */
this.MouseMove += new MouseEventHandler(btnBat1_MouseMove);
/* and use it by making a new method */
public void btnBat1_MouseMove(Object sender, MouseEventArgs e)
{
// I use the variable mouseXCo to change the button1.Left value.
mouseXCo = e.X;
}
Thanks in advance guys :-)
I have a Form that contains only 2 things, a PictureBox and a Label.
I added a mouse click event handler to the picture box.
this.pictureBox1.MouseClick += picture_MouseClick;
Inside the handler I need to check if the location of the mouse click is within the bounds of the label. To do this, I am using the mouse event location and checking to see whether that location is within the bounds of the label.
private void picture_MouseClick(object sender, MouseEventArgs e)
{
if (label1.Bounds.Contains(e.Location))
{
MessageBox.Show("FOUND YOU!");
}
}
I expected this to work as it seems easy enough however the click location (the orange box in the image) that leads to the MessageBox being shown is offset down and to the right of the label.
Is this because the mouse event is relative to the PictureBox and the Label bounds are relative to the Form? Or vice versa?
By the way, the label you see in the image is hidden at runtime. I am just using the label as a "hack" way of knowing if the user clicked in a certain spot.
public Form1()
{
InitializeComponent();
this.label1.Visible = false;
this.pictureBox1.MouseClick += picture_MouseClick;
}
(I tried subtracting the width of the label from e.X and the height of the label from e.Y but that didn't seem to work.)
Thank you,
Jan
The e.Location is the mouse position (a point) relative to the upper-left corner of the picturebox.
The Bounds property is relative to the container of the control.
(And in this case, the container is the form, as you and SLacks have rightly pointed out)
To check the correct position I will try with this code (now tested)
Point p = e.Location;
p.X += pictureBox1.Left;
p.Y += pictureBox1.Top;
if(label1.Bounds.Contains(p))
.....