Simulate Mouse Drag Programmatically - c#

I have a Windows Forms application with a control. The control consists of a chart panel with a canvas on which I paint. What I would like to do is to programmatically mouse drag the panel so that I have a specific distance between the right edge of the canvas and the last item painted on the canvas. I have tried two approaches. The both work in the sense that the panel is dragged as desired BUT I cannot seem to be able to get the precision of movement I desire. I coded a mouse simulator and have tried two approaches.
Approach 1:
if(this.ChartControl.ChartPanel.CanFocus)
{
// ... Focus the chart panel to be adjusted.
this.ChartControl.ChartPanel.Focus();
// ... Move cursor to lastBarScreenCoordinates on the chart panel to be adjusted.
Cursor.Position = new Point(lastBarScreenCoordinates.X, lastBarScreenCoordinates.Y);
MouseSimulator.SetMouseDragThresholds();
// ... Move chart panel to required position.
MouseSimulator.LeftMouseButtonDown(lastBarScreenCoordinates.X, lastBarScreenCoordinates.Y);
MouseSimulator.MouseMove(lastBarScreenCoordinates.X-positionShift,
lastBarScreenCoordinates.Y);
MouseSimulator.LeftMouseButtonUp(lastBarScreenCoordinates.X-positionShift,
lastBarScreenCoordinates.Y);
MouseSimulator.ResetMouseDragThresholds(_cx_default, _cy_default);
// ... Redraw the chart panel.
this.ChartControl.ChartPanel.Refresh();
// ... Reset cursor to its starting position.
Cursor.Position = new Point(startingCursorX, startingCursorY);
}
Approach 2:
if(this.ChartControl.ChartPanel.CanFocus)
{
// ... Focus the chart panel to be adjusted.
this.ChartControl.ChartPanel.Focus();
// ... Move cursor to lastBarScreenCoordinates on the chart panel to be adjusted.
Cursor.Position = new Point(lastBarScreenCoordinates.X, lastBarScreenCoordinates.Y);
MouseSimulator.SetMouseDragThresholds();
// ... Move chart panel to required position.
MouseSimulator.LeftMouseButtonDown(lastBarScreenCoordinates.X, lastBarScreenCoordinates.Y);
Cursor.Position = new Point(lastBarScreenCoordinates.X-positionShift,
lastBarScreenCoordinates.Y);
WindowsCommunication.SendMessage(this.ChartControl.Handle, 0x200, IntPtr.Zero,IntPtr.Zero);
MouseSimulator.LeftMouseButtonUp(lastBarScreenCoordinates.X-positionShift,
lastBarScreenCoordinates.Y);
MouseSimulator.ResetMouseDragThresholds(_cx_default, _cy_default);
// ... Redraw the chart panel.
this.ChartControl.ChartPanel.Refresh();
// ... Reset cursor to its starting position.
Cursor.Position = new Point(startingCursorX, startingCursorY);
}
I am using SendInput for simulating mouse clicks. Here is sample left mouse button down code ...
public static void LeftMouseButtonDown(int x, int y)
{
INPUT mouseInput = new INPUT();
mouseInput.type = SendInputEventType.InputMouse;
mouseInput.mkhi.mi.dx = CalculateAbsoluteCoordinateX(x);
mouseInput.mkhi.mi.dy = CalculateAbsoluteCoordinateY(y);
mouseInput.mkhi.mi.mouseData = 0;
mouseInput.mkhi.mi.time = 0;
mouseInput.mkhi.mi.dwFlags = MouseEventFlags.MOUSEEVENTF_LEFTDOWN;
SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));
}
And I calculate normalized absolute coordinates for the mouse as follows ...
private static int CalculateAbsoluteCoordinateX(int x)
{
return ((x * 65536) + GetSystemMetrics(SystemMetric.SM_CXSCREEN) - 1) /
GetSystemMetrics(SystemMetric.SM_CXSCREEN);
}
So here are the precision issues. If I use Approach 1 (mouse move), the measured distance between the last item painted and the right edge of the canvas is different from what I set in positionShift and the cursor position difference does not equal positionShift. I initially thought it was due to pointer ballistics issues so I tried using Approach 2. Approach 2 does give me precision in pointer positioning but I am still having difficulty in that the panel moves but the distance between the last bar painted and the right edge of the canvas does not equal the positionShift amount as it should. It always seems to be off. I have been working on this for a long time now and am at my wits end. I am not sure what is going on here. How to improve the precision in my canvas drag by simulated mouse drag?

