C# WPF -- Mouse click spawning shape from the center of the click - c#

I'm working on a practice application that adds blue squares to a canvas when you click inside the canvas. One of the requirements is that the shape is added at that point with the mouse representing the center of the new shape.
By default, the click point of the mouse will be the top left of the square. Is there a way to make the square spawn from the center of the mouse click instead of the top right?
This is how i currently add my squares to the canvas:
private void canvasArea_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Shape Rendershape = null;
switch (Shape1)
{
case SelectedShape.Rectangle:
Rendershape = new Rectangle()
{
Fill = Brushes.Blue,
Height = num1,
Width = num2
};
break;
default:
return;
}
Canvas.SetLeft(Rendershape, e.GetPosition(canvasArea).X);
Canvas.SetTop(Rendershape, e.GetPosition(canvasArea).Y);
canvasArea.Children.Add(Rendershape);
}

All you need to do is shift the rectangle by half the rectangle's width and height, see following code:
Canvas.SetLeft(Rendershape, e.GetPosition(canvasArea).X - ( Rendershape.Width / 2.0 ) );
Canvas.SetTop(Rendershape, e.GetPosition(canvasArea).Y - ( Rendershape.Height / 2.0 ) );

Related

A selection-rectangle (drawn by mouse on canvas) does not recognize rectangles, which are inside that selection-rectangle (C# WPF)

I am now really desperate. I just can't get any further and in principle it shouldn't be that difficult. I tried solving it the last 2 weeks.
The following task/problem:
I have a canvas in my C# WPF project called “MarkPointsMap”, on which there are different kind of mark points (3 different kind of rectangles with different colors). One MarkPointsMap can contain round about 3000-4000 different MarkPoints (Children of that canvas) all over across the MarkPointsMap.
The positions of that MarkPoints are loaded from a .tab-file, which is converted into a list called “MarkPoints” and added as Children on the “MarkPointsMap” canvas after loading the respective .tab-file.
This a screenshot of the MarkPointsMap:
There are e.g. mostly small rectangles (the green ones), a few middle rectangles (the yellow ones) and two big rectangles (the red ones). Mulitple markpoints should be selected with a selection-rectangle. In my project I can already draw the selection rectangle, but the “if this.selectRect.Contains(MarkPointsMap.Children)” (see below) part does not work.
The selection-rectangle looks as follows:
The following method “drawMarkPoints”, which is called after loading the .tab file in the program, draws the markpoints on the canvas “MarkPointsMap”:
public void drawMarkPoints()
{
MarkPointsMap.Children.Clear();
foreach (MarkPoint markpoint in this.Marks.markpoints)
{
if (markpoint.type.relevant)
{
Rectangle markpointArea = new Rectangle();
markpointArea.Tag = markpoint;
//!!
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
mySolidColorBrush.Color = markpoint.fillColor;
markpointArea.Fill = mySolidColorBrush;
markpointArea.StrokeThickness = 0.2;
markpointArea.Stroke = Brushes.Black;
markpointArea.Width = markpoint.symbol.Width;
markpointArea.Height = markpoint.symbol.Height;
//set the markpoints in canvas
Canvas.SetLeft(markpointArea, getCanvasCoordinates(markpoint.center).X - 0.5 * markpoint.symbol.Width);
Canvas.SetTop(markpointArea, getCanvasCoordinates(markpoint.center).Y - 0.5 * markpoint.symbol.Height);
MarkPointsMap.Children.Add(markpointArea);
}
}
}
The selection-rectangle is drawn in the canvas “MarkPointsMap” with the three Mouse-Events (MarkPointsMap_MouseDown, MarkPointsMap_MouseMove, MarkPointsMap_MouseUp):
public Rect itemRect;
public Rect selectRect;
private point anchorPoint;
private Rectangle manualDragRect = new Rectangle();
private void MarkPointsMap_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
isLeftMouseButtonDownOnWindow = true;
anchorPoint = e.MouseDevice.GetPosition(MarkPointsMap);
MarkPointsMap.CaptureMouse();
e.Handled = true;
manualDragRect = new Rectangle
{
Stroke = Brushes.Red,
StrokeThickness = 2
};
MarkPointsMap.Children.Add(manualDragRect);
}
}
private void MarkPointsMap_MouseMove(object sender, MouseEventArgs e)
{
if (!MarkPointsMap.IsMouseCaptured)
return;
Point location = e.MouseDevice.GetPosition(MarkPointsMap);
double minX = Math.Min(location.X, anchorPoint.X);
double minY = Math.Min(location.Y, anchorPoint.Y);
double maxX = Math.Max(location.X, anchorPoint.X);
double maxY = Math.Max(location.Y, anchorPoint.Y);
Canvas.SetTop(manualDragRect, minY);
Canvas.SetLeft(manualDragRect, minX);
double height = maxY - minY;
double width = maxX - minX;
manualDragRect.Height = Math.Abs(height);
manualDragRect.Width = Math.Abs(width);
Point xy_2 = new Point((Canvas.GetLeft(manualDragRect) + 0.5 * width), (Canvas.GetTop(manualDragRect) + 0.5 * height));
this.selectRect = new Rect(xy_2.X, xy_2.Y, width, height);
}
private void MarkPointsMap _MouseUp(object sender, MouseButtonEventArgs e)
{
MarkPointsMap.ReleaseMouseCapture();
foreach (MarkPoint markpoint in this.Marks.markpoints)
{
Rect itemRect = new Rect(markpoint.center.X, markpoint.center.Y, markpoint.symbol.Width, markpoint.symbol.Height);
if (selectRect.Contains(itemRect))
{
MessageBox.Show("Test!"); // it does not reach this code, if I circle several markpoints with the red selection-rectangle called “selectRect”
}
}
}
Why doesn’t this work? I guess it has to do with the converting from rectangle (System.Windows.Shapes using derictive) to struct Rect : IFormattable.
The “if (selectRect.Contains(itemRect)”, which is not reached should in perspective color all mark points, which are inside the selection-rectangle in red, then compute the Point (x-Coordinate, y-Coordinate) of the middle-point of the selection-rectangle and add this middle point back to the original .tab-file.
Any ideas or hints, how I could continue? Thanks in advance.
Best regards!
It looks like your calculations are wrong. You are offsetting the selectRect. You are centering it relative to the rendered manualDragRect. This way you are testing a totally different area than the one you have defined by the rendered selection bounds.
Additionally, you are capturing the mouse on the Canvas but release the capture on some different control. It should be released on the MarkPointsMap too. Also be careful with marking mouse events as handled. If the input handling is not part of a custom control, you should generally avoid it.
You should consider to use an ItemsControl with a Canvas as panel. You can then move the drawing to the markup and define a DataTemplate for the MarkPoint data.
I recommend to use the original Rect returned from the manualDragRect shape. This means you should remove the selectRect field and related computations:
private void MarkPointsMap_MouseUp(object sender, MouseButtonEventArgs e)
{
MarkPointsMap.ReleaseMouseCapture();
foreach (MarkPoint markpoint in this.Marks.markpoints)
{
Rect itemRect = new Rect(markpoint.center.X, markpoint.center.Y, markpoint.symbol.Width, markpoint.symbol.Height);
Rect selectionBounds = manualDragRect.RenderedGeometry.Bounds;
selectionBounds.Offset(Canvas.GetLeft(manualDragRect), Canvas.GetTop(manualDragRect));
if (selectionBounds.Contains(itemRect))
{
MessageBox.Show("Test!");
}
// TODO::Calculate center of selectionBounds
Point selectionBoundsCenter = ...;
}
}

How to get mouse coordinates between two points in a picture box

Can anyone help me on this?
I have a picture box with a image and this image have some coordinates.
My X starts at 60 and end at 135
My Y stats at 75 and end at 120
Because i have only the first and the last point, i want to calculate and see the coordinates when i mouse over my image.
I started with solving my first problem: i have to delimitate my start and my end.
So i tried a trackbar.
Im trying first get the current X position:
Set my picturebox at position x = 0;
Set my trackbar at position x = -10, so my first pin will be at position 0;
Set my tracbar size.x = picturebox.x + 20, so my last pin will be at end of picture box.
My trackbar have the current properties:
Minimum = 60, Maximum = 135;
Set a mouse move event in my picturebox:
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
double dblValue;
dblValue = ((double)e.X/ (double)trackBar1.Width) * (trackBar1.Maximum - trackBar1.Minimum);
dblValue = dblValue + 60;
trackBar1.Value = Convert.ToInt32(dblValue);
lblX.Text = dblValue.ToString();
}
It's almost working, but still not very accurate.
Anyone have some idea that may work?
I'm not exactly sure what it is you are trying to do, but if you are trying to get to the coordinates within the picturebox, there is a function on the pictureBox class called PointToClient(Point) that computes the location of the specified screen point into client coordinates. You can use the X and Y coordinates from the MouseEventArgs to create the Point object to pass to the function.
To clarify:
The X and Y properties of the MouseEventArgs in the MouseMove event are the screen coordinates (0,0) being the upper left corner of the screen.
Many controls like the PictureBox control include a PointToClient method that will convert the screen coordinates to the local control's coordinates, where (0,0) will be the upper left corner of the control.
So, for example, if your control is placed on the screen at location (60, 75) and has a bottom right coordinate of (135, 120). If your mouse is over the control, and is 10 pixels from the left and 20 pixels from the top, then the X and Y properties of the MouseEventArgs in the MouseMove event would be: X = 70 and Y = 95. If you convert these to the internal coordinates of the picturebox control using PointToClient, it will indicate that X = 10 and Y = 20.
Now, if you were wanting to have a TrackBar that displays a relative indication of where the X coordinate of the mouse is over some control, you would calculate it as follows:
// Set the minimum and maximum of the trackbar to 0 and 100 for a simple percentage.
trackBar1.Minimum = 0;
trackBar1.Maximum = 100;
// In the pictureBox1_MouseMove event have the following code:
trackBar1.Value = pictureBox1.PointToClient(new Point(e.X, e.Y)).X * 100 / pictureBox1.Width;
If you wanted to have the trackbar use screen coordinates to track the relative position of the X coordinate of the mouse over some control, you would calculate it as follows:
// Set the minimum and maximum values of the track bar to the screen coordinates of the
// control we want to track.
trackBar1.Minimum = pictureBox1.PointToScreen(0,0).X;
trackBar1.Maximum = pictureBox1.PointToScreen(pictureBox1.Width, 0).X;
// In the pictureBox1_MouseMove event have the following code:
trackBar1.Value = e.X;
If you wanted to have the trackbar use the internal coordinates of some control to track the internal position of the X coordinate of the mouse over that control, you would calculate it as follows:
// Set the minimum and maximum values of the track bar to zero and the width of the
// control we want to track.
trackBar1.Minimum = 0;
trackBar1.Maximum = pictureBox1.Width;
// In the pictureBox1_MouseMove event have the following code:
trackBar1.Value = pictureBox1.PointToClient(new Point(e.X, e.Y)).X;
// or - not recommended - read below.
trackBar1.Value = e.X - pictureBox1.Left;
Now, there is one caveat, and that is if you put controls inside other controls, like a panel inside a panel inside a panel, etc. then the 'world' coordinates of a control inside of another control are based upon their location within the parent control. That is why it is a good idea to use the internal coordinates of the control via PointToClient and screen coordinates from internal coordinates via PointToScreen because otherwise you would have to work your way up through all of the containers until you reached the screen, keeping track of Top and Left coordinates all the way.
I hope this answers your question.

