I'm working with a WPF app, more specifically a Canvas with draggable elements.
Once an item is being dragged, I'd like to limit the scope of cursor movement to inside the canvas where the items are being dragged about.
The event which can start a drag is shown below
private void WidgetCanvas_PreviewHeaderLeftMouseDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
... logic to figure out if this is a valid drag, blah blah blah ...
this.IsDragging = true;
// TODO: clip the available cursor movement to actual width of Canvas
}
On the Preview-MouseUp, I'd like to simply "unclip" the cursor movement back to it's normal state.
I'll be monitoring the movement of the mouse once I start dragging (PreviewMouseMove), so worst case, I could manually check the position of the mouse and keep it constrained to the canvas, but that seems a little ugly.
Anyone have a better way to limit the cursor boundaries?
There's no clean way to do this and the not-so-clean ways will make your mouse cursor "jitter" at the border of the clipping area.
Moreover, I'd question if this is really a good idea. The user should really own the mouse and he or she generally gets frustrated when you try to artificially limit things that he or she owns.
If you want to provide feedback when the mouse leaves your canvas, maybe you could leave the item being dragged stuck along the border while the mouse button is still down? This would tell the user that he or she has left the target area without trying to impose limitations on where the mouse can go.
Good luck!
You should be able to do it using the ClipCursor native API.
Related
I am developing a WPF application, where I need to allow user to rearrange objects by drag-drop. It is basically dragging objects within a canvas area and moving them to another area of the canvas. The mouse cursor would change to a drag cursor while dragging is happening.
The requirement is like the mouse pointer cannot leave the canvas area while the dragging is in progress. For that I am using the Win32 ClipCursor api to restrict the cursor to a certain boundary of the screen.
This is working fine when mouse is used. However, if touch is used, i.e. if the pointer is moved through touch, it is possible to take the cursor out of the bounding rectangle. Can anyone please help to understand why it is happening? I am handling the PreviewMouseMove and MouseLeftButtonUp events for starting and ending the drag. Do I need to handle the Touch and the Stylus events as well?
Thanks!
How can I make it so that the mouse cursor within the game window so that the cursor won't, for example, go into the other screen if you're using dual screens? I do not want to use the Screen.lockCursor since I want to be able to use the mousepointer inside the game and not make it hidden and centered which you do with the lockCursor function.
This might be able to help you Cursor.lockState = CursorLockMode.Confined;
For more information look at the API at https://docs.unity3d.com/ScriptReference/Cursor-lockState.html
I'm working on a miniature golf game in XNA, I originally had everything in Game.cs (main), but I now want it to be more object-oriented, so I made separate class for most of my stuff.
When I had everything in Game.cs, it was working fine, now it doesn't.
What is happening is this:
When my cursor is at the top left corner of the game window, it's like X=200, Y=50.
It's supposed to be X=0, Y=0.
Even when I look for the 0, 0 position, it's way outside the game window.
Does anyone know what could be causing this?
How is that possible? Your cursor position is the mouse position. Simply draw the cursor there.
Unless you are talking about the Windows cursor. In that case, yes, the input data in XNA will not match the Windows cursor movement. They probably apply some modifiers for acceleration, etc. You have to interprete the input data yourself. In other words, draw your own cursor.
I have a Drag() method on form_MouseDown event. I also have a click event on the form. The problem is that if I click on the form, MouseDown event gets triggered and it never gets the chance to trigger click event.
What is the best way to solve this issue? I was thinking counting pixels if the form is actually dragged, but there has to be a better way. Any suggestions?
I was thinking counting pixels if the form is actually dragged, but there has to be a better way.
Nope, that's exactly how you have to do it.
This isn't just a software limitation; it's very much a practical one as well. If you think through the problem from a user's perspective, you'll immediately see the problem as well as the solution. Ask yourself, what is the difference between a click and a drag?
Both of them start with the mouse button going down over the object, but one of them ends with the mouse button going back up over the object in the same position and the other one ends with the mouse button going back up in a completely different position.
Since time machines haven't been perfected yet, you have no way of knowing this in advance.
So yes, you need to maintain some kind of a distance threshold, and if the pointer moves outside of that distance threshold while it is down over the object, then you consider it a drag. Otherwise, you consider it a click.
That distance threshold should not be 0. The user should not be required to hold the mouse completely still in order to initiate a click. A lot of users are sub-par mousers. They are very likely to twitch slightly when trying to click. If the threshold is 0, they'll end up doing a lot of inadvertent dragging when they try to click.
Of course, you don't actually have to worry about any of this or compute the drag threshold yourself. Instead, use the Windows default values, obtainable by calling the GetSystemMetrics function and specifying either SM_CXDRAG or SM_CYDRAG. (These might be exposed somewhere by the WinForms framework, but I don't think so. It's just as easy to P/Invoke them yourself.)
const int SM_CXDRAG = 68;
const int SM_CYDRAG = 69;
[DllImport("user32.dll")]
static extern int GetSystemMetrics(int index);
Point GetDragThreshold()
{
return new Point(GetSystemMetrics(SM_CXDRAG), GetSystemMetrics(SM_CYDRAG));
}
In the field of UX/UI, this sort of thing is called hysteresis or debouncing, by analogy to the use of these terms in physics and electronics.
I found this solution, although it is for a double-click and a mouse down events:
void pictureBox_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && e.Clicks ==1)
{
PictureBox pb = (PictureBox)sender;
DoDragDrop((ImageData)pb.Tag, DragDropEffects.Copy);
}
}
source: http://code.rawlinson.us/2007/04/c-dragdrop-and-doubleclick.html
Unfortunatelly, at the point of time when "button-is-pressed" you don't know yet if the desired action is just a click or a drag-drop. You will find it out it later.
For a click, the determinant is "no movement" and "button up".
For a drag, the determinant is "movement" and "button up".
Hence, to disambiguate those interactions, you have to track not only the buttons, but also the movement. You do not need to track the overall movement, only the movement between button-down and button-up is interesting.
Those events are therefore a good place to start/stop the Mouse.Capture mechanisms (to dynamically present drag adorners and drop location hints), or, in simplier form - to store the origin and target of movement vector and check if the distance is > D (even if movement occurred, there should be some safe minimal distance within which the DRAG is canceleed. The mouse is "jaggy" sometimes, and people would really don't like your app to start dragging when they double click at the end of fast mouse pointer movement :) )
I play with small but hopefuly nice app for WM6.
I have noticed that there is no MouseUp and MouseDown triggered when playing with stylus (acting as a mouse) on the screen.
That's ok for me, I mean I can live without it.
But there is something else happening that I cannot live with for a change.
When paint on the screen using stylus and read coordinates of the mouse every MouseMove event I get something (lets say for X axis) like: 2,4,6,8,10,12 etc. (every second one)
This same happens for Y axis.
In other words no matter how slow I move stylus I never get coordinates like 2,3,4,5,6 etc.
Using this coordinates to paint I don't get nice continuous line but rather separate dots.
Hopefully I have made myself clear enough.
ps. I forgot about one thing.
To paint on the screen I use pictureBox and bitmap attached to this pictureBox.
When paint on the bitmap I cannot see the evect on the screen unless I refresh the pictureBox or do something like pictureBox.Image = bitmap;
What should I do to see painted dots straight away without refreshing whole pictureBox?
[edit]
List<Point> array = new List<Point>(); // in the header of the class
private void pictureBox_Screen_MouseMove(object sender, MouseEventArgs e) //event handler body
{
array.Add(new Point(e.X, e.Y));
}
As you can see, it is very simple routine. I have deleted all unnecessary noise to make it more clear.
If your only problem is that you need to draw a line by painting each pixel that you catch in mouse move, just connect the dots and you should be set. Even in a "standard" app you will never get each point in MouseMove if the user moves the mouse quickly, so the fact that you don't get every coordinate with the stylus should not be a problem.