How to make a custom Button responsiveness smoother? - c#

I made a Custom Control with the functions of a Button.
The problem with this controls is that when you hover over it, the color of the button changes: when this action is repeated quickly, it glitches out.
To make it more clear, I recorded a video.
Update 09.07.2020;
I updated the code, perhaps this is because I do not add the image correctly, although judging by the appearance, the original, everything seems to be correct, the image works, but what happens next, you have already seen in the video.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace Test_Project.SupportClass
{
[DesignerCategory("Code")]
public class button_check : Control
{
private int m_BorderSize = 2;
private int m_ButtonRoundRadius = 15;
private bool IsHighlighted = false;
private bool IsPressed = false;
private Image _image;
private ImageLayout LautsCallBack { get; set; }
public button_check()
{
SetStyle(ControlStyles.Opaque |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.UserPaint, true);
// To be explicit...
SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
this.DoubleBuffered = false;
InitializeComponent();
}
private void InitializeComponent()
{
Size = new Size(100, 40);
BackColor = Color.Tomato;
BackColor2 = Color.Tomato;
ButtonBorderColor = Color.Black;
ButtonHighlightColor = Color.Orange;
ButtonHighlightColor2 = Color.OrangeRed;
ButtonHighlightForeColor = Color.Black;
ButtonPressedColor = Color.Red;
ButtonPressedColor2 = Color.Maroon;
ButtonPressedForeColor = Color.White;
}
public ImageLayout LayoutImage
{
get
{
return LautsCallBack;
}
set
{
LautsCallBack = value;
RecreateHandle();
}
}
public Image ImageButtom
{
get
{
return _image;
}
set
{
_image = value;
RecreateHandle();
}
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x00000020; // WS_EX_TRANSPARENT
return cp;
}
}
// Invalidate(rect) in Design-Mode to refresh the view
public int BorderSize
{
get => m_BorderSize;
set
{
m_BorderSize = Math.Max(Math.Min(value, 6), 1);
RepaintControl();
}
}
// Set Max = 44, Min = 1 to avoid quirks and exceptions
public int ButtonRoundRadius
{
get => m_ButtonRoundRadius;
set
{
m_ButtonRoundRadius = Math.Max(Math.Min(value, 44), 1);
RepaintControl();
}
}
public override string Text
{
get => base.Text;
set
{
base.Text = value;
RepaintControl();
}
}
// You should Invalidate the Parent also when these change
public Color BorderColor { get; set; } = Color.Tomato;
public Color BackColor2 { get; set; } = Color.Tomato;
public Color ButtonBorderColor { get; set; }
public Color ButtonHighlightColor { get; set; }
public Color ButtonHighlightColor2 { get; set; }
public Color ButtonHighlightForeColor { get; set; }
public Color ButtonPressedColor { get; set; }
public Color ButtonPressedColor2 { get; set; }
public Color ButtonPressedForeColor { get; set; }
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
var foreColor = IsPressed ? ButtonPressedForeColor : IsHighlighted ? ButtonHighlightForeColor : ForeColor;
var backColor = IsPressed ? ButtonPressedColor : IsHighlighted ? ButtonHighlightColor : BackColor;
var backColor2 = IsPressed ? ButtonPressedColor2 : IsHighlighted ? ButtonHighlightColor2 : BackColor2;
var rect = Path.GetBounds();
using (var pen = new Pen(ButtonBorderColor, m_BorderSize))
using (var pathBrush = new LinearGradientBrush(rect, backColor, backColor2, LinearGradientMode.Vertical))
using (var textBrush = new SolidBrush(foreColor))
using (var sf = new StringFormat())
{
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
e.Graphics.FillPath(pathBrush, Path);
if (m_BorderSize > 0) e.Graphics.DrawPath(pen, Path);
if (_image != null)
{
switch (LayoutImage)
{
case ImageLayout.Stretch:
e.Graphics.DrawImage(_image, this.ClientRectangle);
break;
case ImageLayout.Center:
int left = (this.ClientSize.Width - _image.Width) / 2;
int top = (this.ClientSize.Height - _image.Height) / 2;
e.Graphics.DrawImage(_image, left, top);
break;
case ImageLayout.Tile:
using (var texture = new TextureBrush(_image))
{
e.Graphics.FillRectangle(texture, this.ClientRectangle);
}
break;
case ImageLayout.Zoom:
double xr = (double)this.ClientSize.Width / _image.Width;
double yr = (double)this.ClientSize.Height / _image.Height;
if (xr > yr)
{
rect.Width = (int)(_image.Width * yr);
rect.X = (this.ClientSize.Width - rect.Width) / 2;
}
else
{
rect.Height = (int)(_image.Height * xr);
rect.Y = (this.ClientSize.Height - rect.Height) / 2;
}
e.Graphics.DrawImage(_image, rect);
break;
}
}
rect.Inflate(-4, -4);
e.Graphics.DrawString(Text, Font, textBrush, rect, sf);
}
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
IsHighlighted = true;
RepaintControl();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
IsHighlighted = false;
IsPressed = false;
RepaintControl();
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
IsPressed = true;
RepaintControl();
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
IsPressed = false;
RepaintControl();
}
private void RepaintControl()
{
Parent?.Invalidate(this.Bounds, true);
Invalidate();
}
private GraphicsPath Path
{
get
{
var rect = ClientRectangle;
int scaleOnBorder = -((m_BorderSize / 2) + 2);
rect.Inflate(scaleOnBorder, scaleOnBorder);
return GetRoundedRectangle(rect, m_ButtonRoundRadius);
}
}
private GraphicsPath GetRoundedRectangle(Rectangle rect, int d)
{
var gp = new GraphicsPath();
gp.StartFigure();
gp.AddArc(rect.X, rect.Y, d, d, 180, 90);
gp.AddArc(rect.X + rect.Width - d, rect.Y, d, d, 270, 90);
gp.AddArc(rect.X + rect.Width - d, rect.Y + rect.Height - d, d, d, 0, 90);
gp.AddArc(rect.X, rect.Y + rect.Height - d, d, d, 90, 90);
gp.CloseFigure();
return gp;
}
}
}

