C# drag a snapping control - c#

I am creating a form that when a panel is dragged from a parent panel to another panel, the new panel becomes the parent. I created a version of the program, but I'm not sure what I'm missing. How would I add a timer to the dragged panel it so that it doesn't move immediately after clicking it, and properly create snapping bounds so that it snaps within a certain distance around the parent panel.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace SNAPtest
{
public partial class Form1 : Form
{
Point MousePoint = new Point();
public Form1()
{
InitializeComponent();
panel_white.MouseDown += new MouseEventHandler(White_MouseDown);
panel_white.MouseMove += new MouseEventHandler(White_MouseMove);
}
private void White_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
int mx = Math.Min(Math.Max(panel_white.Location.X - MousePoint.X + e.X, 0), panel_white.Parent.Width - panel_white.Width);
int my = Math.Min(Math.Max(panel_white.Location.Y - MousePoint.Y + e.Y, 0), panel_white.Parent.Height - panel_white.Height);
panel_white.Location = new Point(mx, my);
}
if (panel_white.Location.X == Math.Max(panel_maroon.Location.X, 10) | panel_white.Location.Y == Math.Max(panel_maroon.Location.Y, 100))
{
Console.WriteLine("Maroon has focus");
panel_white.Parent = panel_maroon;
}
if (panel_white.Location.X == Math.Max(panel_blue.Location.X, 10) | panel_white.Location.Y == Math.Max(panel_blue.Location.Y, 100))
{
Console.WriteLine("Blue has focus");
panel_white.Parent = panel_blue;
}
}
private void White_MouseDown(object sender, MouseEventArgs e)
{
panel_white.Parent = this;
panel_white.BringToFront();
if (e.Button == MouseButtons.Left)
{
MousePoint = e.Location;
}
}
}
}

Related

How does it go to its first position if I don't drag the button where I want to drag it?

enter image description hereI have 9 buttons. Three are located above, 3 are located below them. All 3 buttons are on the right side. The antonyms of the words written on the buttons above are written on the buttons on the right. I would like to place the buttons with these antonyms on the buttons at the bottom. The buttons at the bottom are colored and their text is the same as the buttons on the right, but their text is not visible because it is the same as the button color. So the buttons on the bottom look like a colored box. That's what I want to do now; for example, button 1 = cold and button7 = hot. I want to place this button 7 on button 4 located below button 1, but if I want to place it on button 5 I want it to return to its original position. In the code I wrote, as soon as I press button7, it goes directly to button4, I can't try the other buttons. how can I try other buttons and return them to the first position in Visual studio form application?
enter code here
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace materyalll
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
startlocation = new Point(button7.Left, button7.Top);
startlocation2 = new Point(button8.Left, button8.Top);
startlocation3 = new Point(button9.Left, button9.Top);
}
Point location, location2, location3, startlocation, startlocation2, startlocation3;
private void button7_MouseDown(object sender, MouseEventArgs e)
{
location = e.Location;
}
private void button7_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
button7.Left += e.X - (location.X);
button7.Top += e.Y - (location.Y);
}
}
private void button7_MouseUp(object sender, MouseEventArgs e)
{
if (button7.Text == button4.Text)
button7.Location = button4.Location;
else if (button7.Text != button5.Text)
button7.Location = startlocation;
else if (button7.Text != button6.Text)
button7.Location = startlocation;
}
private void button8_MouseDown(object sender, MouseEventArgs e)
{
location2 = e.Location;
}
private void button8_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
button8.Left += e.X - (location2.X);
button8.Top += e.Y - (location2.Y);
}
}
private void button8_MouseUp(object sender, MouseEventArgs e)
{
if (button8.Text == button5.Text)
button8.Location = button5.Location;
else if (button8.Text != button4.Text)
button8.Location = startlocation2;
else if (button8.Text != button6.Text)
button8.Location = startlocation2;
}
private void button9_MouseDown(object sender, MouseEventArgs e)
{
location3 = e.Location;
}
private void button9_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
button9.Left += e.X - (location3.X);
button9.Top += e.Y - (location3.Y);
}
}
private void button9_MouseUp(object sender, MouseEventArgs e)
{
if (button9.Text == button6.Text)
button9.Location = button6.Location;
else if (button9.Text != button4.Text)
button9.Location = startlocation3;
else if (button9.Text != button5.Text)
button9.Location = startlocation3;
}
}
}
Store the original location of the Button in the .Tag property of the Button itself. When the user releases the mouse, see if the RECTANGLES of the correct target button and the current button INTERSECT. If they don't, snap back to the stored location in the Tag; otherwise snap to the location of the correct button.
Here it is with button7:
public Form1()
{
InitializeComponent();
button7.Tag = button7.Location;
button8.Tag = button8.Location;
button9.Tag = button9.Location;
}
private void button7_MouseUp(object sender, MouseEventArgs e)
{
if (button7.Bounds.IntersectsWith(button4.Bounds)) {
button7.Location = button4.Location;
}
else
{
button7.Location = (Point)button7.Tag;
}
}
The code for the other two buttons would be very similar.
Try something like this:
Dictionary<string, Point> originalPoints = new Dictionary<string, Point>();
originalPoints.Add(nameof(button1), new Point(button1.Left, button1.Top));
// repeat this for all buttons
void SetToOriginalPosition(Button button)
{
Point p = originalPoints[nameof(button)];
button.Left = p.X;
button.Top = p.Y;
}