Well what you can do is this, First of all I believe the SendInput API allows for an AbsoluteValue flag so there is no need to calculate those values which may be the issue but most likely not.
Although I am curious as to why you are using a Mouse Drag opperation for this. It seems like all you want to do is reposition the canvas on every draw by some specified amount. if this is the case why not just set it explicitly on the canvas itself. Also it is unclear if you are using pure WinForms or WPF. The unclear bit being Canvas which I am fairly certain is only usable with WPF enabled windows.
That being said
WPF fix,
Depending on the Object containing the canvas just set its margin appropriately for the situation, since I do not know the data you are working with I cant say much about that. But this is a relatively simple idea so let me know if that works, it should give you, at least close too, a pixel perfect alignment.
WinForms,
Just do the above for the "Canvas" object you were talking about, or use absolute coordinates of the object to move it around.
If you could supply a sample of what you were working on looked like roughly maybe we could have a better idea of what you mean.

Related

Create a minimap but how to make both original panel and minimap panel coordinate in sync with each other?

This is an example of the form I have created. The minimap will show anything that I have drawn in the main area.
Notice the RED COLOR BOX in the minimap. The red color box will follow my mouse cursor once the mouse enter the minimap.
What I wanted to achieve is that I click on any part of the minimap that I wanted, the main form will scroll to the position that I clicked in the minimap.
Eg. https://docs.devexpress.com/WindowsForms/17894/Controls-and-Libraries/Map-Control/End-User-Features/Mini-Map
I used AutoScrollPosition but seems like something wrong with my calculation for coordinate. Any hints or help for calculating the coordinate? Thanks
public void moveTo(double x, double y)
{
/*Calculation for the coordinate*/
panel1.AutoScrollPosition = new Point((int)g, (int)f);
}

Chart is rescaling area for axis labels when long labels appear due to scrolling

I have some string labels that are associated with a number each.
I've created a Chart element with one ChartArea and one Series which is using the SeriesChartType.Bar type to show my labels on the x-axis (which, confusingly, is the vertical axis when using the Bar type, but anyway...) and show the number as a bar next to it.
Since I have many labels that don't fit on the screen at once, I "enabled" the scrollbar using the Zoom method in my Paint event handler like this:
private void chart1_Paint(object sender, PaintEventArgs e)
{
var scaleView = chart1.ChartAreas.First().AxisX.ScaleView;
var pos = double.IsNaN(scaleView.Position) ? 0.0 : scaleView.Position;
scaleView.Zoom(pos, pos + chart1.Height / 22.0);
}
I don't know if this is the proper way to do that, but it does (almost) what I want:
show the scrollbar if there are too many data points (labels) to fit on the screen
update the visible area properly when the window is resized
There is only one annoying thing: If due to scrolling a long label appears in the visible area or disappears, the area occupied by the labels is adjusted to the longest visible label. I hope these screenshots explain what I mean:
Here one long label is visible (at the bottom):
Here I scrolled up by one unit so that the long label is not visible any more:
This is super annoying during scrolling as everything gets rescaled whenver a long label appears or disappears.
How to fix the area occupied by the labels to always fit the longest label in the series, no matter if it is currently visible or not? I tried IsLabelAutoFit = false for both, x- and y-axis, but that doesn't help.
Ok, I've got it. I used
chartArea.InnerPlotPosition.Auto = false;
InnerPlotPosition.X = 33.333;
to give one third of the chart area to the labels and the other two thirds to the bars.
InnerPlotPosition.Auto = false makes this fixed so that it doesn't update while scrolling.