All right, try your Button with these modifications:
► SetStyle(ControlStyles.Opaque, true) is set. Combined with WS_EX_TRANSPARENT, you have a fully transparent Control that supports TransparentColor. No need to also set ControlStyles.SupportsTransparentBackColor, it's understood.
► Double buffering is explicitly disable, since we're painting the Control ourselves when required (ControlStyles.AllPaintingInWmPaint, ControlStyles.UserPaint and ControlStyles.ResizeRedraw are all set).
► Moved the invalidating functions to the RepaintControl() method, including invalidating the parent Control, if available, when in design-mode. At run-time, Invalidate() is enough.
► Recalculated the GraphicsPath bounds when the BorderSize is changed, so the border is always painted inside the Control's bounds (to preserve anti-aliasing, which requires at least one pixel to render correctly. When too close to the Control's bounds, the rendering is incomplete).
► base.OnPaint(e) is still called, but you only need it if you want to allow User customization of the Control. Otherwise, you can remove it (or place it after your code, at the bottom of the OnPaint method).
► Added, as example, Min/Max checkes on some properties:
The BorderSize is limited in the range (0, 6) with Math.Max(Math.Min(value, 6), 0)
The ButtonRoundRadius is limited to range (1, 44) with Math.Max(Math.Min(value, 44), 1): the GraphicsPath cannot add arcs with a null angle and, above 45, it'll generate re-entrant curves.
I don't see any flickering at run-time.
Note that this is tested with .Net Framework 4.8
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
[DesignerCategory("Code")]
public class l1_Button : Control
{
private int m_BorderSize = 2;
private int m_ButtonRoundRadius = 15;
private bool IsHighlighted = false;
private bool IsPressed = false;
public l1_Button()
{
SetStyle(ControlStyles.Opaque |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.UserPaint, true);
// To be explicit...
SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
this.DoubleBuffered = false;
InitializeComponent();
}
private void InitializeComponent()
{
Size = new Size(100, 40);
BackColor = Color.Tomato;
BackColor2 = Color.Tomato;
ButtonBorderColor = Color.Black;
ButtonHighlightColor = Color.Orange;
ButtonHighlightColor2 = Color.OrangeRed;
ButtonHighlightForeColor = Color.Black;
ButtonPressedColor = Color.Red;
ButtonPressedColor2 = Color.Maroon;
ButtonPressedForeColor = Color.White;
}
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x00000020; // WS_EX_TRANSPARENT
return cp;
}
}
// Invalidate(rect) in Design-Mode to refresh the view
public int BorderSize {
get => m_BorderSize;
set {
m_BorderSize = Math.Max(Math.Min(value, 6), 1);
RepaintControl();
}
}
// Set Max = 44, Min = 1 to avoid quirks and exceptions
public int ButtonRoundRadius {
get => m_ButtonRoundRadius;
set {
m_ButtonRoundRadius = Math.Max(Math.Min(value, 44), 1);
RepaintControl();
}
}
public override string Text {
get => base.Text;
set {
base.Text = value;
RepaintControl();
}
}
// You should Invalidate the Parent also when these change
public Color BorderColor { get; set; } = Color.Tomato;
public Color BackColor2 { get; set; } = Color.Tomato;
public Color ButtonBorderColor { get; set; }
public Color ButtonHighlightColor { get; set; }
public Color ButtonHighlightColor2 { get; set; }
public Color ButtonHighlightForeColor { get; set; }
public Color ButtonPressedColor { get; set; }
public Color ButtonPressedColor2 { get; set; }
public Color ButtonPressedForeColor { get; set; }
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
var foreColor = IsPressed ? ButtonPressedForeColor : IsHighlighted ? ButtonHighlightForeColor : ForeColor;
var backColor = IsPressed ? ButtonPressedColor : IsHighlighted ? ButtonHighlightColor : BackColor;
var backColor2 = IsPressed ? ButtonPressedColor2 : IsHighlighted ? ButtonHighlightColor2 : BackColor2;
var rect = Path.GetBounds();
using (var pen = new Pen(ButtonBorderColor, m_BorderSize))
using (var pathBrush = new LinearGradientBrush(rect, backColor, backColor2, LinearGradientMode.Vertical))
using (var textBrush = new SolidBrush(foreColor))
using (var sf = new StringFormat()) {
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
e.Graphics.FillPath(pathBrush, Path);
if (m_BorderSize > 0) e.Graphics.DrawPath(pen, Path);
rect.Inflate(-4, -4);
e.Graphics.DrawString(Text, Font, textBrush, rect, sf);
}
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
IsHighlighted = true;
RepaintControl();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
IsHighlighted = false;
IsPressed = false;
RepaintControl();
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
IsPressed = true;
RepaintControl();
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
IsPressed = false;
RepaintControl();
}
private void RepaintControl() {
Parent?.Invalidate(this.Bounds, true);
Invalidate();
}
private GraphicsPath Path {
get {
var rect = ClientRectangle;
int scaleOnBorder = -((m_BorderSize / 2) + 2);
rect.Inflate(scaleOnBorder, scaleOnBorder);
return GetRoundedRectangle(rect, m_ButtonRoundRadius);
}
}
private GraphicsPath GetRoundedRectangle(Rectangle rect, int d)
{
var gp = new GraphicsPath();
gp.StartFigure();
gp.AddArc(rect.X, rect.Y, d, d, 180, 90);
gp.AddArc(rect.X + rect.Width - d, rect.Y, d, d, 270, 90);
gp.AddArc(rect.X + rect.Width - d, rect.Y + rect.Height - d, d, d, 0, 90);
gp.AddArc(rect.X, rect.Y + rect.Height - d, d, d, 90, 90);
gp.CloseFigure();
return gp;
}
}