Removing a rectangle from imagebox

I borrowed some code to draw a rectangle on an image, e.g. like a selection box. Right now, the code draws a rectangle any time you click and drag your mouse. If you simply left-click without dragging, nothing at all happens - the existing rectangle stays put. If you click and drag a new rectangle, the old rectangle disappears.
That's almost exactly like I want (I'm not wanting to permanently draw on the image... yet...), but with one change: I'd like for a single left-click to make the rectangle disappear as well.
The code is as follows:
public partial class ScreenSelection : Form
{
private Point RectStartPoint;
private Rectangle Rect = new Rectangle();
private Brush selectionBrush = new SolidBrush(Color.FromArgb(128, 72, 145, 220));
public ScreenSelection(DataTable buttonData)
{
InitializeComponent();
}
private void Canvas_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
RectStartPoint = e.Location;
Invalidate();
}
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
return;
Point tempEndPoint = e.Location;
Rect.Location = new Point(
Math.Min(RectStartPoint.X, tempEndPoint.X),
Math.Min(RectStartPoint.Y, tempEndPoint.Y));
Rect.Size = new Size(
Math.Abs(RectStartPoint.X - tempEndPoint.X),
Math.Abs(RectStartPoint.Y - tempEndPoint.Y));
Canvas.Invalidate();
}
private void Canvas_Paint(object sender, PaintEventArgs e)
{
// Draw the rectangle...
if (Canvas.Image != null)
{
if (Rect != null && Rect.Width > 0 && Rect.Height > 0)
{
e.Graphics.FillRectangle(selectionBrush, Rect);
}
}
}
}
I also have the user load a bitmap in as the image of the canvas, so once the user does that, canvas.image won't equal null.
so how can I make that rectangle disappear on a left click? I'm already doing an invalidate on the left-click, and that's clearly not getting rid of it.
I tried forcing the rectangle size on a left-click by doing:
if (e.Button == MouseButtons.Left)
{
RectStartPoint = e.Location;
Rect.Height = 0;
Rect.Width = 0;
Invalidate();
}
and tried Rect.Size, Rect = Rectangle.Empty, Canvas.Refresh()...
How can I accomplish this?
Edit:
I've also tried saving the graphics state and restoring it. That doesn't work... (no errors, just not getting rid of the rectangle)
Finally found a way to do it where I keep the drawing inside the paint event to improve performance / remove flicker...
It all had to do with fillRectangles
here's the working code:
public partial class ScreenSelection : Form
{
private Point RectStartPoint;
private Rectangle Rect = new Rectangle();
private Brush selectionBrush = new SolidBrush(Color.FromArgb(128, 72, 145, 220));
private List<Rectangle> Rects = new List<Rectangle>();
private bool RectStart = false;
public ScreenSelection(DataTable buttonData)
{
InitializeComponent();
}
private void Canvas_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (RectStartPoint == e.Location)
{
int i = Rects.Count;
if (i > 0) { Rects.RemoveAt(i - 1); }
Canvas.Refresh();
}
}
}
private void Canvas_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
RectStartPoint = e.Location;
int i = Rects.Count;
if (i >= 1) { Rects.RemoveAt(i - 1); }
RectStart = false;
Canvas.Refresh();
}
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
return;
Point tempEndPoint = e.Location;
Rect.Location = new Point(
Math.Min(RectStartPoint.X, tempEndPoint.X),
Math.Min(RectStartPoint.Y, tempEndPoint.Y));
Rect.Size = new Size(
Math.Abs(RectStartPoint.X - tempEndPoint.X),
Math.Abs(RectStartPoint.Y - tempEndPoint.Y));
if (!RectStart)
{
Rects.Add(Rect);
RectStart = true;
}
else
{
Rects[(Rects.Count - 1)] = Rect;
}
Canvas.Invalidate();
}
private void Canvas_Paint(object sender, PaintEventArgs e)
{
// Draw the rectangle...
if (Canvas.Image != null)
{
if (Rects.Count > 0)
{
e.Graphics.FillRectangles(selectionBrush, Rects.ToArray());
}
}
}
}
The bonus to this is that if I'm careful, I'll be able to also make some rectangles permanent while removing others.