Drawing multiple rectangles chokes my WPF app

I am working in my own editor for making WPF forms. My issue is that I am having the worst time selecting multiple controls (buttons, labels, etc) and dragging them smoothly across the main window. My application chokes when I try to drag, oh say, 20 selected buttons at the same time.
I found that the culprit is the fact that I am drawing multiple rectangles for each object as they are being dragged and this function is being called in the MouseMove event.
void ControlObjectControl_MouseMove(object sender, MouseEventArgs e)
{
if (newPosition != oldPosition)
{
DragSelection(newPosition);
}
}
private void DragSelection(Point newPosition)
{
foreach (FrameworkElement item in designer.SelectionService.CurrentSelection)
{
if (item is ObjectControl)
(item as ObjectControl).m_ParentControlObject.Position = new System.Drawing.Rectangle() { X = X, Y = Y, Width = (int)item.Width, Height = (int)item.Height };
//..There's code that calculates the item's position and dimensions
}
}
How do I make it to where it only draws the rectangle once and I am still able to see my selected object(s) move smoothly when I drag them?
I did something similar in my application except I used a TranslateTransform to move my elements. Each "frame" (every mouse move) that I was dragging, I got the position of the mouse and compared that to the previous position of the mouse. I would then set a new TranslateTransform X/Y values equal to the X/Y mouse position change and then would give that to the RenderTransform of each object I wanted to move. Something like:
void ControlObjectControl_MouseMove(object sender, MouseEventArgs e)
{
if (dragging)
{
// Get the change in Location
mouseLocation = Mouse.GetPosition();
Point deltaLocation = mouseLocation - previousLocation;
// Make a new transform
TranslateTransform transform = new TranslateTransform();
transform.X = deltaLocation.X;
transform.Y = deltaLocation.Y;
// Apply the transform
// foreach thing
thing.RenderTransform = transform;
// set previous location
previousLocation = mouseLocation;
}
}
Now your objects only get drawn once and only their positions get changed. Hope this helps

