Hi i want to make a square to resize button like from normal button to this: https://prnt.sc/ESy2d63JMgRv if you know how to do resizing squares to resize top, left, right and bottom of button by dragging mouse please let me know
I could not fully understand whether you want to make such a change while the application is running or in the designer, neither the link you shared above nor the question is fully explanatory, but I tried to write something about how this can be done while the application is running.
I'm writing this to give you some idea of how to do it, obviously the code below doesn't solve your specific problem.
private int squareSize = 5;
private bool isDragging = false;
private bool onlyX = false;
private bool onlyY = false;
private bool bothXY = false;
You need to draw squares on the corners and edges of the button.
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
Rectangle rect1 = new Rectangle(new Point(button1.Left - squareSize, button1.Top - squareSize), new Size(squareSize, squareSize));
Rectangle rect2 = new Rectangle(new Point(button1.Right - (button1.Right - button1.Left) / 2, button1.Top - squareSize), new Size(squareSize, squareSize));
Rectangle rect3 = new Rectangle(new Point(button1.Right, button1.Top), new Size(squareSize, squareSize));
Rectangle rect4 = new Rectangle(new Point(button1.Left - squareSize, button1.Bottom - (button1.Bottom - button1.Top) / 2), new Size(squareSize, squareSize));
Rectangle rect5 = new Rectangle(new Point(button1.Right, button1.Bottom - (button1.Bottom - button1.Top) / 2), new Size(squareSize, squareSize));
Rectangle rect6 = new Rectangle(new Point(button1.Left - squareSize, button1.Bottom - squareSize), new Size(squareSize, squareSize));
Rectangle rect7 = new Rectangle(new Point(button1.Left + (button1.Right - button1.Left) / 2, button1.Bottom), new Size(squareSize, squareSize));
Rectangle rect8 = new Rectangle(new Point(button1.Right, button1.Bottom - squareSize), new Size(squareSize, squareSize));
// copy the left, right, top and bottom coordinates of the rectangles above.
// draw the rectangles.
}
You need to write the mouse down event to determine which corner or edge it was grabbed and dragged
(For example, the first and eighth squares act on the x=-y line, while the third and sixth squares act on the x=y line, with that in mind I suggest you write different boolean values so you can differentiate when resizing the button.), something like this:
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
// Dragging first rectangle, changes on both axis
if (e.Location.X < rect1Right && e.Location.X > rect1Left && e.Location.Y < rect1Bottom && e.Location.Y > rect1Top && e.Button == MouseButtons.Left)
{
bothXY = true;
isDragging = true;
}
// Dragging second rectangle, changes on only y-axis
if (e.Location.X < rect2Right && e.Location.X > rect2Left && e.Location.Y < rect2Bottom && e.Location.Y > rect2Top && e.Button == MouseButtons.Left)
{
onlyY = true;
isDragging = true;
}
}
You should write a mouse move event function to determine the direction and amount of movement on each axis. (I'm not sure if you should do the resizing inside that function, give it a try.)
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (isDragging && bothXY)
{
resizeX = e.X - button1.Right;
resizeY = e.Y - button1.Bottom;
}
if(isDragging && onlyY)
{
resizeY = e.Y - button1.Bottom;
}
button1.Width += resizeX;
button1.Height += resizeY;
this.Invalidate();
}
And finally you should write a mouse up event where you reset all boolean values to initials to stop resizing the button.
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
isDragging = false;
onlyY = false;
onlyX = false;
bothXY = false;
}
Related
I have a winforms app where I draw on an image in a picturebox. The drawing part works really well, except for lag when PictureBox.SizeMode = PictureBoxSizeMode.Zoom. The reason I set the SizeMode to Zoom is so I could zoom in and out of the image while preserving memory. Is there any way to speed up the drawing process?
The code for the PictureBox's Paint method looks like so:
pen = new Pen(color, 5);
solidBrush = new SolidBrush(solid);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.ScaleTransform(PictureScale, PictureScale);
foreach (List<Point> polygon in Polygons)
{
e.Graphics.DrawLines(pen, polygon.ToArray());
}
if (NewPolygon != null)
{
Pen pp = new Pen(color, 5);
if (NewPolygon.Count > 1)
{
e.Graphics.DrawLines(pp, NewPolygon.ToArray());
}
if (NewPolygon.Count > 0)
{
using (Pen dashed_pen = pp)
{
dashed_pen.DashPattern = new float[] { 3, 3 };
e.Graphics.DrawLine(dashed_pen, NewPolygon[NewPolygon.Count - 1], NewPoint);
}
}
}
The code allows the user to draw a series of points connected by some solid lines. After adding the first point, there is a dotted line going from the previous point to the mouse's current position. The program 'fills' in the line when the user adds a new Point NewPolygon is a List<Point> object, and the program adds a point every time the user clicks. Pol is a List<List<Point>> object which holds NewPolygon. I attached a GIF of the drawing process because an image is worth 1000 words. The lag in the gif is not noticeable at all in different size modes.
The if (NewPolygon.Count > 1) loop draws solid lines between at least 2 points. The if (NewPolygon.Count > 0) draws a dotted line between spot and NewPoint when there is at least one point.
EDIT : full drawing code
int x = 0;
int y = 0;
bool drag = false;
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
Point spot = new Point((int)((float)(e.Location.X) / PictureScale), (int)((float)(e.Location.Y) / PictureScale));
if (e.Button == MouseButtons.Middle)
{
x = e.X;
y = e.Y;
drag = true;
}
else
{
if (NewPolygon != null)
{
if (e.Button == MouseButtons.Right)
{
if (NewPolygon.Count > 1)
{
int counter = NewPolygon.Count;
Polygons.Add(NewPolygon);
}
NewPolygon = null;
}
else if (e.Button == MouseButtons.Left)
{
if (NewPolygon[NewPolygon.Count - 1] != spot)
{
NewPolygon.Add(spot);
scaffolds.Add(new Rectangle(spot.X - 3, spot.Y - 3, 6, 6));
}
}
}
else
{
NewPolygon = new List<Point>();
NewPoint = spot;
NewPolygon.Add(spot);
scaffolds.Add(new Rectangle(spot.X - 3, spot.Y - 3, 6, 6));
}
}
//this.Capture = true;
pictureBox1.Refresh();
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (drag)
{
pictureBox1.Top += e.Y - y;
pictureBox1.Left += e.X - x;
this.Cursor = Cursors.SizeAll;
}
else
{
if (NewPolygon == null)
return;
NewPoint = new Point((int)((float)(e.Location.X) / PictureScale), (int)((float)(e.Location.Y) / PictureScale));
pictureBox1.Refresh();
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.ScaleTransform(PictureScale, PictureScale);
foreach (Rectangle rect in scaffolds)
{
e.Graphics.DrawEllipse(pen, rect);
}
foreach (List<Point> polygon in Polygons)
{
e.Graphics.DrawLines(pen, polygon.ToArray());
}
if (NewPolygon != null)
{
Pen pp = new Pen(color, 5);
if (NewPolygon.Count > 0)
{
if (NewPolygon.Count > 1)
{
e.Graphics.DrawLines(pp, NewPolygon.ToArray());
}
using (Pen dashed_pen = pp)
{
dashed_pen.DashPattern = new float[] { 3, 3 };
e.Graphics.DrawLine(dashed_pen, NewPolygon[NewPolygon.Count - 1], NewPoint);
}
}
}
}
Sorry for my eng. Im quite new to C#, and i need this:
After clicking a button, a rectangle form with pre-determined size will appear;
This rectangle must move all around the form + move on screen even if the form is minimized;
The retangle must follow the mouse movements;
When i make a mouse click on the place of screen i want, i need that all coordinates of the area of the rectangle are stored in variables.
This coordinates of rectangle will later be checked by a read-pixel code that i already have, so i really need that the rectangle area of where i clicked really be stored in variables.
EDIT:
Im really new to c# and what i have done so far is:
private void button5_Click(object sender, EventArgs e)
{
Graphics dc = this.CreateGraphics();
Pen Bluepen = new Pen(Color.Blue, 3);
dc.DrawRectangle(Bluepen, 0, 0, 50, 50);
}
And:
private void button5_MouseMove(object sender, MouseEventArgs e)
{
if (isMouseDown == true)
{
rect.Location = e.Location;
if (rect.Right > pictureBox1.Width)
{
rect.X = pictureBox1.Width - rect.Width;
}
if (rect.Top < 0)
{
rect.Y = 0;
}
if (rect.Left < 0 )
{
rect.X = 0;
}
if (rect.Bottom > pictureBox1.Height)
{
rect.Y = pictureBox1.Height - rect.Height;
}
Refresh();
}
}
Thanks in advance!!
I have found this question (as a few others), but this is the one I have implemented so far:
Crosshair cursor with additional lines in C#
As it states, I can use a stock cursor "cross" directly in the IDE. This is a really good way to do things. The answer specified in the answer above draws a cross on the screen at the given width / height. Eg:
private Cursor crossCursor(Pen pen, Brush brush, int x, int y)
{
var pic = new Bitmap(x, y);
Graphics gr = Graphics.FromImage(pic);
var pathX = new GraphicsPath();
var pathY = new GraphicsPath();
pathX.AddLine(0, y / 2, x, y / 2);
pathY.AddLine(x / 2, 0, x / 2, y);
gr.DrawPath(pen, pathX);
gr.DrawPath(pen, pathY);
IntPtr ptr = pic.GetHicon();
var c = new Cursor(ptr);
return c;
}
My issue is that I want my cross hairs to extend to the Bounds of the viewing area. To provide context here, I have:
//Form
//TableLayoutPanel
//UserControl (fills the TableLayoutPanel visible area)
So how can I adjust my cursor so that the lines extend (much like in CAD pacakages)?
Thanks.
Update: I have tried calling the method from here:
protected override void OnLoad(System.EventArgs e)
{
Cursor = crossCursor(Pens.WhiteSmoke, Brushes.WhiteSmoke, Bounds.Width, Bounds.Height);
}
But it is not Ok because at this point in time Bounds is returning a dimension of 150 by 150 which is not the size of the TableLayoutPanel.
Update: I have adjuted it to use the Resize handler instead and it does improve things:
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
Cursor = crossCursor(Pens.WhiteSmoke, Brushes.WhiteSmoke, Bounds.Width, Bounds.Height);
}
The only problem now (and it kind of makes sense I suppose) is that the cursor will only take the full width and height of the view when it is central to the view. As soon as I move about in the view that cursor does not adjust. I always want a horizontal/vertical line through the mouse position (not just the initial cross).
See:
The crosshairs need extending (the thicker red lines). Either I need to constantly create the cursor as the mouse moves or construct the two lines another way. What to do?
I came across this:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/7bdbad6d-1f65-461b-8f0c-6ef4f243fa6b/crosshair-cursor-using-c?forum=csharpgeneral
So, instead of changing the cursor object I now draw lines in the controls MouseMove handler:
Region r = new Region();
r.Union(new Rectangle(0, lastY, this.Width, 1));
r.Union(new Rectangle(lastX, 0, 1, this.Height));
this.Invalidate(r);
this.Update();
Graphics g = Graphics.FromHwnd(this.Handle);
g.DrawLine(Pens.White, 0, e.Y, this.Width, e.Y);
g.DrawLine(Pens.White, e.X, 0, e.X, this.Height);
int intDiameter = 20;//the diameter of this circle
g.DrawEllipse(Pens.White, e.X - intDiameter / 2, e.Y - intDiameter / 2, 20, 20);
//to draw the circle
lastX = e.X;
lastY = e.Y;
It works, but I get noticiable screen flicker doing it this way.
You don't need to create a cursor. You can create a double buffered control and draw cross over control.
using System;
using System.Drawing;
using System.Windows.Forms;
public class DrawingSurface : Control
{
Pen crossPen;
Pen rectanglePen;
Brush rectangleBrush;
public DrawingSurface()
{
this.DoubleBuffered = true;
this.ResizeRedraw = true;
crossPen = new Pen(Color.Red, 2);
rectangleBrush = new SolidBrush(Color.FromArgb(50, Color.Blue));
rectanglePen = new Pen(Color.Blue, 1);
}
bool mouseDown = false;
Point startPoint = Point.Empty;
Point endPoint = Point.Empty;
protected override void OnMouseDown(MouseEventArgs e)
{
startPoint = e.Location;
mouseDown = true;
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
mouseDown = false;
base.OnMouseUp(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
endPoint = e.Location;
this.Invalidate();
base.OnMouseMove(e);
}
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics;
if (this.ClientRectangle.Contains(endPoint))
DrawCross(e.Graphics, endPoint);
if (mouseDown)
DrawRectangle(e.Graphics, startPoint, endPoint);
}
void DrawCross(Graphics g, Point point)
{
g.DrawLine(crossPen, new Point(0, point.Y), new Point(Width, point.Y));
g.DrawLine(crossPen, new Point(point.X, 0), new Point(point.X, Height));
}
void DrawRectangle(Graphics g, Point point1, Point point2)
{
var rectangle = new Rectangle(
Math.Min(point1.X, point2.X), Math.Min(point1.Y, point2.Y),
Math.Abs(point1.X - point2.X), Math.Abs(point1.Y - point2.Y));
g.FillRectangle(rectangleBrush, rectangle);
g.DrawRectangle(rectanglePen, rectangle);
}
protected override void Dispose(bool disposing)
{
crossPen.Dispose();
rectanglePen.Dispose();
rectangleBrush.Dispose();
base.Dispose(disposing);
}
}
I'm building custom control that will display tiles, like colored grid.
I've managed to do drawing, scrolling and basic logic, but I have problem with creating tooltip for each tile.
Each tile color depends on data that is "bound" to that tile.
I'll try to describe my idea:
Above image shows my control, I have 4 squares drawn there, I'd like to show different tooltip when user hovers different parts of my control.
Below is my tooltip (small red rectangle) and lower is standard WinForms Chart component.
I'd like to get this kind of behavior in my control (tooltip is outside of control, so long text is displayed properly).
Below is code of my control with just basic functionality:
public sealed class UC1 : UserControl
{
private bool _showTooltip;
private string _tooltipText;
private Point _mousePosition;
public UC1()
{
MinimumSize = new Size(100, 100);
Size = new Size(100, 100);
SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer, true);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.FillRectangle(Brushes.LightGoldenrodYellow, 0, 0, Width/2, Height/2);
e.Graphics.FillRectangle(Brushes.LightGray, Width/2, 0, Width, Height/2);
e.Graphics.FillRectangle(Brushes.LightSlateGray, 0, Height/2, Width/2, Height);
e.Graphics.FillRectangle(Brushes.LightSteelBlue, Width/2, Height/2, Width, Height);
using (var p = new Pen(Color.Black, 2))
{
e.Graphics.DrawLine(p, Width/2, 0, Width/2, Height);
e.Graphics.DrawLine(p, 0, Height/2, Width, Height/2);
}
if (_showTooltip)
{
SizeF c = e.Graphics.MeasureString(_tooltipText, DefaultFont);
int width = (int) c.Width;
int height = (int) c.Height;
const int offset = 12;
var x = _mousePosition.X + width + offset > Width ? _mousePosition.X - width : _mousePosition.X + offset;
var y = _mousePosition.Y + height + offset > Height ? _mousePosition.Y - height : _mousePosition.Y + offset;
e.Graphics.FillRectangle(Brushes.Red, x, y, width, height);
e.Graphics.DrawString(_tooltipText, DefaultFont, Brushes.Black, x, y);
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.X > 0 && e.X < Width/2 && e.Y > 0 && e.Y < Height/2)
{
Debug.WriteLine("1,1 square");
_tooltipText = "1,1";
}
else if (e.X > Width/2 && e.X < Width && e.Y > 0 && e.Y < Height/2)
{
Debug.WriteLine("1,2 square");
_tooltipText = "1,2";
}
else if (e.X > 0 && e.X < Width/2 && e.Y > Height/2 && e.Y < Height)
{
Debug.WriteLine("2,1 square");
_tooltipText = "2,1";
}
else if (e.X > Width/2 && e.X < Width && e.Y > Height/2 && e.Y < Height)
{
Debug.WriteLine("2,2 square");
_tooltipText = "2,2";
}
_mousePosition = e.Location;
_showTooltip = true;
Refresh();
}
protected override void OnMouseLeave(EventArgs e)
{
_showTooltip = false;
Refresh();
base.OnMouseLeave(e);
}
}
I've found similar question: How to make a floating (tooltip) control in Windows.Forms? but I'd like to avoid creating custom tooltip and instead use standard one.
How can I show standard tooltip that will change it text when hovering on different tiles. I'd like to add everything to my user control, so I won't have to add any code to form hosting my control.
Why do you try to draw the ToolTip yourself instead of using the system one?
Just add one to the UC class
// private bool _showTooltip; ?? probably not needed any more..
private string _tooltipText;
// private Point _mousePosition; ??..
ToolTip ttip = new ToolTip();
and set it like this:
// _mousePosition = e.Location; ??..
// _showTooltip = true; ??..
ttip.SetToolTip(this, _tooltipText); // use this in the mousemove
Refresh();
Of course now you can skip the whole Painting part..
If you want to control the location where the ToolTip is shown use one of the ShowToolTip() overloads instead of SetToolTip() ..!
While both are still there this is the result, going over the UC's border and displaying with a nice drop shadow..:
If you really want your ToolTip to look different from the usual ones, you can set its OwnerDraw to true and code its Draw event just like any other control with GDI+ graphics methods..
Update:
There is an inherent flicker problem; for an explanation see Hans' answer here; his recommendation #2 and one of the answers is helpful:
Remember last mouse position and set the tooltip only when the mouse
position changes.
So we need to add a last ToolTip location:
Point tipLoc = Point.Empty;
Which we test and set in the mouse move:
if (tipLoc != e.Location )
{
tipLoc = e.Location;
ttip.SetToolTip(this, _tooltipText);
}
I have drawn and saved the Rectangle on the image i loaded in the picture box. How i like to draw multiple rectangles for that i tried array in the rectangle but it gives error ("Object reference not set to an instance of an object." (Null reference Exception was unhandled).
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (mybitmap == null)
{
mybitmap = new Bitmap(sz.Width, sz.Height);
}
rect[count] = new Rectangle(e.X, e.Y, 0, 0);
this.Invalidate();
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (stayToolStripMenuItem.Checked == true)
{
switch (e.Button)
{
case MouseButtons.Left:
{
rect[count] = new Rectangle(rect[count].Left, rect[count].Top, e.X - rect[count].Left, e.Y - rect[count].Top);
pictureBox1.Invalidate();
count++:
break;
}
}
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (stayToolStripMenuItem.Checked == true)
{
button1.Visible = true;
button2.Visible = true;
if (mybitmap == null)
{
return;
}
using (g = Graphics.FromImage(mybitmap))
{
using (Pen pen = new Pen(Color.Red, 2))
{
//g.Clear(Color.Transparent);
e.Graphics.DrawRectangle(pen, rect);
label1.Top = rect[count].Top; label1[count].Left = rect[count].Left; label1.Width = rect[count].Width;
label1.Height = rect[count].Height;
if (label1.TextAlign == ContentAlignment.TopLeft)
{
e.Graphics.DrawString(label1.Text, label1.Font, new SolidBrush(label1.ForeColor), rect);
g.DrawString(label1.Text, label1.Font, new SolidBrush(label1.ForeColor), rect);
g.DrawRectangle(pen, rect[count]);
}
}
}
}
}
How can i do this.....
You're incrementing the count variable after filling the rect array. By the time pictureBox1_Paint is executed, this increment has already taken place, so rect[count] is now a null reference, which you then try to draw :)
Also, there appears to be a compiler error in pictureBox1_MouseDown. The count++ inside the switch statement doesn't belong to any case block. Put it before the break; statement.
I imagine your intention is something like this:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (mybitmap == null)
return;
using (g = Graphics.FromImage(mybitmap))
using (Pen pen = new Pen(Color.Red, 2))
{
g.Clear(Color.Transparent);
for (int i = 0; i < count; i++)
{
// Code to draw rect[i], instead of rect[count]
}
}
}
How large is your rect array, by the way? Arrays do not automatically grow to accommodate your needs. You may want to look at using List<Rectangle> instead.
Have change the code little bit. Instead of mouse move event used Mouse click event.
On using mouse move event it calls every time when mouse move on the picture box. so list rectangle count gets increased. for this i used mouse click event.
When list rectangle added in Mouse down event, it's only get the values for height and width of the rectangle (if use (0,0,e.X,e.Y))and also rectangle always starts from top left corner (not able to start the rectangle point where user likes) and it's gets only X and Y values (if use(e.X, e.Y,0,0))
to over come this I used the list rectangle in the mouse click event, so I get all values.
List<Rectangle> rectangles = new List<Rectangle>();
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (stayToolStripMenuItem.Checked == true)
{
if (mybitmap == null)
{
mybitmap = new Bitmap(sz.Width, sz.Height);
}
rect = new Rectangle(e.X, e.Y, 0, 0);
this.Invalidate();
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (stayToolStripMenuItem.Checked == true)
{
button1.Visible = true;
button2.Visible = true;
if (mybitmap == null)
{
return;
}
using (g = Graphics.FromImage(mybitmap))
{
using (Pen pen = new Pen(Color.Red, 2))
{
//g.Clear(Color.Transparent);
e.Graphics.DrawRectangle(pen, rect);
label1.Top = rect.Top; label1.Left = rect.Left; label1.Width = rect.Width;
label1.Height = rect.Height;
if (label1.TextAlign == ContentAlignment.TopLeft)
{
e.Graphics.DrawString(label1.Text, label1.Font, new SolidBrush(label1.ForeColor), rect);
g.DrawString(label1.Text, label1.Font, new SolidBrush(label1.ForeColor), rect);
g.DrawRectangle(pen, rect);
}
}
}
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
rect = new Rectangle(rect.Left, rect.Top, e.X - rect.Left, e.Y - rect.Top);
rectangles.Add(rect);
pictureBox1.Invalidate();
f = 0;
}
}
What ever rectangle I draw get saved in the list rectangles and get saved when i saved the work (could see all the drawn rectangles when i opened the saved file).
Now the problem is, when I draw a new rectangle the previous one get disappeared (on run time. However this added in list rectangle).
How to display all the rectangles drawn on run time, so user can know how many rectangles drawn and where.