Related

How to change the backcolor of this tab control?

I want to change the background color of a custom control that I found on the internet.
The code looks like this:
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
namespace BetterNotepad
{
public class TabControlZ : System.Windows.Forms.TabControl
{
private Color nonactive_color1 = Color.LightGreen;
private Color nonactive_color2 = Color.DarkBlue;
private Color backcolor = Color.WhiteSmoke;
private Color active_color1 = Color.FromArgb(255, 210, 250, 220);
private Color active_color2 = Color.FromArgb(255, 140, 160, 200);
public Color forecolor = Color.Navy;
private int color1Transparent = 150;
private int color2Transparent = 150;
private System.ComponentModel.IContainer components;
private int angle = 90;
public Color ActiveTabStartColor
{
get { return active_color1; }
set { active_color1 = value; Invalidate(); }
}
public Color ActiveTabEndColor
{
get { return active_color2; }
set { active_color2 = value; Invalidate(); }
}
public Color NonActiveTabStartColor
{
get { return nonactive_color1; }
set { nonactive_color1 = value; Invalidate(); }
}
public Color NonActiveTabEndColor
{
get { return nonactive_color2; }
set { nonactive_color2 = value; Invalidate(); }
}
public Color BackroudColor
{
get { return backcolor; }
set { backcolor = value; Invalidate(); }
}
public int Transparent1
{
get { return color1Transparent; }
set
{
color1Transparent = value;
if (color1Transparent > 255)
{
color1Transparent = 255;
Invalidate();
}
else
Invalidate();
}
}
public int Transparent2
{
get { return color2Transparent; }
set
{
color2Transparent = value;
if (color2Transparent > 255)
{
color2Transparent = 255;
Invalidate();
}
else
Invalidate();
}
}
public int GradientAngle
{
get { return angle; }
set { angle = value; Invalidate(); }
}
public Color TextColor
{
get { return forecolor; }
set { forecolor = value; Invalidate(); }
}
public TabControlZ()
{
this.DrawMode = TabDrawMode.OwnerDrawFixed;
this.Padding = new System.Drawing.Point(30, 4);
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
base.OnDrawItem(e);
Rectangle rc = GetTabRect(e.Index);
Rectangle rc2 = GetTabRect(e.Index);
if (this.SelectedTab == this.TabPages[e.Index])
{
Color c1 = Color.FromArgb(color1Transparent, active_color1);
Color c2 = Color.FromArgb(color2Transparent, active_color2);
using (LinearGradientBrush br = new LinearGradientBrush(rc, c1, c2, angle))
{
e.Graphics.FillRectangle(br, rc);
}
}
else
{
Color c1 = Color.FromArgb(color1Transparent, nonactive_color1);
Color c2 = Color.FromArgb(color2Transparent, nonactive_color2);
using (LinearGradientBrush br = new LinearGradientBrush(rc, c1, c2, angle))
{
e.Graphics.FillRectangle(br, rc);
}
}
this.TabPages[e.Index].BorderStyle = BorderStyle.FixedSingle;
this.TabPages[e.Index].ForeColor = SystemColors.ControlText;
Rectangle paddedBounds = e.Bounds;
paddedBounds.Inflate(-12, -4);
e.Graphics.DrawString(this.TabPages[e.Index].Text, this.Font, new SolidBrush(forecolor), paddedBounds);
}
private void InitializeComponent()
{
this.SuspendLayout();
this.ResumeLayout(false);
}
}
}
But the background color is white:
the withe part is the tab control and the black part is a richtextbox.
I expected the background color to change but it did not happen. I have tried to add
base.BackColor = Color.Black;
But it does not work.