Drag and drop rectangles in list

I have made a simple app in c# that lets me draw a rectangle and than move it by mouse. Now I want to draw multiple rectangles I also add them to a list, this also works, but I want to be able to move each rectangle by itself. This goes wrong. I can only move the first rectangle I created. If I try to move the other rectangle the first rectangle teleports to my mouse but only if I click on the second rectangle, if i click anywhere else it crashes with a nullpointer (I know how to fix this but its not the problem) What I cant figure out is why cant I move the second rectangle
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace TekenTest
{
public partial class Form1 : Form
{
bool isMouseDown;
List<Item> _Items;
Item i;
public Form1()
{
InitializeComponent();
_Items = new List<Item>();
isMouseDown = false;
}
private void tekenVel_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
foreach (Item i in this._Items)
{
i.drawItem(g);
}
}
private void tekenVel_MouseDown(object sender, MouseEventArgs e)
{
this.i = _Items.Find(Item => ((i.X <= e.X && (i.WIDTH + i.X) >= e.X) &&
(i.Y <= e.Y && (i.HEIGTH + i.Y) >= e.Y)));
i.note = Color.Azure;
isMouseDown = true;
}
private void tekenVel_MouseMove(object sender, MouseEventArgs e)
{
if (isMouseDown == true)
{
i.X = e.X;
i.Y = e.Y;
Refresh();
}
}
private void tekenVel_MouseUp(object sender, MouseEventArgs e)
{
isMouseDown = false;
}
private void itemToolStripMenuItem_Click(object sender, EventArgs e)
{
this.i = new Item();
this._Items.Add(i);
this.Refresh();
}
}
}
Object class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TekenTest
{
class Object
{
public int X
{
get;
set;
}
public int Y
{
get;
set;
}
public int HEIGTH
{
get;
set;
}
public int WIDTH
{
get;
set;
}
public Object()
{
}
}
}
Item class
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TekenTest
{
class Item : Object
{
public Rectangle rect;
public String text;
public Font font;
public Brush textb;
public Color note;
public Item()
{
this.X = 200;
this.Y = 200;
this.WIDTH = 200;
this.HEIGTH = 200;
font = new Font("Arial", 12, FontStyle.Bold, GraphicsUnit.Point);
text = "Ik ben tekst";
note = Color.Yellow;
textb = Brushes.Black;
}
public void drawItem(Graphics g)
{
this.rect = new Rectangle(X, Y, WIDTH, HEIGTH);
// g.DrawRectangle(new)
g.FillRectangle(new SolidBrush(note), rect);
g.DrawString(text, font, textb, rect);
}
}
}
I would suggest a different approach. I would not make the Item a class variable or a property. Every control has a Property named Tag of type object. It can be used for any purposes. What I would typically do is to upon creation set the item as the tag of the represented control. Then In the Move triggers I would handle extract the Item from the Tag property, cast it from object and then manipulate it directly via values.
private void itemToolStripMenuItem_Click(object sender, EventArgs e)
{
var i = ((Control)sender.Tag) as Item;
this.i = new Item();
this._Items.Add(i);
// you dynamically create a control and set the Tag property
someControl.Tag = i;
this.Refresh();
}
private void tekenVel_MouseDown(object sender, MouseEventArgs e)
{
var i = ((Control)sender.Tag) as Item;
if(i!=null)
{
i.note = Color.Azure;
isMouseDown = true;
}
}
private void tekenVel_MouseMove(object sender, MouseEventArgs e)
{
if (isMouseDown == true)
{
i.X = e.X;
i.Y = e.Y;
Refresh();
}
}
You have probably the problem that the instance is not being found by the find method and you are trying to manipulate a null object. This way this is always going to work on a specific object and you you do not have to search for it. It cleans up the code and it runs much smoother.
EDIT
Also, I would Suggest renaming your class from Object to something else. just so it does not get confused with the .NET object root type
Problem can be because of in function tekenVel_Paint in foreach loop you are using name for Item the same as for instance variable i. Change it to other like here:
private void tekenVel_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
foreach (Item obj in _Items)
{
obj.drawItem(g);
}
}
And similar problem in tekenVel_MouseDown. You also should change name in conditional statement in function Find.
private void tekenVel_MouseDown(object sender, MouseEventArgs e)
{
this.i = _Items.Find(item => ((item.X <= e.X && (item.WIDTH + item.X) >= e.X) &&
(item.Y <= e.Y && (item.HEIGTH + item.Y) >= e.Y)));
i.note = Color.Azure;
isMouseDown = true;
}
Also you don't need isMouseDown variable. You can check if mouse button is pressed using MouseEventArgs e in tekenVel_MouseMove function.
On MouseUp event you should set usual color for your active item i and set it to null. And check in OnMouseMove event if i is not null in case if user click inside your control but not inside any object.
I fixed by changing my List searching method. I now the for each loop isnt the best way to go so I will change that later:
private Item selectItem(MouseEventArgs e)
{
IEnumerable<Item> itemQuerry =
from it in _Items
where it.X <= e.X && it.WIDTH + it.X >= e.X && it.Y <= e.Y && it.HEIGTH + it.Y >= e.Y
select it;
foreach (Item foundItem in itemQuerry)
{
this.mItem = foundItem;
}
mItem.note = Color.Azure;
return mItem;
}