Textbox Positioning Issues On Form Bigger Than Resolution

I have this form that works perfectly fine on resolution 1920 x 1080 (mainly cause I made it using that resolution). I read information about controls from a database (labels and textboxs). This information tells me where to place the controls. Like I said, on 1920 x 1080 everything is placed correctly. When I go down resolutions the form is now bigger than the resolution so I added scroll bars. Issue is that the controls are being placed as if the form that is visable on the screen is all that there is. So if I were to place a textbox at location (4, 90) on the form on lower resolution it might place it at (100,90). Y coord is fine, X coord is not.
Although TableLayoutPanels looks very nice in this case it would be a complete hassle and a waste of time to basically completely redo what I have done already. If I were to have started fresh I probably would have used TableLayoutPanels. Instead what I did was, since the Form was bigger than the resolution, I turned my forms AutoScroll to true (which I said in the question). I then set the AutoScrollPosition to (0,0) on form_shown which makes the horizontal one go to the left and the vertical one to the top using...
this.AutoScrollPosition = new Point(0,0);
Since the controls information is based off of where they are on the screen I just force the form's top left to be located in the top left of the screen and not off. I also had to have an override for the ScrollToControl. Every time a control was placed it would reset back to the center and would mess the placement up. So I added this...
protected override Point ScrollToControl(Control activeControl)
{
return this.AutoScrollPosition;
}

Interact with drawings

I'm using bmp but I'm open to other suggestions since I found barely any info while searching for bmp interaction.
I finally plotted my drawing into a pictureBox
Everything is working properly, but when i click the drawing, I'd like to read where is the pointer on the bmp. If it is above the displaying graph, I would wanna take the X position out of it.
Edit:
Another not important but interesting question:
Is it possible to stick the mouse position whenever it is above the bmp, to move only above or below the graph function?
I've found my own answer, here :)
private void obterposicaonografico()
{
// Set the Current cursor, and display how many points in X axis you took
this.Cursor = new Cursor(Cursor.Current.Handle);
Cursor.Position = new Point(Cursor.Position.X, Cursor.Position.Y);
MessageBox.Show(Cursor.Position.X.ToString());
//Define it's position on X axis, subtracting the whole form X location
//This will ensure that if the user moves the form around, it will still be relative to the own form
//and not the windows positioning
decimal posicaoX = Cursor.Position.X -this.Location.X
MessageBox.Show(Cursor.Position.X.ToString());
}

Getting the top left coordinates of a WPF UIElement

I am working on extending the Microsoft resize Adorner example and need to be able to reposition the element after say the bottom left drag handle has been dragged.
So if I have a textbox of say 150 wide, 35 high postitioned on my form, and the bottom left drag handle changes the width to 200 wide, the right hand of the text box remains unchanged but the left hand edge moves to the left.
So I need to know the top left coordinates of the UIElement. I have tried Canvas.GetLeft and Canvas.GetTop but they return NaN which is confusing.
I just tried VisualTreeHelper.GetOffset which does return an offset but when you try and use it in the arrange method of the element it disappears, presumably as the values in the offset are too high.
In the days before Wpf the coordinate system was quite simple, wpf has overcomplicated things I think.
And if someone just wants the control's screen coordinates:
Point targetLoc = targetCtrl.PointToScreen(new Point(0, 0));
(this doesn't match the thread's description, but it does match the title. Figured it might help people coming in off search results)
You can transform coordinates of the UIElement to its parent. In your case it's a form. Here is an example of a method that returns coordinates of a visual:
private Point GetPosition(Visual element) {
var positionTransform = element.TransformToAncestor(MyForm);
var areaPosition = positionTransform.Transform(new Point(0, 0));
return areaPosition;
}

Categories