How to Create bigger CheckBox

I want to design above screen in windows forms. When I am tried to use checkbox control the size of the box is not increasing.
I tried below code but checkbox color and bored color is not changing.
using System;
using System.Drawing;
using System.Windows.Forms;
class MyCheckBox : CheckBox {
public MyCheckBox() {
this.TextAlign = ContentAlignment.MiddleRight;
}
public override bool AutoSize {
get { return base.AutoSize; }
set { base.AutoSize = false; }
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
int h = this.ClientSize.Height - 2;
Rectangle rc = new Rectangle(new Point(0, 1), new Size(h, h));
ControlPaint.DrawCheckBox(e.Graphics, rc,
this.Checked ? ButtonState.Checked : ButtonState.Normal);
}
}
RadioButton also tried but I don't know how to apply colors and highlight the text.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsForms
{
public class BiggerCheckBox : CheckBox
{
#region variables
private int _boxsize = 14;
private int _boxlocatx = 0;
private int _boxlocaty = 0;
private int _textX = 16;
private int _textY = 1;
private Color _boxBackColor = Color.Transparent;
private Color _tickColor = Color.Black;
private float _tickSize = 11f;
private Color _boxColor = Color.Black;
private float _tickLeftPosition = 0f;
private float _tickTopPosition = 0f;
#endregion
#region Properties
public int TextLocationX
{
get { return _textX; }
set { _textX = value; Invalidate(); }
}
public int TextLocationY
{
get { return _textY; }
set { _textY = value; Invalidate(); }
}
public int BoxSize
{
get { return _boxsize; }
set { _boxsize = value; Invalidate(); }
}
public int BoxLocationX
{
get { return _boxlocatx; }
set { _boxlocatx = value; Invalidate(); }
}
public int BoxLocationY
{
get { return _boxlocaty; }
set { _boxlocaty = value; Invalidate(); }
}
public Color BoxBackColor
{
get { return _boxBackColor; }
set { _boxBackColor = value; Invalidate(); }
}
public Color TickColor
{
get { return _tickColor; }
set { _tickColor = value; Invalidate(); }
}
public float TickSize
{
get { return _tickSize; }
set { _tickSize = value; Invalidate(); }
}
public Color BoxColor
{
get { return _boxColor; }
set { _boxColor = value; Invalidate(); }
}
public float TickLeftPosition
{
get { return _tickLeftPosition; }
set { _tickLeftPosition = value; Invalidate(); }
}
public float TickTopPosition
{
get { return _tickTopPosition; }
set { _tickTopPosition = value; Invalidate(); }
}
#endregion
#region Constrctors
public BiggerCheckBox()
{
Appearance = Appearance.Button;
FlatStyle = FlatStyle.Flat;
TextAlign = ContentAlignment.MiddleRight;
FlatAppearance.BorderSize = 0;
AutoSize = false;
}
#endregion
#region Methods
protected override void OnPaint(PaintEventArgs pevent)
{
try
{
base.OnPaint(pevent);
pevent.Graphics.Clear(BackColor);
//checkbox text - using draw string method with specified location
using (SolidBrush brushText = new SolidBrush(ForeColor))
{
pevent.Graphics.DrawString(Text, Font, brushText, _textX, _textY);
}
//checkbox box - using rectangle for checkbox box
Rectangle _rectangleBox = new Rectangle(_boxlocatx, _boxlocaty, _boxsize, _boxsize);
//checkbox box - checckbox box back color and border color
using (SolidBrush brushBackColor = new SolidBrush(_boxBackColor))
{
pevent.Graphics.FillRectangle(brushBackColor, _rectangleBox);
}
using (Pen penBoxColor = new Pen(_boxColor))
{
pevent.Graphics.DrawRectangle(penBoxColor, _rectangleBox);
}
//checkbox box - check and uncheck
if (Checked)
{
using (SolidBrush brush = new SolidBrush(_tickColor))
{
using (Font wing = new Font("Wingdings", _tickSize))
{
pevent.Graphics.DrawString("ü", wing, brush, _tickLeftPosition, _tickTopPosition);
}
}
}
pevent.Dispose();
}
catch (Exception ex)
{
}
}
#endregion
}
}