Moving Child Form with Parent Form

I've got 2 forms: A Parent and a supporting Child form.
When the Child form is initially displayed, I set something called my "Zero Point", which is where the Child form should be positioned relative to the Parent, and defined by one of the controls on the Parent:
private void miShowChild_Click(object sender, EventArgs e) {
if ((m_childForm == null) || m_childForm .IsDisposed) {
m_formLeft = this.Left + this.Control1.Left;
m_formTop = this.Top + this.Control1.Top;
m_childForm = new ChildForm1();
m_childForm.Show(this);
m_childForm.Parent_Shift(m_formTop, m_formLeft);
}
m_roleMap.TopLevel = true;
}
When the Parent form is moved somewhere else, that information is translated to the Child form:
private void Form1_MoveResize(object sender, EventArgs e) {
if ((m_childForm != null) && !m_childForm.IsDisposed) {
m_formLeft = this.Left + this.Control1.Left;
m_formTop = this.Top + this.Control1.Top;
m_childForm.Parent_Shift(m_formTop, m_formLeft);
}
}
That logic seems OK, right?
So, what happens in the Child form?
First, I've got these variables defined:
private bool m_automate;
private int m_left, m_top;
private Point m_zero;
private void ChildForm_Load(object sender, EventArgs e) {
m_left = 0;
m_top = 0;
m_zero = Point.Empty;
}
When the Child is set using Parent_Shift, I want to get where this "Zero Point" should be defined:
public void Parent_Shift(int parentTop, int parentLeft) {
m_automate = true;
if (m_zero != Point.Empty) {
this.Left = parentLeft - m_left;
this.Top = parentTop - m_top;
} else {
this.Left = parentLeft;
this.Top = parentTop;
}
m_zero = new Point(this.Left, this.Top);
m_automate = false;
}
When the Child form is repositioned, and not because of the Parent moving, then I want to record how far from my "Zero Point" this is:
private void Form_MoveResize(object sender, EventArgs e) {
if (!m_automate && this.Visible) {
if (m_zero != Point.Empty) {
m_left = m_zero.X - this.Left;
m_top = m_zero.Y - this.Top;
} else {
m_zero = new Point(this.Left, this.Top);
}
}
}
All of the methods are firing, but I can't seem to get the coordinate system figured out.
I can move the Child form around manually, then move the Parent form and the Child form warps to some other location - then moves proportional to the Parent.
I want the Child to remain at some (X, Y) coordinates relative to the Parent.
When the Child form is moved, that relative coordinate needs to be updated to be where ever the Child form was left.
Does anyone know how to fix my issue?
Put this code in your parent. You don't need any code in your child form.
public partial class ParentForm : Form
{
private bool updatingChildPosition = false;
private Point childFormOffset = Point.Empty;
private ChildForm child = null;
public ParentForm()
{
InitializeComponent();
this.child = new ChildForm();
child.Show();
child.Move += child_Move;
UpdateChildFormOffset();
}
void child_Move(object sender, EventArgs e)
{
// Child form is moved, store it's new offset
UpdateChildFormOffset();
}
private void UpdateChildFormOffset()
{
if (!updatingChildPosition)
{
this.childFormOffset = new Point(
this.child.Location.X - this.Location.X,
this.child.Location.Y - this.Location.Y);
}
}
private void ParentForm_Move(object sender, EventArgs e)
{
// Updating child position
this.updatingChildPosition = true;
child.Location = new Point(
this.Location.X + childFormOffset.X,
this.Location.Y + childFormOffset.Y);
this.updatingChildPosition = false;
}
}