Drag Ovalshape across form

So I have a bit of a problem with moving Ovalshapes around a form. The goal is to have a circle move within the bounds of two other circles, one placed inside the other and the moving circle essentially moving around them.
I am having trouble moving the circle with the mouse. Whenever I click and hold the circle, the circle moves to the coordinates of the location I clicked on the circle, such that if I click in the middle of an Ovalshape of size 10, it would set the circle's location to (5,5).
Here is what I have:
public partial class Form1 : Form
{
int smallRadius;
int largeRadius;
int movingRadius;
int distanceFromCenterToLocation;
bool mouseDown;
Point movingCenter;
Point returnPoint;
public Form1()
{
mouseDown = false;
InitializeComponent();
smallRadius = (ovalShape1.Right - ovalShape1.Left) / 2;
largeRadius = (ovalShape2.Right - ovalShape2.Left) / 2;
Point center = new Point(ovalShape1.Left + smallRadius, ovalShape1.Top + smallRadius);
ovalShape3.Height = largeRadius - smallRadius;
ovalShape3.Width = largeRadius - smallRadius;
movingRadius = (ovalShape3.Right - ovalShape3.Left) / 2;
ovalShape3.Location = new Point(center.X - (movingRadius), center.Y - largeRadius);
movingCenter = new Point(ovalShape3.Left + movingRadius, ovalShape3.Top + movingRadius);
distanceFromCenterToLocation = Convert.ToInt32(Math.Sqrt(Math.Pow(movingRadius, 2.0) + Math.Pow(movingRadius, 2.0)));
int middleRadius = center.X - movingCenter.X;
}
private void ovalShape3_MouseUp(object sender, MouseEventArgs e)
{
mouseDown = false;
}
private void ovalShape3_MouseDown(object sender, MouseEventArgs e)
{
mouseDown = true;
}
private void ovalShape3_MouseMove(object sender, MouseEventArgs e)
{
if (mouseDown)
{
ovalShape3.Location = e.Location;
}
}
}
For some reason OvalShape isn't derived from Control and doesn't behave like a control.
When a control receives a mouse event the Location property of the MouseEventArgs holds the coordinates relative to the upper-left corner of the form. Shapes however receive the coordinates relative to their own upper-left corner.
When a mouse button is pressed over a control it will capture the mouse so that subsequent events are sent to the same control until you release the button. Shapes only receive mouse events if the mouse is over the shape no matter if a mouse button is pressed. Once you move the mouse to the top or left the shape doesn't receive any mouse events. Therefore the shape doesn't follow the mouse and also the MouseUp event is not handled.
All shapes in a form are embedded in a single control of type ShapeContainer. This is created automatically when you add a shape to a form in the Visual Studio designer. To get the expected behavior you need to find that ShapeContainer (probably called shapeContainer1) and handle its mouse events instead:
public Form1()
{
InitializeComponent();
// do your initialization
...
// assign the events to the ShapeContainer
// don't forget to remove the handlers from ovalShape3 in the designer!!!
ShapeContainer container = ovalShape3.Parent;
container.MouseUp += ovalShape3_MouseUp;
container.MouseDown += ovalShape3_MouseDown;
container.MouseMove += ovalShape3_MouseMove;
}