Display image in ComboBox

I'm using the following code to display image representing a color in a combobox as per this SO answer https://stackoverflow.com/a/13385209/848968
I have added the custom control to the form,but i cannot figure out how to add items with images to it.Kindly advice.
public sealed class ColorSelector : ComboBox
{
public ColorSelector()
{
DrawMode = DrawMode.OwnerDrawFixed;
DropDownStyle = ComboBoxStyle.DropDownList;
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
e.DrawBackground();
e.DrawFocusRectangle();
if (e.Index >= 0 && e.Index < Items.Count)
{
DropDownItem item = (DropDownItem)Items[e.Index];
e.Graphics.DrawImage(item.Image, e.Bounds.Left, e.Bounds.Top);
e.Graphics.DrawString(item.Value, e.Font, new SolidBrush(e.ForeColor), e.Bounds.Left + item.Image.Width, e.Bounds.Top + 2);
}
base.OnDrawItem(e);
}
}
public sealed class DropDownItem
{
public string Value { get; set; }
public Image Image { get; set; }
public DropDownItem()
: this("")
{ }
public DropDownItem(string val)
{
Value = val;
Image = new Bitmap(16, 16);
using (Graphics g = Graphics.FromImage(Image))
{
using (Brush b = new SolidBrush(Color.FromName(val)))
{
g.DrawRectangle(Pens.White, 0, 0, Image.Width, Image.Height);
g.FillRectangle(b, 1, 1, Image.Width - 1, Image.Height - 1);
}
}
}
public override string ToString()
{
return Value;
}
}
Based on your requirement, I have modified OnDrawItem method and changed the comboBox width and height to clear image visible
Please find the code below
protected override void OnDrawItem(DrawItemEventArgs e)
{
if (e.Index >= 0 )
{
e.DrawBackground();
e.DrawFocusRectangle();
string imageFilePath = #Items[e.Index].ToString();;
int width = 40;
int height = 20;
Image img = Image.FromFile(imageFilePath);
e.Graphics.DrawImage(img, 0, e.Bounds.Top + 2, width, height);
e.Graphics.DrawString(imageFilePath, e.Font, new
SolidBrush(e.ForeColor), e.Bounds.Left + width, e.Bounds.Top + 2);
base.OnDrawItem(e);
}
}
}
output :

Want to remove drop shadow from my custom button?