How To Make A Selection Tool In C#

Download the project
I am trying to make a panel with a background color which should be able to be drawn in runtime when the user holds down left mouse button and moves it around. All works find when the user is starting from top left and go to bottom right just like the image shows:
But I want the user to be able to make the panel from bottom right to top left. Just like when you select something on your computer with your mouse
Here is my code for now:
public void parent_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
Point tempLoc = e.Location;
this.Location = new Point
(
Math.Min(this.Location.X, tempLoc.X),
Math.Min(this.Location.Y, tempLoc.Y)
);
this.Size = new Size
(
Math.Abs(this.Location.X - tempLoc.X),
Math.Abs(this.Location.Y - tempLoc.Y)
);
this.Invalidate();
}
}
I think this is where I go wrong, and I simply can't find the right algorithm for it:
this.Size = new Size
(
Math.Abs(this.Location.X - tempLoc.X),
Math.Abs(this.Location.Y - tempLoc.Y)
);
But if I use a rectangle it works fine, but I want my panel to be able to do it as well.
You need to just check the minimums and maximums of your starting point versus your mousemoving point. The problem with the code is you are using the control location as a starting point, but if you move the mouse from bottom-right to top-left, your location needs to change. A control can't have a negative size.
Here is how I re-wrote it (I removed unnecessary stuff for testing):
public class SelectionTool : Panel {
Form parent;
Point _StartingPoint;
public SelectionTool(Form parent, Point startingPoint) {
this.DoubleBuffered = true;
this.Location = startingPoint;
//this.endingPoint = startingPoint;
_StartingPoint = startingPoint;
this.parent = parent;
this.parent.Controls.Add(this);
this.parent.MouseMove += new MouseEventHandler(parent_MouseMove);
this.BringToFront();
this.Size = new Size(0, 0);
}
public void parent_MouseMove(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
int minX = Math.Min(e.Location.X, _StartingPoint.X);
int minY = Math.Min(e.Location.Y, _StartingPoint.Y);
int maxX = Math.Max(e.Location.X, _StartingPoint.X);
int maxY = Math.Max(e.Location.Y, _StartingPoint.Y);
this.SetBounds(minX, minY, maxX - minX, maxY - minY);
this.Invalidate();
}
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
this.BackColor = Color.Blue;
}
}
Here is the code I used to test it on a form:
private SelectionTool _SelectPanel = null;
private void Form1_MouseMove(object sender, MouseEventArgs e) {
if (e.Button == System.Windows.Forms.MouseButtons.Left) {
if (_SelectPanel == null)
_SelectPanel = new SelectionTool(this, e.Location);
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e) {
if (_SelectPanel != null) {
_SelectPanel.Dispose();
_SelectPanel = null;
}
}

Categories