I'm currently making simple programs to draw line on picturebox. Click for the first point then drag & release for the 2nd point.
Now I want to make that line can be edited rather than making new one. like when click & drag on the edge it will resized to the new drop point and when click & drag on the body it will move the entire item without changing the shape.
the current picturebox code is like this
public Form1()
{
InitializeComponent();
pictureBox1.BackgroundImage = Image.FromFile("C:/Users/RPC1940/Pictures/500px.jpg");
DrawArea = new Bitmap(pictureBox1.Size.Width, pictureBox1.Size.Height);
pictureBox1.Image = DrawArea;
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Pen pen = new Pen(Color.Black, 3);
g.DrawLine(pen,x1,y1,x2,y2);
pen.Dispose();
pictureBox1.Refresh();
this.DoubleBuffered = true;
}
and the current mouse click & drag to draw line is like this
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (drawRadio.Checked == true)
{
x1 = e.Location.X;
y1 = e.Location.Y;
x1Box.Text = x1.ToString();
y1Box.Text = y1.ToString();
}
else if (editRadio1.Checked == true)
{
x1 = e.Location.X;
y1 = e.Location.Y;
x1Box.Text = x1.ToString();
y1Box.Text = y1.ToString();
pictureBox1.Refresh();
}
else if(editRadio2.Checked == true)
{
x2 = e.Location.X;
y2 = e.Location.Y;
x2Box.Text = x2.ToString();
y2Box.Text = y2.ToString();
pictureBox1.Refresh();
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
if(drawRadio.Checked == true)
{
x2 = e.Location.X; //Second x coordinate (mouse release)
y2 = e.Location.Y; //Second y coordinate (mouse release)
x2Box.Text = x2.ToString();
y2Box.Text = y2.ToString();
}
}
Anyone know how to edit the lines that has drawn in the box by dragging the edge or body of it? Thank you very much
**Edit
added few radio box. drawRadio is default to draw the line, editRadio1 & editRadio2 for edit the coordinates of point 1 & 2. there's 1 more radio box to move the etire line shape but I dont know how is it works yet. if anyone know please tell me. Thanks
It's getting to the F_Paint event but not to the F_MouseDown event.
What I want to be able to draw rectangle on the F form.
Maybe since the F form is transparent it can't draw on it ? But it's never get to the F_MouseDown event I use a break point inside the F_MouseDown event.
Not sure why it's not getting to the MouseDown event.
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 Tester
{
public partial class Form1 : Form
{
Ffmpeg ffmpeg = new Ffmpeg();
private bool _canDraw;
private int _startX, _startY;
private Rectangle _rect;
public Form1()
{
InitializeComponent();
BackColor = Color.Blue;
TransparencyKey = BackColor;
Opacity = 1;
var f = new HelperForm { Opacity = 0, ShowInTaskbar = false, FormBorderStyle = FormBorderStyle.None };
f.MouseDown += F_MouseDown;
f.MouseMove += F_MouseMove;
f.MouseUp += F_MouseUp;
f.Paint += F_Paint;
f.Show();
Visible = false;
Owner = f;
Visible = true;
Move += (o, a) => f.Bounds = Bounds;
Resize += (o, a) => f.Bounds = Bounds;
f.Bounds = Bounds;
ffmpeg.Start(#"d:\ffmpegx86\test.mp4", 24);
}
private void F_Paint(object sender, PaintEventArgs e)
{
//Create a new 'pen' to draw our rectangle with, give it the color red and a width of 2
using (Pen pen = new Pen(Color.Red, 2))
{
//Draw the rectangle on our form with the pen
e.Graphics.DrawRectangle(pen, _rect);
}
}
private void F_MouseUp(object sender, MouseEventArgs e)
{
//The system is no longer allowed to draw rectangles
_canDraw = false;
}
private void F_MouseMove(object sender, MouseEventArgs e)
{
//If we are not allowed to draw, simply return and disregard the rest of the code
if (!_canDraw) return;
//The x-value of our rectangle should be the minimum between the start x-value and the current x-position
int x = Math.Min(_startX, e.X);
//The y-value of our rectangle should also be the minimum between the start y-value and current y-value
int y = Math.Min(_startY, e.Y);
//The width of our rectangle should be the maximum between the start x-position and current x-position minus
//the minimum of start x-position and current x-position
int width = Math.Max(_startX, e.X) - Math.Min(_startX, e.X);
//For the hight value, it's basically the same thing as above, but now with the y-values:
int height = Math.Max(_startY, e.Y) - Math.Min(_startY, e.Y);
_rect = new Rectangle(x, y, width, height);
//Refresh the form and draw the rectangle
Refresh();
}
private void F_MouseDown(object sender, MouseEventArgs e)
{
//The system is now allowed to draw rectangles
_canDraw = true;
//Initialize and keep track of the start position
_startX = e.X;
_startY = e.Y;
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
ffmpeg.Close();
}
}
class HelperForm : Form
{
protected override CreateParams CreateParams
{
get
{
const int WS_EX_TOOLWINDOW = 0x80;
CreateParams cp = base.CreateParams;
cp.ExStyle |= WS_EX_TOOLWINDOW;
return cp;
}
}
}
}
There're several reasons preventing the rectangle from being drawn and firing mouse events. First, you're setting the Opacity to 0; this means whatever you try to draw could never be visible. Instead, you should set the TransparencyKey to the color in BackColor:
f.TransparencyKey = f.BackColor;
Then, you're trying to draw a rectangle using the object _rect which was never initialized; therefore the rectangle you're trying to draw will be drawn with the size of 0 width and 0 height which means it won't be drawn, so, during your initialization, you should give a default value for _rect e.g:
_rect = new Rectangle(Point.Empty, this.Size);
The drawn rectangle should be visible by now; however, the events won't fire because you're reversing form ownership, so, instead of
Owner = f;
Use:
f.Owner = this;
I have created a custom Control called Ellipse. I'm able to resize, move, and paint this Ellipse. Now I'm trying to add undo/redo functionality for the resizing. The user can resize the control at the bottom right corner. At the moment the control prints hello as long as the cursor is positioned at the bottom right corner of the Control. But what I want is that when the user starts resizing (so leftmouse button is down and cursor is at the bottom right corner) hello is printed (only once). How to do this or is there a another (better) way to do it?
Ellipse.cs
class Ellipse : Control
{
private Point mDown { get; set; }
public Ellipse()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
this.DoubleBuffered = true;
this.ResizeRedraw = true;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Draw a black ellipse in the rectangle represented by the control.
e.Graphics.FillEllipse(Brushes.Black, 0, 0, Width, Height);
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
mDown = e.Location;
}
protected override void OnMouseMove(MouseEventArgs e)
{
// Call MyBase.OnMouseMove to activate the delegate.
base.OnMouseMove(e);
if (e.Button == MouseButtons.Left)
{
Location = new Point(e.X + Left - mDown.X, e.Y + Top - mDown.Y);
}
}
/* Allow resizing at the bottom right corner */
protected override void WndProc(ref Message m)
{
const int wmNcHitTest = 0x84;
const int htBottomLeft = 16;
const int htBottomRight = 17;
if (m.Msg == wmNcHitTest)
{
Console.WriteLine("Hello");
int x = (int)(m.LParam.ToInt64() & 0xFFFF);
int y = (int)((m.LParam.ToInt64() & 0xFFFF0000) >> 16);
Point pt = PointToClient(new Point(x, y));
Size clientSize = ClientSize;
if (pt.X >= clientSize.Width - 16 && pt.Y >= clientSize.Height - 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(IsMirrored ? htBottomLeft : htBottomRight);
return;
}
}
base.WndProc(ref m);
}
I would try adding a couple more messages to check for the mouse going down in the non-client area and then another one for when the sizing was finished to complete the transaction:
private bool userResizing = false;
protected override void WndProc(ref Message m) {
const int wmNcHitTest = 0x84;
const int htBottomLeft = 16;
const int htBottomRight = 17;
const int WM_EXITSIZEMOVE = 0x232;
const int WM_NCLBUTTONDWN = 0xA1;
if (m.Msg == WM_NCLBUTTONDWN) {
if (!userResizing) {
userResizing = true;
Console.WriteLine("Start Resizing");
}
} else if (m.Msg == WM_EXITSIZEMOVE) {
if (userResizing) {
userResizing = false;
Console.WriteLine("Finish Resizing");
}
} else if (m.Msg == wmNcHitTest) {
int x = (int)(m.LParam.ToInt64() & 0xFFFF);
int y = (int)((m.LParam.ToInt64() & 0xFFFF0000) >> 16);
Point pt = PointToClient(new Point(x, y));
Size clientSize = ClientSize;
if (pt.X >= clientSize.Width - 16 &&
pt.Y >= clientSize.Height - 16 &&
clientSize.Height >= 16) {
m.Result = (IntPtr)(IsMirrored ? htBottomLeft : htBottomRight);
return;
}
}
base.WndProc(ref m);
}
I want to draw a rectangle on a panel wherever the mouse is clicked. When the user clicks the same rectangle again, it will change its color. It has to be drawn on a panel.
What I have done with the help of the internet is this. It displays a message mouse is clicked between the coordinates of the rectangle drawn first.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public int xcord1;
public int ycord1;
Dictionary<Color, List<Rectangle>> rectangles
= new Dictionary<Color,List<Rectangle>>();
private void panel1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
//Gets a color for the rectangle
Color c = Color.Blue;
//Adds the rectangle using the color as the key for the dictionary
if (!rectangles.ContainsKey(c))
{
rectangles.Add(c, new List<Rectangle>());
}
if (textBox1.Text.Length == 0 && textBox2.Text.Length == 0)
{
textBox1.Text = e.Location.X.ToString();
textBox2.Text = e.Location.Y.ToString();
xcord1 = int.Parse(textBox1.Text);
ycord1 = int.Parse(textBox2.Text);
}
if (Cursor.Position.X <= xcord1 + 100 && Cursor.Position.X >= xcord1
&& Cursor.Position.Y <= ycord1 + 100 && Cursor.Position.Y >= ycord1)
{
MessageBox.Show("click");
}
else
{
rectangles[c].Add(new Rectangle(e.Location.X - 12, e.Location.Y - 12,100,100));
//Adds the rectangle to the collection
}
}
//Make the panel repaint itself.
panel1.Refresh();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
//The key value for the dictionary is the color to use to paint with, so loop through all the keys (colors)
foreach (var rectKey in rectangles.Keys)
{
//Create the pen used to draw the rectangle (using statement makes sure the pen is disposed)
using (var pen = new Pen(rectKey))
{
//Draws all rectangles for the current color
//Note that we're using the Graphics object that is passed into the event handler.
e.Graphics.DrawRectangles(pen, rectangles[rectKey].ToArray());
}
}
}
}
}
How can I change the BorderColor of the Textbox when a user Clicks on it or focuses on it?
You can handle WM_NCPAINT message of TextBox and draw a border on the non-client area of control if the control has focus. You can use any color to draw border:
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class ExTextBox : TextBox
{
[DllImport("user32")]
private static extern IntPtr GetWindowDC(IntPtr hwnd);
private const int WM_NCPAINT = 0x85;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == WM_NCPAINT && this.Focused)
{
var dc = GetWindowDC(Handle);
using (Graphics g = Graphics.FromHdc(dc))
{
g.DrawRectangle(Pens.Red, 0, 0, Width - 1, Height - 1);
}
}
}
}
Result
The painting of borders while the control is focused is completely flicker-free:
BorderColor property for TextBox
In the current post I just change the border color on focus. You can also add a BorderColor property to the control. Then you can change border-color based on your requirement at design-time or run-time. I've posted a more completed version of TextBox which has BorderColor property:
in the following post:
BorderColor property for TextBox
try this
bool focus = false;
private void Form1_Paint(object sender, PaintEventArgs e)
{
if (focus)
{
textBox1.BorderStyle = BorderStyle.None;
Pen p = new Pen(Color.Red);
Graphics g = e.Graphics;
int variance = 3;
g.DrawRectangle(p, new Rectangle(textBox1.Location.X - variance, textBox1.Location.Y - variance, textBox1.Width + variance, textBox1.Height +variance ));
}
else
{
textBox1.BorderStyle = BorderStyle.FixedSingle;
}
}
private void textBox1_Enter(object sender, EventArgs e)
{
focus = true;
this.Refresh();
}
private void textBox1_Leave(object sender, EventArgs e)
{
focus = false;
this.Refresh();
}
This is an ultimate solution to set the border color of a TextBox:
public class BorderedTextBox : UserControl
{
TextBox textBox;
public BorderedTextBox()
{
textBox = new TextBox()
{
BorderStyle = BorderStyle.FixedSingle,
Location = new Point(-1, -1),
Anchor = AnchorStyles.Top | AnchorStyles.Bottom |
AnchorStyles.Left | AnchorStyles.Right
};
Control container = new ContainerControl()
{
Dock = DockStyle.Fill,
Padding = new Padding(-1)
};
container.Controls.Add(textBox);
this.Controls.Add(container);
DefaultBorderColor = SystemColors.ControlDark;
FocusedBorderColor = Color.Red;
BackColor = DefaultBorderColor;
Padding = new Padding(1);
Size = textBox.Size;
}
public Color DefaultBorderColor { get; set; }
public Color FocusedBorderColor { get; set; }
public override string Text
{
get { return textBox.Text; }
set { textBox.Text = value; }
}
protected override void OnEnter(EventArgs e)
{
BackColor = FocusedBorderColor;
base.OnEnter(e);
}
protected override void OnLeave(EventArgs e)
{
BackColor = DefaultBorderColor;
base.OnLeave(e);
}
protected override void SetBoundsCore(int x, int y,
int width, int height, BoundsSpecified specified)
{
base.SetBoundsCore(x, y, width, textBox.PreferredHeight, specified);
}
}
WinForms was never good at this and it's a bit of a pain.
One way you can try is by embedding a TextBox in a Panel and then manage the drawing based on focus from there:
public class BorderTextBox : Panel {
private Color _NormalBorderColor = Color.Gray;
private Color _FocusBorderColor = Color.Blue;
public TextBox EditBox;
public BorderTextBox() {
this.DoubleBuffered = true;
this.Padding = new Padding(2);
EditBox = new TextBox();
EditBox.AutoSize = false;
EditBox.BorderStyle = BorderStyle.None;
EditBox.Dock = DockStyle.Fill;
EditBox.Enter += new EventHandler(EditBox_Refresh);
EditBox.Leave += new EventHandler(EditBox_Refresh);
EditBox.Resize += new EventHandler(EditBox_Refresh);
this.Controls.Add(EditBox);
}
private void EditBox_Refresh(object sender, EventArgs e) {
this.Invalidate();
}
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.Clear(SystemColors.Window);
using (Pen borderPen = new Pen(this.EditBox.Focused ? _FocusBorderColor : _NormalBorderColor)) {
e.Graphics.DrawRectangle(borderPen, new Rectangle(0, 0, this.ClientSize.Width - 1, this.ClientSize.Height - 1));
}
base.OnPaint(e);
}
}
Using OnPaint to draw a custom border on your controls is fine. But know how to use OnPaint to keep efficiency up, and render time to a minimum. Read this if you are experiencing a laggy GUI while using custom paint routines: What is the right way to use OnPaint in .Net applications?
Because the accepted answer of PraVn may seem simple, but is actually inefficient. Using a custom control, like the ones posted in the answers above is way better.
Maybe the performance is not an issue in your application, because it is small, but for larger applications with a lot of custom OnPaint routines it is a wrong approach to use the way PraVn showed.
set Text box Border style to None
then write this code to container form "paint" event
private void Form1_Paint(object sender, PaintEventArgs e)
{
System.Drawing.Rectangle rect = new Rectangle(TextBox1.Location.X,
TextBox1.Location.Y, TextBox1.ClientSize.Width, TextBox1.ClientSize.Height);
rect.Inflate(1, 1); // border thickness
System.Windows.Forms.ControlPaint.DrawBorder(e.Graphics, rect,
Color.DeepSkyBlue, ButtonBorderStyle.Solid);
}
With PictureBox1
.Visible = False
.Width = TextBox1.Width + 4
.Height = TextBox1.Height + 4
.Left = TextBox1.Left - 2
.Top = TextBox1.Top - 2
.SendToBack()
.Visible = True
End With
Here is my complete Flat TextBox control that supports themes including custom border colors in normal and focused states.
The control uses the same concept mentioned by Reza Aghaei https://stackoverflow.com/a/38405319/5514131 ,however the FlatTextBox control is more customizable and flicker-free.
The control handles the WM_NCPAINT window message in a better way to help eliminate flicker.
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = WindowMessage.WM_NCPAINT AndAlso _drawBorder AndAlso Not DesignMode Then 'Draw the control border
Dim w As Integer
Dim h As Integer
Dim clip As Rectangle
Dim hdc As IntPtr
Dim clientRect As RECT = Nothing
GetClientRect(Handle, clientRect)
Dim windowRect As RECT = Nothing
GetWindowRect(Handle, windowRect)
w = windowRect.Right - windowRect.Left
h = windowRect.Bottom - windowRect.Top
clip = New Rectangle(CInt((w - clientRect.Right) / 2), CInt((h - clientRect.Bottom) / 2), clientRect.Right, clientRect.Bottom)
hdc = GetWindowDC(Handle)
Using g As Graphics = Graphics.FromHdc(hdc)
g.SetClip(clip, CombineMode.Exclude)
Using sb = New SolidBrush(BackColor)
g.FillRectangle(sb, 0, 0, w, h)
End Using
Using p = New Pen(If(Focused, _borderActiveColor, _borderNormalColor), BORDER_WIDTH)
g.DrawRectangle(p, 0, 0, w - 1, h - 1)
End Using
End Using
ReleaseDC(Handle, hdc)
Return
End If
MyBase.WndProc(m)
End Sub
I have removed the default BorderStyle property and replaced it with a simple boolean DrawBorder property that controls whether to draw a border around the control or not.
Use the BorderNormalColor property to specify the border color when the TextBox has no focus, and the BorderActiveColor property to specify the border color when the control receives focus.
The FlatTextBox comes with two themes VS2019 Dark and VS2019 Light, use the Theme property to switch between them.
Complete FlatTextBox control code written in VB.NET
https://gist.github.com/ahmedosama007/37fe2004183a51a4ea0b4a6dcb554176