I made a custom button using C#. But the drop shadow or something else is the main problem which is not removing form the button.
How can I remove that.
Here is the code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
namespace ButtonZ
{
public class ButtonZ : System.Windows.Forms.Button
{
Color clr1, clr2;
private Color color1 = Color.LightGreen;
private Color color2 = Color.DarkBlue;
private Color m_hovercolor1 = Color.Yellow;
private Color m_hovercolor2 = Color.DarkOrange;
private int color1Transparent = 150;
private int color2Transparent = 150;
private Color clickcolor1 = Color.DarkOrange;
private Color clickcolor2 = Color.Red;
private int angle = 90;
private int textX = 100;
private int textY = 25;
private String text = "";
public String DisplayText
{
get { return text; }
set { text = value; Invalidate(); }
}
public Color StartColor
{
get { return color1; }
set { color1 = value; Invalidate(); }
}
public Color EndColor
{
get { return color2; }
set { color2 = value; Invalidate(); }
}
public Color MouseHoverColor1
{
get { return m_hovercolor1; }
set { m_hovercolor1 = value; Invalidate(); }
}
public Color MouseHoverColor2
{
get { return m_hovercolor2; }
set { m_hovercolor2 = value; Invalidate(); }
}
public Color MouseClickColor1
{
get { return clickcolor1; }
set { clickcolor1 = value; Invalidate(); }
}
public Color MouseClickColor2
{
get { return clickcolor2; }
set { clickcolor2 = value; Invalidate(); }
}
public int Transparent1
{
get { return color1Transparent; }
set
{
color1Transparent = value;
if (color1Transparent > 255)
{
color1Transparent = 255;
Invalidate();
}
else
Invalidate();
}
}
public int Transparent2
{
get { return color2Transparent; }
set
{
color2Transparent = value;
if (color2Transparent > 255)
{
color2Transparent = 255;
Invalidate();
}
else
Invalidate();
}
}
public int GradientAngle
{
get { return angle; }
set { angle = value; Invalidate(); }
}
public int TextLocation_X
{
get { return textX; }
set { textX = value; Invalidate(); }
}
public int TextLocation_Y
{
get { return textY; }
set { textY = value; Invalidate(); }
}
public ButtonZ ()
{
this.Size = new System.Drawing.Size(200, 50);
this.ForeColor = Color.White;
this.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
}
//method mouse enter
protected override void OnMouseEnter (EventArgs e)
{
base.OnMouseEnter(e);
clr1 = color1;
clr2 = color2;
color1 = m_hovercolor1;
color2 = m_hovercolor2;
}
//method mouse leave
protected override void OnMouseLeave (EventArgs e)
{
base.OnMouseLeave(e);
color1 = clr1;
color2 = clr2;
}
//method mouse click
protected override void OnMouseClick (MouseEventArgs e)
{
if (e.Clicks == 1)
{
base.OnMouseClick(e);
color1 = clickcolor1;
color2 = clickcolor2;
}
}
protected override void OnPaint (PaintEventArgs pe)
{
base.OnPaint(pe);
text = this.Text;
if (textX == 100 && textY == 25)
{
textX = ((this.Width) / 3) + 10;
textY = (this.Height / 2) - 1;
}
Color c1 = Color.FromArgb(color1Transparent, color1);
Color c2 = Color.FromArgb(color2Transparent, color2);
//drawing string & filling gradient rectangle
Brush b = new System.Drawing.Drawing2D.LinearGradientBrush(ClientRectangle, c1, c2, angle);
Point p = new Point(textX, textY);
SolidBrush frcolor = new SolidBrush(this.ForeColor);
Border3DStyle borderStyle = Border3DStyle.SunkenInner;
pe.Graphics.FillRectangle(b , ClientRectangle);
pe.Graphics.DrawString(this.text, this.Font, frcolor, p);
ControlPaint.DrawBorder3D(pe.Graphics, ClientRectangle, borderStyle);
b.Dispose();
}
}
}

C# - Most suitable design patterns to create a PictureBox supporting: panning / zooming / scrollable AND with selection tools (draggable rectangles)

