Please take a look at this implementation from Telerik:
When hover:
When click:
Can I implement it without using any 3rd party program ?
I have created an example to demonstrate how to create your custom control.
Create a UserControl and give the name Sample and place the following code in .cs file.
[DefaultEvent("Click")]
public partial class Sample : UserControl
{
private string _text;
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override string Text
{
get
{
return _text;
}
set
{
_text = value;
}
}
private bool _mouseDown = false;
private bool _mouseHover = false;
private bool _invalidateRequired = true;
public Sample()
{
InitializeComponent();
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
_mouseDown = true;
_invalidateRequired = true;
this.Invalidate();
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
_mouseDown = false;
_invalidateRequired = true;
this.Invalidate();
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
_mouseHover = true;
if (_invalidateRequired)
{
this.Invalidate();
_invalidateRequired = false;
}
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
_mouseHover = false;
this.Invalidate();
_invalidateRequired = true;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Rectangle r = new Rectangle(0, 0, this.ClientRectangle.Width - 1, this.ClientRectangle.Height - 1);
Color bg = Color.White;
Color fr = Color.Gray;
Color br = Color.FromArgb(173, 178, 173);
if (_mouseDown)
{
bg = Color.FromArgb(24, 162, 231);
fr = Color.White;
}
if (_mouseHover)
br = Color.FromArgb(24, 162, 231);
e.Graphics.FillRectangle(new SolidBrush(bg), r);
e.Graphics.DrawRectangle(new Pen(br, 3), r);
StringFormat sf = new StringFormat();
sf.LineAlignment = StringAlignment.Center;
sf.Alignment = StringAlignment.Center;
e.Graphics.DrawString(Text, this.Font, new SolidBrush(fr), r, sf);
}
}
Code with Designer.cs
Related
my drawings are deleted in the code I wrote. If "refresh" is not used, the drawing does not appear. Is there a mistake in my drawing? I don't understand
How can I draw a new line without erasing my lines ?
Here's the code
Pen pen;
Point startXY;
Point endXY;
bool ismoving = false;
string choice = "Line";
public Form1(){
InitializeComponent();}
private void drawArea_MouseDown(object sender, MouseEventArgs e){
startXY = e.Location;
ismoving = true;}
private void drawArea_MouseMove(object sender, MouseEventArgs e){
if (ismoving == true){
endXY = e.Location;
drawArea.Refresh();}}
private void drawArea_MouseUp(object sender, MouseEventArgs e){
endXY = e.Location;
ismoving = false;}
private void Form1_Load(object sender, EventArgs e){
//Graphics graph = drawArea.CreateGraphics();
//graph.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
pen = new Pen(Color.White,1);
pen.StartCap = pen.EndCap = System.Drawing.Drawing2D.LineCap.Round;}
private void drawArea_Paint(object sender, PaintEventArgs e){
switch (choice){
case "Line":{
e.Graphics.DrawLine(pen, startXY, endXY);
break;}}}
Your code needs to know how to recreate the entire graphics window at any time, because windows may ask it to redraw at any time.
So you need to keep track of the things to be drawn in a list and use this list in the Paint event to render to the screen.
Below is an example code that does this. The result looks like this
public enum Tool
{
Line,
PolyLine,
Circle,
}
public partial class Form1 : Form
{
Point startXY;
Point endXY;
bool isMoving;
Tool next;
public Form1()
{
InitializeComponent();
this.Drawing = new List<IDrawObject>();
this.isMoving = false;
this.next = Tool.Line;
this.drawArea.BackColor = Color.Black;
this.drawArea.BorderStyle = BorderStyle.FixedSingle;
}
public List<IDrawObject> Drawing { get; }
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
drawArea.Resize += (s, ev) => drawArea.Refresh();
drawArea.Paint += pictureBox1_Paint;
drawArea.MouseDown += PictureBox1_MouseDown;
drawArea.MouseMove += PictureBox1_MouseMove;
drawArea.MouseUp += PictureBox1_MouseUp;
this.KeyDown += (s, ev) =>
{
switch (ev.KeyCode)
{
case Keys.Space:
next = NextTool(next);
UpdateFormTitle();
break;
}
};
UpdateFormTitle();
}
public void UpdateFormTitle()
{
this.Text = $"Draw Area. Tool={next}. Press [SPACE] to switch.";
}
private void PictureBox1_MouseUp(object sender, MouseEventArgs e)
{
isMoving = false;
endXY = e.Location;
Drawing.Add(NextDrawObject());
drawArea.Invalidate();
}
private void PictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (isMoving)
{
endXY = e.Location;
drawArea.Invalidate();
}
}
private void PictureBox1_MouseDown(object sender, MouseEventArgs e)
{
isMoving = true;
startXY = e.Location;
}
protected void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
using (var pen = new Pen(Color.White, 1))
{
pen.EndCap = LineCap.Round;
pen.StartCap = LineCap.Round;
foreach (var item in Drawing)
{
item.Draw(e.Graphics, pen);
}
if (isMoving)
{
pen.Color = Color.SteelBlue;
pen.DashStyle = DashStyle.Dash;
NextDrawObject().Draw(e.Graphics, pen);
}
}
}
public static float Distance(Point from, Point to)
{
var dx = (float)(to.X-from.X);
var dy = (float)(to.Y-from.Y);
return (float)Math.Sqrt(dx*dx+dy*dy);
}
public Tool NextTool(Tool current)
{
switch (current)
{
case Tool.Line:
return Tool.Circle;
case Tool.Circle:
return Tool.Line;
case Tool.PolyLine:
throw new NotImplementedException();
default:
throw new NotSupportedException();
}
}
public IDrawObject NextDrawObject()
{
switch (next)
{
case Tool.Line:
return new Line(startXY, endXY);
case Tool.PolyLine:
throw new NotImplementedException();
case Tool.Circle:
var radius = Distance(startXY, endXY);
return new Circle(startXY, radius);
default:
throw new NotSupportedException();
}
}
}
public interface IDrawObject
{
void Draw(Graphics g, Pen pen);
}
public struct Line : IDrawObject
{
public Line(PointF from, PointF to)
{
this.From=from;
this.To=to;
}
public PointF From { get; }
public PointF To { get; }
public void Draw(Graphics g, Pen pen)
{
g.DrawLine(pen, From, To);
}
}
public struct PolyLine : IDrawObject
{
public PolyLine(params PointF[] nodes)
{
this.Nodes=nodes;
}
public PointF[] Nodes { get; }
public void Draw(Graphics g, Pen pen)
{
g.DrawLines(pen, Nodes);
}
}
public struct Circle : IDrawObject
{
public Circle(PointF center, float radius)
{
this.Center=center;
this.Radius=radius;
}
public PointF Center { get; }
public float Radius { get; }
public void Draw(Graphics g, Pen pen)
{
g.DrawEllipse(pen, Center.X-Radius, Center.Y-Radius, 2*Radius, 2*Radius);
}
}
Hi I have a custom TextEditor:
public partial class TextEditor : TextBox
{
public TextEditor() : base()
{
this.Font = new Font("Calibri", 12.0f);
this.BackColor = Color.Gainsboro;
this.BorderStyle = BorderStyle.FixedSingle;
if (this.ReadOnly)
{
this.BackColor = Color.DarkGray;
}
}
protected override void InitLayout()
{
base.InitLayout();
base.CharacterCasing = _charCasing;
//SetStyle(ControlStyles.UserPaint, true);
}
}
I would like to change its BackGroundColor when the property ReadOnly = true but its not working.
Any clue?
You are doing it on constructor. Which ReadOnly be default to False
What you need is listen to ReadOnlyChanged event
public partial class TextEditor : TextBox
{
public TextEditor()
{
this.Font = new Font("Calibri", 12.0f);
this.BackColor = Color.Gainsboro;
this.BorderStyle = BorderStyle.FixedSingle;
ReadOnlyChanged += OnReadOnlyChanged;
}
private void OnReadOnlyChanged(object sender, EventArgs eventArgs)
{
if (ReadOnly)
BackColor = Color.DarkGray;
}
protected override void InitLayout()
{
base.InitLayout();
CharacterCasing = _charCasing;
//SetStyle(ControlStyles.UserPaint, true);
}
}
I've created GradientButton which changes angle of gradient when mouse cursor is in its' bounds. Unfortunately the graphics is corrupted as there are random flashes during rendering.
To achieve gradient rotation I start a thread upon MouseEnter and stop it at MouseLeave. Doublebuffered set to true, helped a lot but did not solve this fully.
public partial class GradientButton : UserControl {
public Color Color1 { get; set; }
public Color Color2 { get; set; }
public bool Down { get; set; }
public bool MouseOnButton { get; set; }
[Browsable(true)]
public string TextToDraw { get; set; }
public int Angle { get; set; }
public GradientButton() {
InitializeComponent();
Color1 = Color.YellowGreen;
Color2 = Color.LightGreen;
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
var color1 = Color1;
var color2 = Color2;
if (Down) {
var temp = color1;
color1 = color2;
color2 = temp;
}
if (MouseOnButton) {
color1 = ControlPaint.Dark(Color2);
}
using (LinearGradientBrush brush = new LinearGradientBrush(this.ClientRectangle, color1, color2, Angle)) {
e.Graphics.FillRectangle(brush, this.ClientRectangle);
}
Rectangle rect1 = ClientRectangle;
// Create a StringFormat object with the each line of text, and the block
// of text centered on the page.
StringFormat stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
// Draw the text and the surrounding rectangle.
e.Graphics.DrawString(TextToDraw, Font, new SolidBrush(ForeColor), rect1, stringFormat);
}
protected override void OnClick(EventArgs e) {
base.OnClick(e);
}
protected override void OnResize(EventArgs e) {
base.OnResize(e);
Invalidate();
}
protected override void OnMouseDown(MouseEventArgs e) {
base.OnMouseDown(e);
Down = true;
Invalidate();
}
protected override void OnMouseUp(MouseEventArgs e) {
base.OnMouseUp(e);
Down = false;
Invalidate();
}
protected override void OnMouseEnter(EventArgs e) {
base.OnMouseEnter(e);
MouseOnButton = true;
Thread t = new Thread(Animate);
t.Start();
}
public void Animate() {
while (MouseOnButton) {
Angle += 5;
Thread.Sleep(25);
Invalidate();
}
}
protected override void OnMouseLeave(EventArgs e) {
base.OnMouseLeave(e);
Angle = 0;
MouseOnButton = false;
Invalidate();
}
}
GradientButton.Designer.cs
partial class GradientButton {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.SuspendLayout();
//
// GradientButton
//
this.Name = "GradientButton";
this.Size = new System.Drawing.Size(78, 28);
this.ResumeLayout(false);
}
#endregion
}
Use
SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlsStyles.Opaque, true);
to prevent the background from being painted prior to OnPaint processing. This should prevent the flicker inbetween the background erase and the painting.
I have a UserControl that has inside some other controls and then a Panel that I use to paint custom shapes on mouse events. I would like to add some other functionalities but for that I would need to detect KeyDown and KeyUp while the mouse is drawing shapes on the Panel (in this situation UserControl.KeyDown/KeyUp never fire). How can be this achieved?
EDIT
Here is a minimum version for the UserControl where the problem can be seen:
public partial class UserControl1 : UserControl
{
TextBox textBox1;
Panel panel1;
public UserControl1()
{
InitializeComponent();
textBox1 = new System.Windows.Forms.TextBox();
textBox1.Location = new System.Drawing.Point(0, 0);
this.Controls.Add(this.textBox1);
panel1 = new System.Windows.Forms.Panel();
panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
panel1.Location = new System.Drawing.Point(0, 25);
panel1.Size = new System.Drawing.Size(300, 150);
panel1.MouseEnter += new System.EventHandler(this.panel1_MouseEnter);
this.Controls.Add(this.panel1);
this.Size = new System.Drawing.Size(300, 200);
this.KeyDown += UserControl1_KeyDown;
}
private void panel1_MouseEnter(object sender, EventArgs e)
{
panel1.Focus();
}
void UserControl1_KeyDown(object sender, KeyEventArgs e)
{
MessageBox.Show("Key down");
}
}
When this control is inside a Form the "Key down" message is never displayed.
I took the code from Hans Passant here (great hint, many thanks!) and I modified it to suit my needs as follow:
class SelectablePanel : Panel
{
public SelectablePanel()
{
this.SetStyle(ControlStyles.Selectable, true);
this.TabStop = true;
}
protected override bool IsInputKey(Keys keyData)
{
if (keyData == Keys.Up || keyData == Keys.Down) return true;
if (keyData == Keys.Left || keyData == Keys.Right) return true;
return base.IsInputKey(keyData);
}
protected override void OnKeyDown(KeyEventArgs e)
{
var handler = KeyDown;
if (handler != null) handler(this, e);
}
protected override void OnKeyPress(KeyPressEventArgs e)
{
var handler = KeyPress;
if (handler != null) handler(this, e);
}
protected override void OnKeyUp(KeyEventArgs e)
{
var handler = KeyUp;
if (handler != null) handler(this, e);
}
protected override void OnMouseEnter(EventArgs e)
{
this.Focus();
base.OnMouseEnter(e);
}
protected override void OnEnter(EventArgs e)
{
this.Invalidate();
base.OnEnter(e);
}
protected override void OnLeave(EventArgs e)
{
this.Invalidate();
base.OnLeave(e);
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
if (this.Focused)
{
var rc = this.ClientRectangle;
rc.Inflate(-2, -2);
ControlPaint.DrawFocusRectangle(pe.Graphics, rc);
}
}
[Browsable(true)]
public new event EventHandler<KeyEventArgs> KeyDown;
[Browsable(true)]
public new event EventHandler<KeyEventArgs> KeyUp;
[Browsable(true)]
public new event EventHandler<KeyPressEventArgs> KeyPress;
}
Finally I modified my original code like this:
public partial class UserControl1 : UserControl
{
TextBox textBox1;
SelectablePanel selectablePanel;
public UserControl1()
{
InitializeComponent();
textBox1 = new System.Windows.Forms.TextBox();
textBox1.Location = new System.Drawing.Point(0, 0);
this.Controls.Add(this.textBox1);
selectablePanel = new SelectablePanel();
selectablePanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
selectablePanel.Location = new System.Drawing.Point(0, 25);
selectablePanel.Size = new System.Drawing.Size(300, 150);
selectablePanel.KeyDown += panel1_KeyDown;
this.Controls.Add(this.selectablePanel);
this.Size = new System.Drawing.Size(300, 200);
}
void panel1_KeyDown(object sender, KeyEventArgs e)
{
MessageBox.Show("Key down");
}
}
and now it works for what I need.
I want to know how to draw rectangle in C# and make it dragged and dropped in the page here my code to draw it but I cannot drag or drop it.
public partial class Form1 : Form
{
public bool drag = false;
int cur_x, cur_y;
Rectangle rec = new Rectangle(10, 10, 100, 100);
public Form1()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs r)
{
base.OnPaint(r);
Graphics g = r.Graphics;
//g.DrawRectangle(Pens.Black, rec);
g.FillRectangle(Brushes.Aquamarine, rec);
}
private void recmousedown(object sender, MouseEventArgs m)
{
if (m.Button != MouseButtons.Left)
return;
rec = new Rectangle(m.X, m.Y,100,100);
drag = true;
cur_x = m.X;
cur_y = m.Y;
}
private void recmousemove(object sender, MouseEventArgs m)
{
if (m.Button != MouseButtons.Left)
return;
rec.X = m.X;
rec.Y = m.Y;
Invalidate();
}
}
You're pretty close, you just need to initialize the rectangle better and adjust the rectangle size in the Move event:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
this.DoubleBuffered = true;
}
Rectangle rec = new Rectangle(0, 0, 0, 0);
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.FillRectangle(Brushes.Aquamarine, rec);
}
protected override void OnMouseDown(MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
rec = new Rectangle(e.X, e.Y, 0, 0);
Invalidate();
}
}
protected override void OnMouseMove(MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
rec.Width = e.X - rec.X;
rec.Height = e.Y - rec.Y;
Invalidate();
}
}
}
You could try the approach in this article: http://beta.codeproject.com/KB/GDI-plus/flickerFreeDrawing.aspx