I am working on a Form that takes and draws a point on a mouse click. I am confused about how to properly get and add the scrolling offset so that the points can be drawn correctly. For example, right now when I add a point where the upper left coordinate is (0,0), the point redraws itself and moves with the scrolling action instead of staying at the spot it was originally created at. I have set
this.AutoScroll = true
and have set the minimum size manually
this.AutoScrollMinsSize = new Size(800,600);
Here is what my mouse click event looks like so far:
if (e.Button == MouseButtons.Left)
{
Point newPoint = new Point(e.X, e.Y);
p.X += this.AutoScrollOffset.X;
p.Y += this.AutoScrollOffset.Y;
this.Invalidate();
}
What is the proper way to use the AutoScrollOffset property to keep the points where they belong instead of moving as I scroll?
I should add that my program also overrides the Scroll event to repaint when a scroll event occurs to fix the problem of a drawing disappearing once the visible area was left.
AutoScrollOffset is not the correct property to use. It has very limited use, it can apply an offset to the scroll position when the ScrollControlIntoView() method is used. Which is pretty rare, never once used it myself.
You need to use the AutoScrollPosition property instead:
if (e.Button == MouseButtons.Left) {
var newPoint = new Point(e.X - this.AutoScrollPosition.X,
e.Y - this.AutoScrollPosition.Y);
// etc..
}
Note that substraction is required, a bit unintuitive.
Related
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))
.....
I think I have miss understand the Invalidate method...I am trying to draw a square with the top left hand corner of the square at the location of the mouse at mousedown and then the bottom right hand corner is the current location of the mouse. Below is the method triggered on the MouseMove event. The parent is a panel with a pictureBox child. (I am trying to draw on top of these.)
The problem seems to be with pictureBoxMain.Invalidate(). When commented out the code behaves as expected and draws a gazillion squares.
[The Graphics g is created by the pictureBox, hence why I call the Invalidate method on the pictureBox.]
When I un-comment out the invalidate line then a box is draw as the mouse moves but as soon as it stops moving the box disappears. I can't work out for the life of me why. When I attempt to debug the code it appears that MouseMove method is being invoked in when the mouse isn't moving, which doesn't make any sense.
Any help is greatly appreciated!
private void pictureBoxMain_MouseMove(object sender, MouseEventArgs e)
{
if (MouseDrawLeft)
{
//Move
}
else if (MouseDrawRight)
{
MouseLast = e.Location;
if (MouseFirst != MouseLast)
{
pictureBoxMain.Invalidate();
Point bl = new Point(MouseFirst.X, MouseLast.Y);
Point tr = new Point(MouseLast.X, MouseFirst.Y);
g.DrawLine(pen, MouseFirst, tr);
g.DrawLine(pen, MouseFirst, bl);
g.DrawLine(pen, bl, MouseLast);
g.DrawLine(pen, tr, MouseLast);
}
}
}
Every component (button, textbox, window...) has its Paint method. This is invoked periodically by Windows (like 50x per second) to draw the object.
What you do is that you paint something on the object - but within a milisecond, it disappears, because the Paint method overpainted it. You need to override the Paint method of the frame and do you paintings there - this way, you drawings will be painted every time Windows ask.
I'm kind of new to WPF although I have some experience in Forms, and I decided to finally try to figure out how to use WPF. So When I got to draggable controls, this is the code I came up with (I attempted to change it to work with WPF but the control just twitches everywhere):
private void rectangle1_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed) {
double x = this.Left + (double)e.GetPosition(this).X - (double)rectangle1.Margin.Left;
double y = this.Top + (double)e.GetPosition(this).Y - (double)rectangle1.Margin.Top;
rectangle1.Margin = new Thickness(x, y, rectangle1.Margin.Right, rectangle1.Margin.Bottom);
}
}
You want to use adorners to achieve dragging, resizing, rotating, etc.
Alternative:
Install NuGet package: Microsoft.Xaml.Behaviors.Wpf
Add this to root element:
xmlns:behaviors="http://schemas.microsoft.com/xaml/behaviors"
And just place this inside your element:
<Grid>
<behaviors:Interaction.Behaviors>
<behaviors:MouseDragElementBehavior ConstrainToParentBounds="True"/>
</behaviors:Interaction.Behaviors>
</Grid>
If you want to do it by hands use following algorithm:
On MouseDown event: Save Mouse position, TopLeft position of control, and delta(offset) of these coordinates, and set some boolean field flag eg. IsDragStartted to true.
On MouseMove check that drag started and use Mouse position and offset to calculate the new value of TopLeft position of control
On MouseUp event set IsDragStartted to false
here is a pretty good article on the matter on MSDN. also, a quick search on Google revealse a veritable cornucopia of choices for you DnD dining pleasure.
I am needing to make a disabled and invisible panel enabled and visible when a button is pressed inside a webbrowser. The idea is to force the user to make an input on a complaints/compliments board. When said input is made, the close button (actually a panel so as to not steal focus from the webbrowser. It's a touch screen application) will appear.
The only way I feel i can make it work (with my limited programming knowledge), is to make said button appear when the "write" button is pressed. Knowing the position of the "write" button on the webbrowers relative to the form, if I could use a click event handler, I think i can make it work, but my code isn't working =/
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
Point point = new Point();
point.X = e.Location.X - Location.X;
point.Y = e.Location.Y - Location.Y;
if (point.X <= 724 && point.X>=670 && point.Y <= 367 && point.Y >= 338)
{
panel3.Visible = true;
panel3.Enabled = true;
}
}
By using the Mouse move event, it seems as though when the mouse goes over the webbrower, the mouse move or click no longer applies to the event handler (i'm assuming it's because it's no longer on the form but on the web browser).
Please help me out!
Thank you
I was able to use a timer to detect location and when in a certain area, to make enable the panel.
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(...);
}
}
}