I would like to create a PictureBox class derived supporting the zoom, pan, scroll features and some selection tools (circle, rectangle, annulus).
I'm wondering which design patterns I should use (I just think about a command pattern for making a undo / redo) and some advices for starting properly design my control.
You don't mention the UI technology you are using, nor do you define what a "PictureBox" is so I'm going to assume you mean you are building an application that is a slim version of "Photoshop" and if you're doing a desktop application I would opt for WPF.
Controls are developed in Visual Studio using a UI Technology so saying it's 'in Visual Studio' doesn't really help as that could mean, WPF, Silverlight, ASP.NET, WebForms, Windows Forms, Javascript, Mono etc ...
Given those assumptions I would always opt for use MVVM as the main architecture for the application UI. It promotes loose-coupling and takes advantage of the benefits of WPFs data-binding technology.
That said if you mean Windows Forms then it would be worth looking at this.
MVVM could be use but when you are dealing with over-big pics it does not worth the cost of implementing it.
I implemented my own control, strongly inspired from an old code project: http://www.codeproject.com/Articles/15743/Pan-and-Zoom-Very-Large-Images.
One of the trick to take advantage of, is to make a copy of the picture given as an input, it would make the process, much more faster. It is not fully implemented cause I did not have that much time to work on it.
Here is below a implementation of mine
public class ARTImageControl : Control
{
public ARTImageControl()
{
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.DoubleBuffered = true;
this.InternalScrollbarHorizontal = new HScrollBar();
this.InternalScrollbarVertical = new VScrollBar();
this.ScrollBarsVisibility = ScrollBarVisibility.Auto;
this.ZoomToFit = false;
this.ZoomFactor = 1.0;
this.ZoomOnMouseWheel = true;
this.BorderColor = Color.Black;
this.BorderWidth = 1;
this.BorderStyle = ButtonBorderStyle.Solid;
this.PixelOffsetMode = PixelOffsetMode.Half;
this.SmoothingMode = SmoothingMode.None;
this.InterpolationMode = InterpolationMode.NearestNeighbor;
this.ActiveTool = ARTImageControlToolType.Pan;
Resize += ARTImageControl_Resize;
MouseMove += ARTImageControl_MouseMove;
MouseDown += ARTImageControl_MouseDown;
MouseWheel += ARTImageControl_MouseWheel;
}
protected void UpdateSizeDrawing()
{
this.SizeDrawing = new Size(Convert.ToInt32(this.Width / this.ZoomFactor), Convert.ToInt32(this.Height / this.ZoomFactor));
}
protected HScrollBar InternalScrollbarHorizontal { get; set; }
protected VScrollBar InternalScrollbarVertical { get; set; }
public ScrollBarVisibility ScrollBarsVisibility { get; set; }
#region Overrides
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.Clear(this.BackColor);
if (this.Image != null)
{
e.Graphics.PixelOffsetMode = this.PixelOffsetMode;
e.Graphics.SmoothingMode = this.SmoothingMode;
e.Graphics.InterpolationMode = this.InterpolationMode;
if (this.ZoomToFit)
{
this.RectangleSource = new Rectangle(0, 0, this.Image.Width, this.Image.Height);
}
else
{
this.RectangleSource = new Rectangle(this.InternalPointDrawingOrigin, this.SizeDrawing);
}
Rectangle rectangleDestination = new Rectangle(this.BorderWidth, this.BorderWidth, this.RectangleDestination.Width - 2 * this.BorderWidth, this.RectangleDestination.Height - 2 * this.BorderWidth);
e.Graphics.DrawImage(this.Image, rectangleDestination, this.RectangleSource, GraphicsUnit.Pixel);
}
if ((this.BorderWidth > 0) && (this.BorderStyle != ButtonBorderStyle.None))
{
ControlPaint.DrawBorder(e.Graphics, this.RectangleDestination,
this.BorderColor, this.BorderWidth, this.BorderStyle,
this.BorderColor, this.BorderWidth, this.BorderStyle,
this.BorderColor, this.BorderWidth, this.BorderStyle,
this.BorderColor, this.BorderWidth, this.BorderStyle);
}
//base.OnPaint(e);
}
protected override void OnSizeChanged(EventArgs e)
{
this.RectangleDestination = new Rectangle(0, 0, ClientSize.Width, ClientSize.Height);
this.UpdateSizeDrawing();
base.OnSizeChanged(e);
this.Invalidate();
}
#endregion
#region Methods
public void ZoomFitToClient()
{
if (this.Image != null)
{
this.ZoomToFit = false;
this.ZoomFactor = Math.Min(ClientSize.Width / InternalImage.Width, ClientSize.Height / InternalImage.Height);
}
}
public void ZoomIn()
{
this.ZoomFactor *= 1.1f;
}
public void ZoomOut()
{
this.ZoomFactor *= 0.9f;
}
protected void CheckBoundaries()
{
if (this.Image != null)
{
Int32 boundaryX = this.Image.Width - Convert.ToInt32(this.ClientSize.Width / this.ZoomFactor);
Int32 boundaryY = this.Image.Height - Convert.ToInt32(this.ClientSize.Height / this.ZoomFactor);
if (InternalPointDrawingOrigin.X > boundaryX)
{
this.InternalPointDrawingOrigin = new Point(boundaryX, this.InternalPointDrawingOrigin.Y);
}
if (InternalPointDrawingOrigin.Y > boundaryY)
{
this.InternalPointDrawingOrigin = new Point(this.InternalPointDrawingOrigin.X, boundaryY);
}
if (InternalPointDrawingOrigin.X < 0)
{
this.InternalPointDrawingOrigin = new Point(0, this.InternalPointDrawingOrigin.Y);
}
if (InternalPointDrawingOrigin.Y < 0)
{
this.InternalPointDrawingOrigin = new Point(this.InternalPointDrawingOrigin.X, 0);
}
}
}
#endregion
#region Properties
protected Image InternalImage { get; set; }
public Image Image
{
get { return this.InternalImage; }
set
{
if (value != null)
{
if (this.InternalImage != null)
{
this.InternalImage.Dispose();
this.InternalPointDrawingOrigin = new Point(0, 0);
this.ZoomFactor = 1;
GC.Collect();
}
Rectangle rectangle = new Rectangle(0, 0, value.Width, value.Height);
this.InternalImage = new Bitmap(value).Clone(rectangle, PixelFormat.Format32bppPArgb);
this.Invalidate();
}
else
{
this.InternalImage = null;
this.Invalidate();
}
}
}
public PixelOffsetMode PixelOffsetMode { get; set; }
public SmoothingMode SmoothingMode { get; set; }
public InterpolationMode InterpolationMode { get; set; }
protected Double InternalZoomFactor { get; set; }
public Double ZoomFactor
{
get { return this.InternalZoomFactor; }
set
{
this.InternalZoomFactor = value;
if (this.InternalZoomFactor > 15)
{
this.InternalZoomFactor = 15;
}
if (this.InternalZoomFactor < 0.05)
{
this.InternalZoomFactor = 0.05;
}
if ((this.Image != null))
{
this.UpdateSizeDrawing();
this.CheckBoundaries();
}
this.Invalidate();
}
}
public Boolean InternalZoomToFit { get; set; }
public Boolean ZoomToFit
{
get { return this.InternalZoomToFit; }
set
{
this.InternalZoomToFit = value;
this.Invalidate();
}
}
public ButtonBorderStyle BorderStyle { get; set; }
public Byte BorderWidth { get; set; }
public Color BorderColor { get; set; }
public Boolean DoubleBuffering
{
get { return this.DoubleBuffered; }
set { this.DoubleBuffered = value; }
}
protected Point InternalPointMouseDownStart { get; set; }
protected Point InternalPointDrawingOrigin { get; set; }
protected Rectangle RectangleSource { get; set; }
protected Rectangle RectangleDestination { get; set; }
protected Size SizeDrawing { get; set; }
public Boolean ZoomOnMouseWheel { get; set; }
#endregion
#region Events
private void ARTImageControl_Resize(Object sender, EventArgs e)
{
this.UpdateSizeDrawing();
if (this.ZoomToFit)
{
this.Invalidate();
}
}
private void ARTImageControl_MouseDown(Object sender, MouseEventArgs e)
{
if (this.InternalImage != null)
{
this.InternalPointMouseDownStart = new Point(e.X, e.Y);
this.Focus();
}
}
private void ARTImageControl_MouseMove(Object sender, MouseEventArgs e)
{
if (this.Image != null)
{
if (e.Button == MouseButtons.Left)
{
if (this.ActiveTool == ARTImageControlToolType.Pan)
{
Int32 DeltaX = InternalPointMouseDownStart.X - e.X;
Int32 DeltaY = InternalPointMouseDownStart.Y - e.Y;
Int32 pointOriginX = Convert.ToInt32(InternalPointDrawingOrigin.X + (DeltaX / InternalZoomFactor));
Int32 pointOriginY = Convert.ToInt32(InternalPointDrawingOrigin.Y + (DeltaY / InternalZoomFactor));
this.InternalPointDrawingOrigin = new Point(pointOriginX, pointOriginY);
this.CheckBoundaries();
this.InternalPointMouseDownStart = new Point(e.X, e.Y);
}
else
{
}
this.Invalidate();
}
}
}
private void ARTImageControl_MouseWheel(Object sender, MouseEventArgs e)
{
if (ZoomOnMouseWheel)
{
if (e.Delta > 0)
{
this.ZoomIn();
}
else if (e.Delta < 0)
{
this.ZoomOut();
}
}
}
#endregion
public ARTImageControlToolType ActiveTool { get; set; }
}
public enum ARTImageControlToolType
{
Pan = 0,
Rectangle = 1,
}
public abstract class ARTShape
{
public String Name { get; protected set; }
public abstract Rectangle GetBoundingRectangle();
}
public class ARTRectangle : ARTShape
{
public ARTRectangle(UInt32 width, UInt32 height)
{
this.Width = width;
this.Height = height;
}
public UInt32 Width { get; protected set; }
public UInt32 Height { get; protected set; }
public override Rectangle GetBoundingRectangle()
{
throw new NotImplementedException();
}
public Color ColorInactive { get; protected set; }
public Color ColorActive { get; protected set; }
}
public enum ScrollBarVisibility
{
Auto = 0,
Always = 1,
Never = 2,
}

Categories

Resources