I have a function that sets the location of a certain textbox to the location of mouse cursor whenever the dragover event is called.
private void DGVLogicSimView_DragOver(object sender, DragEventArgs e)
{
txtBoxDragPoint.Visible = true;
txtBoxDragPoint.BackColor = Color.LightSkyBlue;
txtBoxDragPoint.Location = new Point(e.X, e.Y);
e.Effect = DragDropEffects.Copy;
}
The above event works perfectly when the form is maximized. However, when the form is not maximized and located somewhere random in the desktop, the txtbox location gets all messed up.
I believe it is returning the mouse location relative to the form, not the screen. What is the best solution for this?
Yes, that's because the D+D events deliver the mouse position in screen coordinates, not in client coordinates. You'll need to map the position relative to the textbox' parent, like this:
txtBoxDragPoint.Location = txtBoxDragPoint.Parent.PointToClient(new Point(e.X, e.Y));
Coordinates are indeed not screen-relative.
You can try changing this:
txtBoxDragPoint.Location = new Point(e.X, e.Y);
to this (assuming DGVLogicSimView is the name of the control you're hovering)
txtBoxDragPoint.Location = new Point(DGVLogicSimView.Left + e.X, DGVLogicSimView.Top + e.Y);
Related
I have a DataGridView with 2 columns of file-names. I would like to emulate the Windows File Explorer 'Rename' context menu on these file-names. To that end, I created a simple WinForms dialog with no header and a textbox entry for renaming. I display it when a grid's file-name cell is right-clicked. I am trying to position it directly over the cell, but have been unable to get it to display in the correct location. It's down by several rows and to the right by a few character widths. I'm positioning the dialog thusly:
Point location;
void dataGridView_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e) {
var cellRect = dataGridView.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false);
// Point location = dataGridView.Location;
location = dataGridView.Bounds.Location;
location.Offset(cellRect.Location);
location = dataGridView.PointToScreen(location);
}
async void renameToolStripMenuItem_Click(object sender, EventArgs e) {
using (var rfd = new RenameFileDialog(fi)) {
// Lifted from designer
rfd.ControlBox = false;
rfd.Text = string.Empty;
rfd.formBorderStyle = FormBorderStyle.SizableToolWindow;
// Actually in method
rfd.StartPosition = FormStartPosition.Manual;
rfd.Location = location;
rfd.ShowDialog(dataGridView);
}
}
I suspect I'm getting tripped up by Location vs ClientRectangle vs Control Bounds or margins or padding, but I haven't been able to identify where the undesired offsets are coming from. Can someone tell me how to position the dialog, or otherwise suggest a way to emulate Explorer's "Rename' in a dataGridView?
The original sin is here:
location = dataGridView.Bounds.Location;
to translate to Screen coordinates the origin point of a Control, using the Control itself as the relative reference, you have to consider its own origin, which is always (0, 0) (Point.Empty).
If you use its Location property, you instead consider the Control's offset in relation to its Parent.
If you then use this measure and call Control.PointToScreen(), you retrieve a position inside the Control's Client area
The offset of a location inside its ClientRectangle, added to this measure, is then of course moved right and down (since the Control's origin is not at (0, 0))
In other words, the Screen coordinates of the origin point of a Control are:
[Control].PointToScreen(Point.Empty);
As described in Open a Form under a DataGridView, you just need to consider the bounds of the Cell that raised the CellMouseDown event:
Point location = Point.Empty;
private void dataGridView_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
var dgv = sender as DataGridView;
var cellRect = dgv.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false);
location = dgv.RectangleToScreen(cellRect).Location;
}
As a note, in normal conditions, the coordinates that GetCellDisplayRectangle() returns are moved 7 pixels to the right and 1 pixel down, in relation to a Cell's grid, since it considers the internal bounds
If you want to position your Form over the Cell's grid, you could add:
location.Offset(-7, -1);
I'm working on a simple practice: Double click to create a textBox on mouse location (X,Y)
It is creating the object, but far from the mouse exact position.
private void DynamicObjects_MouseDoubleClick(object sender, MouseEventArgs e)
{
System.Windows.Forms.TextBox txtBox = new System.Windows.Forms.TextBox();
this.Controls.Add(txtBox);
txtBox.Top = MousePosition.Y;
txtBox.Left = MousePosition.X;
//txtBox.Location = MousePosition; --Still off away from Mouse real location
//txtBox.Location = MousePosition.Y; -- Fives erro Cannot implicitly convert Int to 'System.Drawing.Point'
}
Only way I've found to short of work is using .Top and .Left.
Why is creating far from mouse?
MousePosition gets the position of mouse cursor in screen coordinates.
We have to translate it to the client coordinates.
var textBox = new TextBox();
textBox.Location = PointToClient(MousePosition);
Controls.Add(textBox);
We need to call this method from the same control that we will place the TextBox on.
For example:
this.PointToClient
this.Controls.Add
or
panel.PointToClient
panel.Controls.Add
So im trying to get mouse coordinates from a click on an image, and it gives the wrong coordinates. When i move the mouse to draw, the line appears away from the cursor.
This is the code i use to get the mouse coordinates:
private void ponaredek_MouseDown(object sender, MouseButtonEventArgs e)
{
mouseDown = true;
//x1 = System.Windows.Forms.Control.MousePosition;
x1 = new System.Drawing.Point((int)e.GetPosition(this).X, (int)e.GetPosition(this).Y);
}
x1 is of type System.Drawing.Point (i need the point from drawing, to use in emgucv). What do i have to do to correct the cursor location (i drew where the cursor was)
You want to get the mouse position relative to the Image element, not the Window. So replace
e.GetPosition(this)
by
e.GetPosition((IInputElement)sender)
or
e.GetPosition(ponaredek)
if that is the Image element.
It should look like this:
var pos = e.GetPosition((IInputElement)sender);
x1 = new System.Drawing.Point(pos.X, pos.Y);
Also make sure the Image element's Stretch property is set to None.
I've done a mini test program to prototype a way to drag and drop an image (at the right)(using a button as a support) to a panel (left part)(The current panel background letter are only for test purpose) and to move it inside the panel perimeter.
The image on the moving control is manually drawn during the Paint event :
void bouton_Paint( object sender, PaintEventArgs e )
{
Button but = sender as Button;
Bitmap img = new Bitmap(WindowsFormsApplication1.Properties.Resources.triaxe);
img.MakeTransparent(Color.White);
e.Graphics.DrawImage(img, 0, 0, but.Width, but.Height);
}
As you can see below, during the moving process, the background of the moved control is frezzed. So it is not very pretty
Is it to make the progress smoother ?
Thank you.
This is the code to move my triaxe on the form :
void bouton_MouseUp( object sender, MouseEventArgs e )
{
Button button = sender as Button;
button.Tag = false;
button.BackColor = Color.Transparent;
}
void bouton_MouseMove( object sender, MouseEventArgs e )
{
Button button = sender as Button;
if (button.Tag != null && (bool)button.Tag == true)
{
button.Left += e.X - MouseDownLocation.X;
button.Top += e.Y - MouseDownLocation.Y;
}
}
private Point MouseDownLocation;
void bouton_MouseDown( object sender, MouseEventArgs e )
{
Button button = sender as Button;
MouseDownLocation = e.Location;
button.Tag = true;
button.BackColor = SystemColors.Control;
}
Let's assume you do not want to draw the graphics alone but do want a Control with transparency move on top of another Control.
You have to solve two problems:
Winforms doesn't support transparency well; you have two options:
Either let the system fake it for you
Or do the faking yourself.
For the first it is enough to have a control with a transparent backcolor and an Image or BackgroundImage (depending on the control type) with transparency and then nest the moving control in the background control.
For a simplistic test you can add if (button.Parent != panel1) button.Parent = panel1; to your mousedown code. This will look jumpy but should display transparency correctly. (Don't forget to make the Backcolor transparent; in your code your make it SystemColors.Control.
To fake it yourself you would do exactly what the system does:
You get the target suface in a bitmaps (with DrawToBitmap) and then combine the area the mover currently covers with the image; here getting that area is key.
Then you can set that image as backgroundimage or draw it onto the mover.
The other problem is with the moving code. Here the issue is that when you move the mouse the MouseMove event is triggered and you can move the control. But by moving it the mouse will have moved relativly to the new location and the event will be triggered again leading to flicker an jumpiness!
To avoid this you can test the numbers or use a flag or unregister the move event before setting the location and re-register afterwards.
Also setting the location in one go instead of the two coordinates is a good idea..: button.Location = new Point(button.Left + e.X - MouseDownLocation.X, button.Top + e.Y - MouseDownLocation.Y);
Imo you should still consider drawing just the graphics on On panel.MouseMove and panel.Paint. On panel.MouseUp you can still add a Button to the panel.Controls right there. Both issues are avoided and you are free to add the functionality you want as well..
In my program I added this code so when I move my mouse all over the screen I will get the mouse cursor coordinates in real time:
Form1 Load:
private void Form1_Load(object sender, EventArgs e)
{
System.Windows.Forms.Timer t1 = new System.Windows.Forms.Timer();
t1.Interval = 50;
t1.Tick += new EventHandler(timer1_Tick);
t1.Enabled = true;;
}
Then the method that get the mouse position:
public static Point GetMousePosition()
{
var position = System.Windows.Forms.Cursor.Position;
return new Point(position.X, position.Y);
}
Then the timer1 tick event:
private void timer1_Tick(object sender, EventArgs e)
{
label1.Text = string.Format("X={0}, Y={1}", GetMousePosition().X, GetMousePosition().Y);
}
Then I ran some application and moved the mouse over a specific location on the screen where the application window is and I found this coordinates:
358, 913
Now I have in my program a listBox with items each item present application screenshot. And if I click on the pictureBox for example in this case on the BATTLEFIELD 3 area I get the mouse cursor coordinates according to the pictureBox area.
So I did:
Point screenCoordinates;
Point pictureBoxSnapCoordinates;
private void pictureBoxSnap_MouseDown(object sender, MouseEventArgs e)
{
screenCoordinates = pictureBoxSnap.PointToScreen(e.Location);
pictureBoxSnapCoordinates = e.Location;
}
Now when I click in the pictureBox at the same location as I found the coordinates 358, 913 but on the pictureBox so the results are:
screenCoordinates 435, 724
pictureBoxSnapCoordinates 23,423
The screenCoordinates isn't the same coordinates as I found with the mouse move 358, 913 it's not even close. There is a big difference between 358,913 and 437,724
e.Location is relative to the Control's top left corner. If you want to use e.Location to get the screen coordinates, then you have to first do pictureBoxSnap.PointToScreen(Point.Empty); and then offset by the e.Location.
Also, Cursor.Position returns a Point object, so making a new Point(...) is pointless.
I must add, if you are dealing with images, and you need to interact with mouse, and do any task related with offset, scroll, etc, I recommend you that library, it is open source and have a lot of examples and methods that will help you
https://github.com/cyotek/Cyotek.Windows.Forms.ImageBox