PictureBox zoom miscalculation

I am attempting to zoom in a picturebox using the mousewheel.
Using the following variables:
public static int offsetX = 0;
public static int offsetY = 0;
public static double scale = .05;
I draw a series of polygons to the picture box. However, I wanted the bottom left corner to refer to 0,, so I draw everything to the form with a -y. The drawn points have been affected by the above variables but the real points stay the same.
void pictureBox1_MouseWheel(object sender, MouseEventArgs e)
{
if (e.Delta > 0)
scale += .025;
else
scale -= .025;
pictureBox1.Invalidate();
}
When the mousewheel is moved forward I increase the scale variable and the box is refreshed. It is repainted using this code in the picturebox paint method:
Graphics g = e.Graphics;
foreach (Member m in activeTruss.members)
{
if (m.Visible == true)
{
Point[] pointArray = new Point[m.poly.Points.Count()];
int index = 0;
foreach (System.Windows.Point p in m.poly.Points)
{
pointArray[index].X = (int)((p.X + offsetX) * scale);
pointArray[index].Y = (int)-((p.Y + offsetY) * scale);
index++;
}
SolidBrush myBrush = new SolidBrush(m.color);
g.FillPolygon(myBrush, pointArray);
}
}
it zooms the correct amount, however it appears to zoom towards the upper left corner because the offsets stay the same. The opposite is true when zooming out. How should I edit the offsets as I turn the mouse wheel in order to zoom directly towards the point under my mouse?
If you want to center your zoom
double offsetX = scale/2
double offsetY = scale/2
Update
To answer your second part about flipped picture, it sounds like you are not making a distinction on the x,y of your picture vs the x,y screen coordinates. Usually, screen coordinates start on the top of the screen, so top left is 0,0, however when you draw your picture, your picture's 0,0 is on the bottom left so you use -y. What you want to do is to start drawing from 0,0 to 0,MAXY without the flip. For this implementation detail, your code would help alot hehe :)

Categories

Resources