How can i display in a middle of a progressBar some text? - c#

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
What i want to do is to show in the middle of the progressBar a text in red and that the text will blink until the progressBar operation end getting to 100%.
The text to display is: Processing Please Wait
In the end in the background completed event just to stop the blinking and display instead: Processing Please Wait to display: Operation Ended or something like that maybe: Process Ended or Finished.
I forgot to mention that my progressBar1 i'm using is a costum UserControl i did and not the regular progressBar for example after dragging this to my form1 designer this progressBar dosen't have the Increment property.
using System;
using System.Data;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing.Drawing2D;
namespace VistaStyleProgressBar
{
/// <summary>
/// A replacement for the default ProgressBar control.
/// </summary>
[DefaultEvent("ValueChanged")]
public class ProgressBar : System.Windows.Forms.UserControl
{
#region - Designer -
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
/// <summary>
/// Create the control and initialize it.
/// </summary>
public ProgressBar()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.Selectable, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.BackColor = Color.Transparent;
if (!InDesignMode())
{
mGlowAnimation.Tick += new EventHandler(mGlowAnimation_Tick);
mGlowAnimation.Interval = 15;
if (Value < MaxValue) { mGlowAnimation.Start(); }
}
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
#region - Component Designer -
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
//
// ProgressBar
//
this.Name = "ProgressBar";
this.Size = new System.Drawing.Size(264, 32);
this.Paint += new PaintEventHandler(ProgressBar_Paint);
}
#endregion
#endregion
#region - Properties -
private int mGlowPosition = -325;
private Timer mGlowAnimation = new Timer();
#region - Value -
private int mValue = 0;
/// <summary>
/// The value that is displayed on the progress bar.
/// </summary>
[Category("Value"),
DefaultValue(0),
Description("The value that is displayed on the progress bar.")]
public int Value
{
get { return mValue; }
set
{
if (value > MaxValue || value < MinValue) { return; }
mValue = value;
if (value < MaxValue) { mGlowAnimation.Start(); }
if (value == MaxValue) { mGlowAnimation.Stop(); }
ValueChangedHandler vc = ValueChanged;
if (vc != null) { vc(this, new System.EventArgs()); }
this.Invalidate();
}
}
private int mMaxValue = 100;
/// <summary>
/// The maximum value for the Value property.
/// </summary>
[Category("Value"),
DefaultValue(100),
Description("The maximum value for the Value property.")]
public int MaxValue
{
get { return mMaxValue; }
set
{
mMaxValue = value;
if (value > MaxValue) { Value = MaxValue; }
if (Value < MaxValue) { mGlowAnimation.Start(); }
MaxChangedHandler mc = MaxChanged;
if (mc != null) { mc(this, new System.EventArgs()); }
this.Invalidate();
}
}
private int mMinValue = 0;
/// <summary>
/// The minimum value for the Value property.
/// </summary>
[Category("Value"),
DefaultValue(0),
Description("The minimum value for the Value property.")]
public int MinValue
{
get { return mMinValue; }
set
{
mMinValue = value;
if (value < MinValue) { Value = MinValue; }
MinChangedHandler mc = MinChanged;
if (mc != null) { mc(this, new System.EventArgs()); }
this.Invalidate();
}
}
#endregion
#region - Bar -
private Color mStartColor = Color.FromArgb(0, 211, 40);
/// <summary>
/// The start color for the progress bar.
/// 210, 000, 000 = Red
/// 210, 202, 000 = Yellow
/// 000, 163, 211 = Blue
/// 000, 211, 040 = Green
/// </summary>
[Category("Bar"),
DefaultValue(typeof(Color), "210, 0, 0"),
Description("The start color for the progress bar." +
"210, 000, 000 = Red\n" +
"210, 202, 000 = Yellow\n" +
"000, 163, 211 = Blue\n" +
"000, 211, 040 = Green\n")]
public Color StartColor
{
get { return mStartColor; }
set { mStartColor = value; this.Invalidate(); }
}
private Color mEndColor = Color.FromArgb(0, 211, 40);
/// <summary>
/// The end color for the progress bar.
/// 210, 000, 000 = Red
/// 210, 202, 000 = Yellow
/// 000, 163, 211 = Blue
/// 000, 211, 040 = Green
/// </summary>
[Category("Bar"),
DefaultValue(typeof(Color), "0, 211, 40"),
Description("The end color for the progress bar." +
"210, 000, 000 = Red\n" +
"210, 202, 000 = Yellow\n" +
"000, 163, 211 = Blue\n" +
"000, 211, 040 = Green\n")]
public Color EndColor
{
get { return mEndColor; }
set { mEndColor = value; this.Invalidate(); }
}
#endregion
#region - Highlights and Glows -
private Color mHighlightColor = Color.White;
/// <summary>
/// The color of the highlights.
/// </summary>
[Category("Highlights and Glows"),
DefaultValue(typeof(Color), "White"),
Description("The color of the highlights.")]
public Color HighlightColor
{
get { return mHighlightColor; }
set { mHighlightColor = value; this.Invalidate(); }
}
private Color mBackgroundColor = Color.FromArgb(201, 201, 201);
/// <summary>
/// The color of the background.
/// </summary>
[Category("Highlights and Glows"),
DefaultValue(typeof(Color), "201,201,201"),
Description("The color of the background.")]
public Color BackgroundColor
{
get { return mBackgroundColor; }
set { mBackgroundColor = value; this.Invalidate(); }
}
private bool mAnimate = true;
/// <summary>
/// Whether the glow is animated.
/// </summary>
[Category("Highlights and Glows"),
DefaultValue(typeof(bool), "true"),
Description("Whether the glow is animated or not.")]
public bool Animate
{
get { return mAnimate; }
set
{
mAnimate = value;
if (value) { mGlowAnimation.Start(); } else { mGlowAnimation.Stop(); }
this.Invalidate();
}
}
private Color mGlowColor = Color.FromArgb(150, 255, 255, 255);
/// <summary>
/// The color of the glow.
/// </summary>
[Category("Highlights and Glows"),
DefaultValue(typeof(Color), "150, 255, 255, 255"),
Description("The color of the glow.")]
public Color GlowColor
{
get { return mGlowColor; }
set { mGlowColor = value; this.Invalidate(); }
}
#endregion
#endregion
#region - Drawing -
private void DrawBackground(Graphics g)
{
Rectangle r = this.ClientRectangle; r.Width--; r.Height--;
GraphicsPath rr = RoundRect(r, 2, 2, 2, 2);
g.FillPath(new SolidBrush(this.BackgroundColor), rr);
}
private void DrawBackgroundShadows(Graphics g)
{
Rectangle lr = new Rectangle(2, 2, 10, this.Height - 5);
LinearGradientBrush lg = new LinearGradientBrush(lr, Color.FromArgb(30, 0, 0, 0),
Color.Transparent,
LinearGradientMode.Horizontal);
lr.X--;
g.FillRectangle(lg, lr);
Rectangle rr = new Rectangle(this.Width - 12, 2, 10, this.Height - 5);
LinearGradientBrush rg = new LinearGradientBrush(rr, Color.Transparent,
Color.FromArgb(20, 0, 0, 0),
LinearGradientMode.Horizontal);
g.FillRectangle(rg, rr);
}
private void DrawBar(Graphics g)
{
Rectangle r = new Rectangle(1, 2, this.Width - 3, this.Height - 3);
r.Width = (int)(Value * 1.0F / (MaxValue - MinValue) * this.Width);
g.FillRectangle(new SolidBrush(GetIntermediateColor()), r);
}
private void DrawBarShadows(Graphics g)
{
Rectangle lr = new Rectangle(1, 2, 15, this.Height - 3);
LinearGradientBrush lg = new LinearGradientBrush(lr, Color.White, Color.White,
LinearGradientMode.Horizontal);
ColorBlend lc = new ColorBlend(3);
lc.Colors = new Color[] { Color.Transparent, Color.FromArgb(40, 0, 0, 0), Color.Transparent };
lc.Positions = new float[] { 0.0F, 0.2F, 1.0F };
lg.InterpolationColors = lc;
lr.X--;
g.FillRectangle(lg, lr);
Rectangle rr = new Rectangle(this.Width - 3, 2, 15, this.Height - 3);
rr.X = (int)(Value * 1.0F / (MaxValue - MinValue) * this.Width) - 14;
LinearGradientBrush rg = new LinearGradientBrush(rr, Color.Black, Color.Black,
LinearGradientMode.Horizontal);
ColorBlend rc = new ColorBlend(3);
rc.Colors = new Color[] { Color.Transparent, Color.FromArgb(40, 0, 0, 0), Color.Transparent };
rc.Positions = new float[] { 0.0F, 0.8F, 1.0F };
rg.InterpolationColors = rc;
g.FillRectangle(rg, rr);
}
private void DrawHighlight(Graphics g)
{
Rectangle tr = new Rectangle(1, 1, this.Width - 1, 6);
GraphicsPath tp = RoundRect(tr, 2, 2, 0, 0);
g.SetClip(tp);
LinearGradientBrush tg = new LinearGradientBrush(tr, Color.White,
Color.FromArgb(128, Color.White),
LinearGradientMode.Vertical);
g.FillPath(tg, tp);
g.ResetClip();
Rectangle br = new Rectangle(1, this.Height - 8, this.Width - 1, 6);
GraphicsPath bp = RoundRect(br, 0, 0, 2, 2);
g.SetClip(bp);
LinearGradientBrush bg = new LinearGradientBrush(br, Color.Transparent,
Color.FromArgb(100, this.HighlightColor),
LinearGradientMode.Vertical);
g.FillPath(bg, bp);
g.ResetClip();
}
private void DrawInnerStroke(Graphics g)
{
Rectangle r = this.ClientRectangle;
r.X++; r.Y++; r.Width -= 3; r.Height -= 3;
GraphicsPath rr = RoundRect(r, 2, 2, 2, 2);
g.DrawPath(new Pen(Color.FromArgb(100, Color.White)), rr);
}
private void DrawGlow(Graphics g)
{
Rectangle r = new Rectangle(mGlowPosition, 0, 60, this.Height);
LinearGradientBrush lgb = new LinearGradientBrush(r, Color.White, Color.White,
LinearGradientMode.Horizontal);
ColorBlend cb = new ColorBlend(4);
cb.Colors = new Color[] { Color.Transparent, this.GlowColor, this.GlowColor, Color.Transparent };
cb.Positions = new float[] { 0.0F, 0.5F, 0.6F, 1.0F };
lgb.InterpolationColors = cb;
Rectangle clip = new Rectangle(1, 2, this.Width - 3, this.Height - 3);
clip.Width = (int)(Value * 1.0F / (MaxValue - MinValue) * this.Width);
g.SetClip(clip);
g.FillRectangle(lgb, r);
g.ResetClip();
}
private void DrawOuterStroke(Graphics g)
{
Rectangle r = this.ClientRectangle; r.Width--; r.Height--;
GraphicsPath rr = RoundRect(r, 2, 2, 2, 2);
g.DrawPath(new Pen(Color.FromArgb(178, 178, 178)), rr);
}
#endregion
#region - Functions -
private GraphicsPath RoundRect(RectangleF r, float r1, float r2, float r3, float r4)
{
float x = r.X, y = r.Y, w = r.Width, h = r.Height;
GraphicsPath rr = new GraphicsPath();
rr.AddBezier(x, y + r1, x, y, x + r1, y, x + r1, y);
rr.AddLine(x + r1, y, x + w - r2, y);
rr.AddBezier(x + w - r2, y, x + w, y, x + w, y + r2, x + w, y + r2);
rr.AddLine(x + w, y + r2, x + w, y + h - r3);
rr.AddBezier(x + w, y + h - r3, x + w, y + h, x + w - r3, y + h, x + w - r3, y + h);
rr.AddLine(x + w - r3, y + h, x + r4, y + h);
rr.AddBezier(x + r4, y + h, x, y + h, x, y + h - r4, x, y + h - r4);
rr.AddLine(x, y + h - r4, x, y + r1);
return rr;
}
private bool InDesignMode()
{
return (LicenseManager.UsageMode == LicenseUsageMode.Designtime);
}
private Color GetIntermediateColor()
{
Color c = this.StartColor;
Color c2 = this.EndColor;
float pc = this.Value * 1.0F / (this.MaxValue - this.MinValue);
int ca = c.A, cr = c.R, cg = c.G, cb = c.B;
int c2a = c2.A, c2r = c2.R, c2g = c2.G, c2b = c2.B;
int a = (int)Math.Abs(ca + (ca - c2a) * pc);
int r = (int)Math.Abs(cr - ((cr - c2r) * pc));
int g = (int)Math.Abs(cg - ((cg - c2g) * pc));
int b = (int)Math.Abs(cb - ((cb - c2b) * pc));
if (a > 255) { a = 255; }
if (r > 255) { r = 255; }
if (g > 255) { g = 255; }
if (b > 255) { b = 255; }
return (Color.FromArgb(a, r, g, b));
}
#endregion
#region - Other -
private void ProgressBar_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
DrawBackground(e.Graphics);
DrawBackgroundShadows(e.Graphics);
DrawBar(e.Graphics);
DrawBarShadows(e.Graphics);
DrawHighlight(e.Graphics);
DrawInnerStroke(e.Graphics);
DrawGlow(e.Graphics);
DrawOuterStroke(e.Graphics);
}
private void mGlowAnimation_Tick(object sender, EventArgs e)
{
if (this.Animate)
{
mGlowPosition += 4;
if (mGlowPosition > this.Width)
{
mGlowPosition = -300;
}
this.Invalidate();
}
else
{
mGlowAnimation.Stop();
mGlowPosition = -320;
}
}
#endregion
#region - Events -
/// <summary>
/// When the Value property is changed.
/// </summary>
public delegate void ValueChangedHandler(object sender, EventArgs e);
/// <summary>
/// When the Value property is changed.
/// </summary>
public event ValueChangedHandler ValueChanged;
/// <summary>
/// When the MinValue property is changed.
/// </summary>
public delegate void MinChangedHandler(object sender, EventArgs e);
/// <summary>
/// When the MinValue property is changed.
/// </summary>
public event MinChangedHandler MinChanged;
/// <summary>
/// When the MaxValue property is changed.
/// </summary>
public delegate void MaxChangedHandler(object sender, EventArgs e);
/// <summary>
/// When the MaxValue property is changed.
/// </summary>
public event MaxChangedHandler MaxChanged;
#endregion
}
}
I tried this case the Increment property not exist so it's a problem.
private void timer2_Tick(object sender, EventArgs e)
{
progressBar1.Increment(+1);
int percent = progressBar1.Value;
progressBar1
.CreateGraphics()
.DrawString(
percent.ToString() + "%",
new Font("Arial", (float)8.25, FontStyle.Regular),
Brushes.Black,
new PointF(progressBar1.Width / 2 - 10,
progressBar1.Height / 2 - 7)
);
if (progressBar1.Value >= 99)
{
timer2.Stop();
this.Close();
}
}

What I would do is make a label with the desired text and then add to the background worker:
if ( progressBar1.Value == 100% )
{
label1.Text = "Process finished";
}
If you cant see the label and it is hidden behind the processBar change the location of the label to front
Hope I could help!
Edit: This link should explain the blinking event How to implement a blinking label on a form

Have wrote no blinking/flickering TextProgressBar
You can find source code here: https://github.com/ukushu/TextProgressBar
Sample:

Related

C# Changing color of group box border color [duplicate]

In C#.NET I am trying to programmatically change the color of the border in a group box.
Update: This question was asked when I was working on a winforms system before we switched to .NET.
Just add paint event.
private void groupBox1_Paint(object sender, PaintEventArgs e)
{
GroupBox box = sender as GroupBox;
DrawGroupBox(box, e.Graphics, Color.Red, Color.Blue);
}
private void DrawGroupBox(GroupBox box, Graphics g, Color textColor, Color borderColor)
{
if (box != null)
{
Brush textBrush = new SolidBrush(textColor);
Brush borderBrush = new SolidBrush(borderColor);
Pen borderPen = new Pen(borderBrush);
SizeF strSize = g.MeasureString(box.Text, box.Font);
Rectangle rect = new Rectangle(box.ClientRectangle.X,
box.ClientRectangle.Y + (int)(strSize.Height / 2),
box.ClientRectangle.Width - 1,
box.ClientRectangle.Height - (int)(strSize.Height / 2) - 1);
// Clear text and border
g.Clear(this.BackColor);
// Draw text
g.DrawString(box.Text, box.Font, textBrush, box.Padding.Left, 0);
// Drawing Border
//Left
g.DrawLine(borderPen, rect.Location, new Point(rect.X, rect.Y + rect.Height));
//Right
g.DrawLine(borderPen, new Point(rect.X + rect.Width, rect.Y), new Point(rect.X + rect.Width, rect.Y + rect.Height));
//Bottom
g.DrawLine(borderPen, new Point(rect.X, rect.Y + rect.Height), new Point(rect.X + rect.Width, rect.Y + rect.Height));
//Top1
g.DrawLine(borderPen, new Point(rect.X, rect.Y), new Point(rect.X + box.Padding.Left, rect.Y));
//Top2
g.DrawLine(borderPen, new Point(rect.X + box.Padding.Left + (int)(strSize.Width), rect.Y), new Point(rect.X + rect.Width, rect.Y));
}
}
Building on the previous answer, a better solution that includes the label for the group box:
groupBox1.Paint += PaintBorderlessGroupBox;
private void PaintBorderlessGroupBox(object sender, PaintEventArgs p)
{
GroupBox box = (GroupBox)sender;
p.Graphics.Clear(SystemColors.Control);
p.Graphics.DrawString(box.Text, box.Font, Brushes.Black, 0, 0);
}
You might want to adjust the x/y for the text, but for my use this is just right.
Just set the paint action on any object (not just buttons) to this method to draw a border.
private void UserControl1_Paint(object sender, PaintEventArgs e)
{
ControlPaint.DrawBorder(e.Graphics, this.ClientRectangle, Color.Red, ButtonBorderStyle.Solid);
}
It still wont be pretty and rounded like the original, but it is much simpler.
FWIW, this is the implementation I used. It's a child of GroupBox but allows setting not only the BorderColor, but also the thickness of the border and the radius of the rounded corners. Also, you can set the amount of indent you want for the GroupBox label, and using a negative indent indents from the right side.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace BorderedGroupBox
{
public class BorderedGroupBox : GroupBox
{
private Color _borderColor = Color.Black;
private int _borderWidth = 2;
private int _borderRadius = 5;
private int _textIndent = 10;
public BorderedGroupBox() : base()
{
InitializeComponent();
this.Paint += this.BorderedGroupBox_Paint;
}
public BorderedGroupBox(int width, float radius, Color color) : base()
{
this._borderWidth = Math.Max(1,width);
this._borderColor = color;
this._borderRadius = Math.Max(0,radius);
InitializeComponent();
this.Paint += this.BorderedGroupBox_Paint;
}
public Color BorderColor
{
get => this._borderColor;
set
{
this._borderColor = value;
DrawGroupBox();
}
}
public int BorderWidth
{
get => this._borderWidth;
set
{
if (value > 0)
{
this._borderWidth = Math.Min(value, 10);
DrawGroupBox();
}
}
}
public int BorderRadius
{
get => this._borderRadius;
set
{ // Setting a radius of 0 produces square corners...
if (value >= 0)
{
this._borderRadius = value;
this.DrawGroupBox();
}
}
}
public int LabelIndent
{
get => this._textIndent;
set
{
this._textIndent = value;
this.DrawGroupBox();
}
}
private void BorderedGroupBox_Paint(object sender, PaintEventArgs e) =>
DrawGroupBox(e.Graphics);
private void DrawGroupBox() =>
this.DrawGroupBox(this.CreateGraphics());
private void DrawGroupBox(Graphics g)
{
Brush textBrush = new SolidBrush(this.ForeColor);
SizeF strSize = g.MeasureString(this.Text, this.Font);
Brush borderBrush = new SolidBrush(this.BorderColor);
Pen borderPen = new Pen(borderBrush,(float)this._borderWidth);
Rectangle rect = new Rectangle(this.ClientRectangle.X,
this.ClientRectangle.Y + (int)(strSize.Height / 2),
this.ClientRectangle.Width - 1,
this.ClientRectangle.Height - (int)(strSize.Height / 2) - 1);
Brush labelBrush = new SolidBrush(this.BackColor);
// Clear text and border
g.Clear(this.BackColor);
// Drawing Border (added "Fix" from Jim Fell, Oct 6, '18)
int rectX = (0 == this._borderWidth % 2) ? rect.X + this._borderWidth / 2 : rect.X + 1 + this._borderWidth / 2;
int rectHeight = (0 == this._borderWidth % 2) ? rect.Height - this._borderWidth / 2 : rect.Height - 1 - this._borderWidth / 2;
// NOTE DIFFERENCE: rectX vs rect.X and rectHeight vs rect.Height
g.DrawRoundedRectangle(borderPen, rectX, rect.Y, rect.Width, rectHeight, (float)this._borderRadius);
// Draw text
if (this.Text.Length > 0)
{
// Do some work to ensure we don't put the label outside
// of the box, regardless of what value is assigned to the Indent:
int width = (int)rect.Width, posX;
posX = (this._textIndent < 0) ? Math.Max(0-width,this._textIndent) : Math.Min(width, this._textIndent);
posX = (posX < 0) ? rect.Width + posX - (int)strSize.Width : posX;
g.FillRectangle(labelBrush, posX, 0, strSize.Width, strSize.Height);
g.DrawString(this.Text, this.Font, textBrush, posX, 0);
}
}
#region Component Designer generated code
/// <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);
}
/// <summary>Required method for Designer support - Don't modify!</summary>
private void InitializeComponent() => components = new System.ComponentModel.Container();
#endregion
}
}
To make it work, you also have to extend the base Graphics class (Note: this is derived from some code I found on here once when I was trying to create a rounded-corners Panel control, but I can't find the original post to link here):
static class GraphicsExtension
{
private static GraphicsPath GenerateRoundedRectangle(
this Graphics graphics,
RectangleF rectangle,
float radius)
{
float diameter;
GraphicsPath path = new GraphicsPath();
if (radius <= 0.0F)
{
path.AddRectangle(rectangle);
path.CloseFigure();
return path;
}
else
{
if (radius >= (Math.Min(rectangle.Width, rectangle.Height)) / 2.0)
return graphics.GenerateCapsule(rectangle);
diameter = radius * 2.0F;
SizeF sizeF = new SizeF(diameter, diameter);
RectangleF arc = new RectangleF(rectangle.Location, sizeF);
path.AddArc(arc, 180, 90);
arc.X = rectangle.Right - diameter;
path.AddArc(arc, 270, 90);
arc.Y = rectangle.Bottom - diameter;
path.AddArc(arc, 0, 90);
arc.X = rectangle.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
}
return path;
}
private static GraphicsPath GenerateCapsule(
this Graphics graphics,
RectangleF baseRect)
{
float diameter;
RectangleF arc;
GraphicsPath path = new GraphicsPath();
try
{
if (baseRect.Width > baseRect.Height)
{
diameter = baseRect.Height;
SizeF sizeF = new SizeF(diameter, diameter);
arc = new RectangleF(baseRect.Location, sizeF);
path.AddArc(arc, 90, 180);
arc.X = baseRect.Right - diameter;
path.AddArc(arc, 270, 180);
}
else if (baseRect.Width < baseRect.Height)
{
diameter = baseRect.Width;
SizeF sizeF = new SizeF(diameter, diameter);
arc = new RectangleF(baseRect.Location, sizeF);
path.AddArc(arc, 180, 180);
arc.Y = baseRect.Bottom - diameter;
path.AddArc(arc, 0, 180);
}
else path.AddEllipse(baseRect);
}
catch { path.AddEllipse(baseRect); }
finally { path.CloseFigure(); }
return path;
}
/// <summary>
/// Draws a rounded rectangle specified by a pair of coordinates, a width, a height and the radius
/// for the arcs that make the rounded edges.
/// </summary>
/// <param name="brush">System.Drawing.Pen that determines the color, width and style of the rectangle.</param>
/// <param name="x">The x-coordinate of the upper-left corner of the rectangle to draw.</param>
/// <param name="y">The y-coordinate of the upper-left corner of the rectangle to draw.</param>
/// <param name="width">Width of the rectangle to draw.</param>
/// <param name="height">Height of the rectangle to draw.</param>
/// <param name="radius">The radius of the arc used for the rounded edges.</param>
public static void DrawRoundedRectangle(
this Graphics graphics,
Pen pen,
float x,
float y,
float width,
float height,
float radius)
{
RectangleF rectangle = new RectangleF(x, y, width, height);
GraphicsPath path = graphics.GenerateRoundedRectangle(rectangle, radius);
SmoothingMode old = graphics.SmoothingMode;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.DrawPath(pen, path);
graphics.SmoothingMode = old;
}
/// <summary>
/// Draws a rounded rectangle specified by a pair of coordinates, a width, a height and the radius
/// for the arcs that make the rounded edges.
/// </summary>
/// <param name="brush">System.Drawing.Pen that determines the color, width and style of the rectangle.</param>
/// <param name="x">The x-coordinate of the upper-left corner of the rectangle to draw.</param>
/// <param name="y">The y-coordinate of the upper-left corner of the rectangle to draw.</param>
/// <param name="width">Width of the rectangle to draw.</param>
/// <param name="height">Height of the rectangle to draw.</param>
/// <param name="radius">The radius of the arc used for the rounded edges.</param>
public static void DrawRoundedRectangle(
this Graphics graphics,
Pen pen,
int x,
int y,
int width,
int height,
int radius)
{
graphics.DrawRoundedRectangle(
pen,
Convert.ToSingle(x),
Convert.ToSingle(y),
Convert.ToSingle(width),
Convert.ToSingle(height),
Convert.ToSingle(radius));
}
}
I'm not sure this applies to every case, but thanks to this thread, we quickly hooked into the Paint event programmatically using:
GroupBox box = new GroupBox();
[...]
box.Paint += delegate(object o, PaintEventArgs p)
{
p.Graphics.Clear(someColorHere);
};
Cheers!
I have achieved same border with something which might be simpler to understand for newbies:
private void groupSchitaCentru_Paint(object sender, PaintEventArgs e)
{
Pen blackPen = new Pen(Color.Black, 2);
Point pointTopLeft = new Point(0, 7);
Point pointBottomLeft = new Point(0, groupSchitaCentru.ClientRectangle.Height);
Point pointTopRight = new Point(groupSchitaCentru.ClientRectangle.Width, 7);
Point pointBottomRight = new Point(groupSchitaCentru.ClientRectangle.Width, groupSchitaCentru.ClientRectangle.Height);
e.Graphics.DrawLine(blackPen, pointTopLeft, pointBottomLeft);
e.Graphics.DrawLine(blackPen, pointTopLeft, pointTopRight);
e.Graphics.DrawLine(blackPen, pointBottomRight, pointTopRight);
e.Graphics.DrawLine(blackPen, pointBottomLeft, pointBottomRight);
}
Set the Paint event on the GroupBox control. In this example the name of my control is "groupSchitaCentru". One needs this event because of its parameter e.
Set up a pen object by making use of the System.Drawing.Pen class : https://msdn.microsoft.com/en-us/library/f956fzw1(v=vs.110).aspx
Set the points which represent the corners of the rectangle represented by the control. Used the property ClientRectangle of the the control to get its dimensions.
I used for TopLeft (0,7) because I want to respect the borders of the control, and draw the line about the its text.
To get more information about the coordinates system walk here : https://learn.microsoft.com/en-us/dotnet/framework/winforms/windows-forms-coordinates
I do not know, may be it helps someone looking to achieve this border adjustment thing.
This tweak to Jim Fell's code placed the borders a little better for me, but it's too long to add as a comment
...
Rectangle rect = new Rectangle(this.ClientRectangle.X,
this.ClientRectangle.Y + (int)(strSize.Height / 2),
this.ClientRectangle.Width,
this.ClientRectangle.Height - (int)(strSize.Height / 2));
Brush labelBrush = new SolidBrush(this.BackColor);
// Clear text and border
g.Clear(this.BackColor);
int drawX = rect.X;
int drawY = rect.Y;
int drawWidth = rect.Width;
int drawHeight = rect.Height;
if (this._borderWidth > 0)
{
drawX += this._borderWidth / 2;
drawY += this._borderWidth / 2;
drawWidth -= this._borderWidth;
drawHeight -= this._borderWidth;
if (this._borderWidth % 2 == 0)
{
drawX -= 1;
drawWidth += 1;
drawY -= 1;
drawHeight += 1;
}
}
g.DrawRoundedRectangle(borderPen, drawX, drawY, drawWidth, drawHeight, (float)this._borderRadius);

C# Mixing up 2 different Custom Label Class

I have a custom label class which allows label control to have 4 different backcolors. (this gives the possibility to fill each label backcolor with 4 colors in the same time using .percentageX property).
Everything working nice so far.
public class CustomLabel: System.Windows.Forms.Label
{
private Color m_color1 = Color.LightGreen; //first color
private Color m_color2 = Color.DarkBlue; // second color
private Color m_color3 = Color.Gray; // third color
private Color m_color4 = Color.LightYellow; //fourth color?!
private int Percentagex1 = 30;
private int Percentagex2 = 20;
private int Percentagex3 = 50;
private int Percentagex4 = 0;
public Color Color1
{
get { return m_color1; }
set { m_color1 = value; Invalidate(); }
}
public Color Color2
{
get { return m_color2; }
set { m_color2 = value; Invalidate(); }
}
public Color Color3
{
get { return m_color3; }
set { m_color3 = value; Invalidate(); }
}
public Color Color4
{
get { return m_color4; }
set { m_color4 = value; Invalidate(); }
}
public int Percentage1
{
get { return Percentagex1; }
set { Percentagex1 = value; Invalidate(); }
}
public int Percentage2
{
get { return Percentagex2; }
set { Percentagex2 = value; Invalidate(); }
}
public int Percentage3
{
get { return Percentagex3; }
set { Percentagex3 = value; Invalidate(); }
}
public int Percentage4
{
get { return Percentagex4; }
set { Percentagex4 = value; Invalidate(); }
}
//public Labelhand()
//{
//}
protected override void OnPaint(PaintEventArgs e)
{
int x = Convert.ToInt32(this.Width * Percentagex1 / 100);
int x2 = Convert.ToInt32(this.Width * Percentagex2 / 100);
int x3 = Convert.ToInt32(this.Width * Percentagex3 / 100);
int x4 = Convert.ToInt32(this.Width * Percentagex4 / 100);
SolidBrush brush = new SolidBrush(m_color1);
SolidBrush brush2 = new SolidBrush(m_color2);
SolidBrush brush3 = new SolidBrush(m_color3);
SolidBrush brush4 = new SolidBrush(m_color4);
Graphics g = e.Graphics;
g.FillRectangle(brush, new Rectangle(0, 0, x, this.Height));
g.FillRectangle(brush2, new Rectangle(x, 0, x2, this.Height));
g.FillRectangle(brush3, new Rectangle(x + x2, 0, x3, this.Height));
g.FillRectangle(brush4, new Rectangle(x + x2 + x3, 0, x4, this.Height));
base.OnPaint(e);
}
}
Then, I got another custom label class, this one gives the labels nice rounded corners.
public class RoundedLabel
{
[Browsable(true)]
public Color _BackColor { get; set; }
public RoundedLabel()
{
this.DoubleBuffered = true;
}
private GraphicsPath _getRoundRectangle(Rectangle rectangle)
{
int cornerRadius = 15; // change this value according to your needs
int diminisher = 1;
GraphicsPath path = new GraphicsPath();
path.AddArc(rectangle.X, rectangle.Y, cornerRadius, cornerRadius, 180, 90);
path.AddArc(rectangle.X + rectangle.Width - cornerRadius - diminisher, rectangle.Y, cornerRadius, cornerRadius, 270, 90);
path.AddArc(rectangle.X + rectangle.Width - cornerRadius - diminisher, rectangle.Y + rectangle.Height - cornerRadius - diminisher, cornerRadius, cornerRadius, 0, 90);
path.AddArc(rectangle.X, rectangle.Y + rectangle.Height - cornerRadius - diminisher, cornerRadius, cornerRadius, 90, 90);
path.CloseAllFigures();
return path;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (var graphicsPath = _getRoundRectangle(this.ClientRectangle))
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
using (var brush = new SolidBrush(_BackColor))
e.Graphics.FillPath(brush, graphicsPath);
using (var pen = new Pen(_BackColor, 1.0f))
e.Graphics.DrawPath(pen, graphicsPath);
TextRenderer.DrawText(e.Graphics, Text, this.Font, this.ClientRectangle, this.ForeColor);
}
}
}
This is also working nicely, we set --> label.backcolor = Color.Transparent;
and then the _backcolor property will show a label with rounded corners.
The question comes how to mix these 2 class together to get rounder corners with all 4 colors, but keeping all 4 rounded corners at all times.
I tried to declare the second class like this:
public class RoundedLabel: CustomLabel
Unfortunately, the rounded corners are then only available to the _backcolor property. And not to the color1,2,3,4 ones.
thanks all for any advice!

Drawing a straight line from points

I am trying to make a simple chart application and I have stumbled upon a problem. I have an array of points which represent a 45 degree line (y = x) of 100 points. I then try to draw the line by converting each point's value to its pixel value and then draw lines from one point to another using graphic.DrawLines().
The the basic code:
struct MyPoint
{
double X { set; get; }
double Y { set; get; }
}
List<MyPoint> Values;
List<System.Drawing.Point> Points;
for (int i = 0; i < Values.Count; i++)
{
// convert point value to pixel coordinate
int x = (int)((Values[i].X - MinimumX) * (double)Control.Width / (MaximumX - MinimumX) + 0.5);
int y = (int)((Values[i].Y - MinimumY) * (double)Control.Height / (MaximumY - MinimumY)) + 0.5;
Points.Add(new System.Drawing.Point(x, y));
}
graphic.DrawLines(pen, Points.ToArray());
Note: MaximumX and MaximumY are 100 (number of points) and minimums are 0.
The thing is that this kind of line is not very straight :) So my question is, how would I draw a straight line through those points, so that it would look the same as
graphic.DrawLine(pen, 0, 0, Control.Width-1, Control.Height-1);
Thanks in advance!
Since you are doing a graph, I have a basic framework for fitting values within a control like this example:
public struct MyPoint
{
public double X { set; get; }
public double Y { set; get; }
public PointF ToPoint()
{
return new PointF((float)X, (float)Y);
}
}
/// <summary>
/// For http://stackoverflow.com/questions/19459519/drawing-a-straight-line-from-points
/// </summary>
public partial class Form1 : Form
{
List<MyPoint> points;
public Form1()
{
InitializeComponent();
// Initialize points to draw
points=new List<MyPoint>(100);
for (int i=0; i<=100; i++)
{
double θ=2*Math.PI*(i/100.0);
double x=(1+θ/Math.PI)*Math.Cos(θ);
double y=(1+θ/Math.PI)*Math.Sin(θ);
points.Add(new MyPoint() { X=x, Y=y });
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
// smooth graphics
e.Graphics.SmoothingMode=SmoothingMode.AntiAlias;
// set margins inside the control client area in pixels
var margin=new System.Drawing.Printing.Margins(16, 16, 16, 16);
// set the domain of (x,y) values
var range=new RectangleF(-3, -3, 6, 6);
// scale graphics
ScaleGraphics(e.Graphics, pictureBox1, range, margin);
// convert points to pixels
PointF[] pixels=points.Select((v) => v.ToPoint()).ToArray();
// draw arrow axes
using (var pen=new Pen(Color.Black, 0))
{
pen.EndCap=System.Drawing.Drawing2D.LineCap.ArrowAnchor;
e.Graphics.DrawLine(pen, range.Left, 0.0f, range.Right, 0.0f);
e.Graphics.DrawLine(pen, 0.0f, range.Top, 0.0f, range.Bottom);
}
// draw bounding rectangle (on margin)
using (var pen=new Pen(Color.LightGray, 0))
{
pen.DashStyle=DashStyle.Dash;
e.Graphics.DrawRectangle(pen, Rectangle.Round(range));
}
// draw curve
using (var pen = new Pen(Color.Blue, 0))
{
//e.Graphics.DrawLines(pen, pixels);
e.Graphics.DrawCurve(pen, pixels);
}
}
/// <summary>
/// Scales the Graphics to fit a Control, given a domain of x,y values and side margins in pixels
/// </summary>
/// <param name="g">The Graphics object</param>
/// <param name="control">The Control</param>
/// <param name="domain">The value domain</param>
/// <param name="margin">The margin</param>
void ScaleGraphics(Graphics g, Control control, RectangleF domain, Margins margin)
{
// Find the drawable area in pixels (control-margins)
int W=control.Width-margin.Left-margin.Right;
int H=control.Height-margin.Bottom-margin.Top;
// Ensure drawable area is at least 1 pixel wide
W=Math.Max(1, W);
H=Math.Max(1, H);
// Find the origin (0,0) in pixels
float OX=margin.Left-W*(domain.Left/domain.Width);
float OY=margin.Top+H*(1+domain.Top/domain.Height);
// Find the scale to fit the control
float SX=W/domain.Width;
float SY=H/domain.Height;
// Transform the Graphics
g.TranslateTransform(OX, OY);
g.ScaleTransform(SX, -SY);
}
private void pictureBox1_SizeChanged(object sender, EventArgs e)
{
// redraw on resize
pictureBox1.Refresh();
}
}
I think the problem is because your points are Integers. Precisions are lost.
Trying using
float x = (float)((Values[i] ...
List<System.Drawing.PointF> PointFs;
instead of
int x = (int)((Values[i] ...
List<System.Drawing.Point> Points;
You probably need something like this:
private void DrawLine()
{
int xInitial = 0, yInitial = 0, xFinal = Control.Width - 1, yFinal = Control.Height - 1;
int dx = xFinal - xInitial, dy = yFinal - yInitial, steps, k, xf, yf;
float xIncrement, yIncrement, x = xInitial, y = yInitial;
if (Math.Abs(dx) > Math.Abs(dy))
steps = Math.Abs(dx);
else
steps = Math.Abs(dy);
xIncrement = dx / (float)steps;
yIncrement = dy / (float)steps;
for (k = 0; k < steps; k++)
{
x += xIncrement;
xf = (int)x;
y += yIncrement;
yf = (int)y;
Points.Add(new System.Drawing.Point(xf, yf));
}
graphic.DrawLines(pen, Points.ToArray());
}
For more details see:
Bresenham's line algorithm
If your points already calculated correctly by algorithm, than simple:
int x = (int) Values[i].X;
int y = (int) Values[i].Y;
Should be enough.
here is my solution:
System.Drawing.Pen myPen;
myPen = new System.Drawing.Pen(System.Drawing.Color.Red);
System.Drawing.Graphics formGraphics = this.CreateGraphics();
formGraphics.DrawLine(myPen, 0, 0, 200, 200);
myPen.Dispose();
formGraphics.Dispose();

Override Paint a control in C#

Can anybody provide a basic tutorial on how to override the paint event in C#? I dont have much experience in C# and this is some part that I dont get easily as I cannot follow some tutorials and tricks as I dont get the concept of overriding in C#.
This is a very basic example, which will (should!) draw a red 'X':
public class FacadeControl : Control
{
private Pen invalidPen;
public FacadeControl()
{
invalidPen = new Pen(Color.Red, 2);
SetStyle(ControlStyles.ResizeRedraw, true); // make sure the control is redrawn every time it is resized
}
protected override void OnPaint(PaintEventArgs pe)
{
// get the graphics object to use to draw
Graphics g = pe.Graphics;
g.DrawLine(invalidPen, 0, 0, Width, Height);
g.DrawLine(invalidPen, 0, Height, Width, 0);
}
}
}
For example :
public class FirstControl : Control{
public FirstControl() {}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
e.Graphics.DrawString(Text, Font, new SolidBrush(ForeColor), ClientRectangle);
}
}
just don't forget to call the base paint handler before writing yours
this is the button
I would like to share my code:
The button class:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Windows.Forms;
namespace Controles.Buttons
{
/// <summary>
/// Clase personalizada button.
/// Jorge Arturo Avilés Nuñez
/// Zapopan, Jalisco, México
/// 18-DIC-2017
/// </summary>
public class SansationRoundButton : Button
{
#region members
private TextRenderingHint _hint = TextRenderingHint.AntiAlias;
private const int FlagMouseOver = 0x0001;
private const int FlagMouseDown = 0x0002;
private int state = 0;
#endregion
#region Constructor
public SansationRoundButton()
{
this.FlatStyle = FlatStyle.Flat;
this.FlatAppearance.BorderSize = 0;
this.Font = new System.Drawing.Font("Sansation", 21.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0);
this.UseVisualStyleBackColor = true;
this.Cursor = Cursors.Hand;
}
#endregion
#region Internal methods and properties
internal bool OwnerDraw
{
get
{
return FlatStyle != FlatStyle.System;
}
}
internal bool MouseIsOver
{
get
{
return GetFlag(FlagMouseOver);
}
}
#endregion
#region Private methods
private bool GetFlag(int flag)
{
return ((state & flag) == flag);
}
private void SetFlag(int flag, bool value)
{
bool oldValue = ((state & flag) != 0);
if (value)
state |= flag;
else
state &= ~flag;
if (OwnerDraw && (flag & FlagMouseDown) != 0 && value != oldValue)
AccessibilityNotifyClients(AccessibleEvents.StateChange, -1);
}
#endregion
#region Overrides
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
SetFlag(FlagMouseOver, true);
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
SetFlag(FlagMouseOver, false);
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
e.Graphics.Clear(Color.White);
e.Graphics.TextRenderingHint = this.TextRenderingHint;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
Color backColor = this.MouseIsOver ? this.BackColorMouseOver : this.BackColor;
Color borderColor = this.MouseIsOver ? this.BorderColorMouseOver : this.BorderColor;
e.Graphics.DrawRoundedRectangle(new Pen(borderColor), 0, 0, this.Width - 1, this.Height - 1, 10);
e.Graphics.FillRoundedRectangle(new SolidBrush(backColor), 0, 0, this.Width - 1, this.Height - 1, 10);
StringFormat sr = BaseControl.CreateStringFormat(this, this.TextAlign, false, this.UseMnemonic);
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(ForeColor), ClientRectangle, sr);
}
#endregion
#region public properties
public TextRenderingHint TextRenderingHint
{
get { return this._hint; }
set { this._hint = value; }
}
public new bool ShowKeyboardCues
{
get
{
return base.ShowKeyboardCues;
}
}
public Color BackColorMouseOver { get; set; } = Color.Red;
public Color BorderColor { get; set; } = Color.Black;
public Color BorderColorMouseOver { get; set; } = Color.Black;
#endregion
}
}
this class BaseControl creates a new instance of StringFormat object that is needed to call the DrawString method from the Graphics object.
using System.Drawing;
using System.Windows.Forms;
namespace Controles.Buttons
{
public class BaseControl
{
private static readonly ContentAlignment anyRight = ContentAlignment.TopRight | ContentAlignment.MiddleRight | ContentAlignment.BottomRight;
private static readonly ContentAlignment anyBottom = ContentAlignment.BottomLeft | ContentAlignment.BottomCenter | ContentAlignment.BottomRight;
private static readonly ContentAlignment anyCenter = ContentAlignment.TopCenter | ContentAlignment.MiddleCenter | ContentAlignment.BottomCenter;
private static readonly ContentAlignment anyMiddle = ContentAlignment.MiddleLeft | ContentAlignment.MiddleCenter | ContentAlignment.MiddleRight;
static StringAlignment TranslateAlignment(ContentAlignment align)
{
StringAlignment result;
if ((align & anyRight) != 0)
result = StringAlignment.Far;
else if ((align & anyCenter) != 0)
result = StringAlignment.Center;
else
result = StringAlignment.Near;
return result;
}
static StringAlignment TranslateLineAlignment(ContentAlignment align)
{
StringAlignment result;
if ((align & anyBottom) != 0)
{
result = StringAlignment.Far;
}
else if ((align & anyMiddle) != 0)
{
result = StringAlignment.Center;
}
else
{
result = StringAlignment.Near;
}
return result;
}
static StringFormat StringFormatForAlignment(ContentAlignment align)
{
StringFormat output = new StringFormat();
output.Alignment = TranslateAlignment(align);
output.LineAlignment = TranslateLineAlignment(align);
return output;
}
public static StringFormat CreateStringFormat(SansationRoundButton ctl, ContentAlignment textAlign, bool showEllipsis, bool useMnemonic)
{
StringFormat stringFormat = StringFormatForAlignment(textAlign);
// Adjust string format for Rtl controls
if (ctl.RightToLeft == RightToLeft.Yes)
{
stringFormat.FormatFlags |= StringFormatFlags.DirectionRightToLeft;
}
if (showEllipsis)
{
stringFormat.Trimming = StringTrimming.EllipsisCharacter;
stringFormat.FormatFlags |= StringFormatFlags.LineLimit;
}
if (!useMnemonic)
{
stringFormat.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.None;
}
else if (ctl.ShowKeyboardCues)
{
stringFormat.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Show;
}
else
{
stringFormat.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Hide;
}
if (ctl.AutoSize)
{
stringFormat.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
}
return stringFormat;
}
}
}
finally this class is used to create and fill the rectangles.
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace Plasmoid.Extensions
{
static class GraphicsExtension
{
private static GraphicsPath GenerateRoundedRectangle(
this Graphics graphics,
RectangleF rectangle,
float radius)
{
float diameter;
GraphicsPath path = new GraphicsPath();
if (radius <= 0.0F)
{
path.AddRectangle(rectangle);
path.CloseFigure();
return path;
}
else
{
if (radius >= (Math.Min(rectangle.Width, rectangle.Height)) / 2.0)
return graphics.GenerateCapsule(rectangle);
diameter = radius * 2.0F;
SizeF sizeF = new SizeF(diameter, diameter);
RectangleF arc = new RectangleF(rectangle.Location, sizeF);
path.AddArc(arc, 180, 90);
arc.X = rectangle.Right - diameter;
path.AddArc(arc, 270, 90);
arc.Y = rectangle.Bottom - diameter;
path.AddArc(arc, 0, 90);
arc.X = rectangle.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
}
return path;
}
private static GraphicsPath GenerateCapsule(
this Graphics graphics,
RectangleF baseRect)
{
float diameter;
RectangleF arc;
GraphicsPath path = new GraphicsPath();
try
{
if (baseRect.Width > baseRect.Height)
{
diameter = baseRect.Height;
SizeF sizeF = new SizeF(diameter, diameter);
arc = new RectangleF(baseRect.Location, sizeF);
path.AddArc(arc, 90, 180);
arc.X = baseRect.Right - diameter;
path.AddArc(arc, 270, 180);
}
else if (baseRect.Width < baseRect.Height)
{
diameter = baseRect.Width;
SizeF sizeF = new SizeF(diameter, diameter);
arc = new RectangleF(baseRect.Location, sizeF);
path.AddArc(arc, 180, 180);
arc.Y = baseRect.Bottom - diameter;
path.AddArc(arc, 0, 180);
}
else path.AddEllipse(baseRect);
}
catch { path.AddEllipse(baseRect); }
finally { path.CloseFigure(); }
return path;
}
/// <summary>
/// Draws a rounded rectangle specified by a pair of coordinates, a width, a height and the radius
/// for the arcs that make the rounded edges.
/// </summary>
/// <param name="brush">System.Drawing.Pen that determines the color, width and style of the rectangle.</param>
/// <param name="x">The x-coordinate of the upper-left corner of the rectangle to draw.</param>
/// <param name="y">The y-coordinate of the upper-left corner of the rectangle to draw.</param>
/// <param name="width">Width of the rectangle to draw.</param>
/// <param name="height">Height of the rectangle to draw.</param>
/// <param name="radius">The radius of the arc used for the rounded edges.</param>
public static void DrawRoundedRectangle(
this Graphics graphics,
Pen pen,
float x,
float y,
float width,
float height,
float radius)
{
RectangleF rectangle = new RectangleF(x, y, width, height);
GraphicsPath path = graphics.GenerateRoundedRectangle(rectangle, radius);
SmoothingMode old = graphics.SmoothingMode;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.DrawPath(pen, path);
graphics.SmoothingMode = old;
}
/// <summary>
/// Draws a rounded rectangle specified by a pair of coordinates, a width, a height and the radius
/// for the arcs that make the rounded edges.
/// </summary>
/// <param name="brush">System.Drawing.Pen that determines the color, width and style of the rectangle.</param>
/// <param name="x">The x-coordinate of the upper-left corner of the rectangle to draw.</param>
/// <param name="y">The y-coordinate of the upper-left corner of the rectangle to draw.</param>
/// <param name="width">Width of the rectangle to draw.</param>
/// <param name="height">Height of the rectangle to draw.</param>
/// <param name="radius">The radius of the arc used for the rounded edges.</param>
public static void DrawRoundedRectangle(
this Graphics graphics,
Pen pen,
int x,
int y,
int width,
int height,
int radius)
{
graphics.DrawRoundedRectangle(
pen,
Convert.ToSingle(x),
Convert.ToSingle(y),
Convert.ToSingle(width),
Convert.ToSingle(height),
Convert.ToSingle(radius));
}
/// <summary>
/// Fills the interior of a rounded rectangle specified by a pair of coordinates, a width, a height
/// and the radius for the arcs that make the rounded edges.
/// </summary>
/// <param name="brush">System.Drawing.Brush that determines the characteristics of the fill.</param>
/// <param name="x">The x-coordinate of the upper-left corner of the rectangle to fill.</param>
/// <param name="y">The y-coordinate of the upper-left corner of the rectangle to fill.</param>
/// <param name="width">Width of the rectangle to fill.</param>
/// <param name="height">Height of the rectangle to fill.</param>
/// <param name="radius">The radius of the arc used for the rounded edges.</param>
public static void FillRoundedRectangle(
this Graphics graphics,
Brush brush,
float x,
float y,
float width,
float height,
float radius)
{
RectangleF rectangle = new RectangleF(x, y, width, height);
GraphicsPath path = graphics.GenerateRoundedRectangle(rectangle, radius);
SmoothingMode old = graphics.SmoothingMode;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.FillPath(brush, path);
graphics.SmoothingMode = old;
}
/// <summary>
/// Fills the interior of a rounded rectangle specified by a pair of coordinates, a width, a height
/// and the radius for the arcs that make the rounded edges.
/// </summary>
/// <param name="brush">System.Drawing.Brush that determines the characteristics of the fill.</param>
/// <param name="x">The x-coordinate of the upper-left corner of the rectangle to fill.</param>
/// <param name="y">The y-coordinate of the upper-left corner of the rectangle to fill.</param>
/// <param name="width">Width of the rectangle to fill.</param>
/// <param name="height">Height of the rectangle to fill.</param>
/// <param name="radius">The radius of the arc used for the rounded edges.</param>
public static void FillRoundedRectangle(
this Graphics graphics,
Brush brush,
int x,
int y,
int width,
int height,
int radius)
{
graphics.FillRoundedRectangle(
brush,
Convert.ToSingle(x),
Convert.ToSingle(y),
Convert.ToSingle(width),
Convert.ToSingle(height),
Convert.ToSingle(radius));
}
}
}
Enjoy!

Visibility of Platonic solids

I'm doing what will draw platonic solids with visibility and constant shading. I have to do it without opengl or directX. I'm solving now the visibility. I could be probably solved by painters alghoritm but I don't know how to implement it in my code. There is how I draw it.
So I'm asking for advice how to implement the painters alghoritm in my code. I allready know something about this alghoritm - teoreticaly. And it will be probably enought just do the step with sorting of faces by the z - coordinate.
For projection I use class projection matrix and I have arrays vertexBuffer and indexBuffer like in OpenGL.
I do all in Visual studio 2010 in C#.
ProjectionMatrix
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace KPG3D
{
/// <summary>
/// Structure of 3D point
/// </summary>
public struct Point3D
{
public float X;
public float Y;
public float Z;
public Point3D(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
}
public class ProjectionMatrix
{
public float[,] projectionMatrix = new float[4, 4]; //projection matrix
/// <summary>
/// Konstructor of projection matrix
/// </summary>
public ProjectionMatrix()
{
setIdentity();
}
/// <summary>
/// Konstructor of projection matrix
/// </summary>
public ProjectionMatrix(float zNear, float zFar, float viewportWidth, float viewportHeight)
{
setIdentity();
float Q = zFar / (zFar - zNear);
float w = 2 * zNear / viewportWidth;
float h = 2 * zNear / viewportHeight;
projectionMatrix[0, 0] = w;
projectionMatrix[1, 1] = h;
projectionMatrix[2, 2] = Q;
projectionMatrix[3, 2] = 1;
projectionMatrix[2, 3] = -Q * zNear;
}
/// <summary>
/// Konstructor of projection matrix
/// </summary>
public ProjectionMatrix(float d, float z)
{
setIdentity();//set identity matrix
projectionMatrix[0, 0] = d / (d - z);
projectionMatrix[1, 1] = d / (d - z);
projectionMatrix[2, 2] = 0;
}
/// <summary>
/// Set the matrix to identity
/// </summary>
public void setIdentity()
{
projectionMatrix[0, 0] = 1;
projectionMatrix[0, 1] = 0;
projectionMatrix[0, 2] = 0;
projectionMatrix[0, 3] = 0;
projectionMatrix[1, 0] = 0;
projectionMatrix[1, 1] = 1;
projectionMatrix[1, 2] = 0;
projectionMatrix[1, 3] = 0;
projectionMatrix[2, 0] = 0;
projectionMatrix[2, 1] = 0;
projectionMatrix[2, 2] = 1;
projectionMatrix[2, 3] = 0;
projectionMatrix[3, 0] = 0;
projectionMatrix[3, 1] = 0;
projectionMatrix[3, 2] = 0;
projectionMatrix[3, 3] = 1;
}
/// <summary>
/// Aplicate projection on set point
/// </summary>
/// <param name="p">Point want to transformate</param>
/// <returns>Transformated point</returns>
public Point3D multiply(Point3D p)
{
Point3D result;
float tmp = projectionMatrix[3, 0] * p.X + projectionMatrix[3, 1] * p.Y + projectionMatrix[3, 2] * p.Z + projectionMatrix[3, 3] * 1;
result.X = (projectionMatrix[0, 0] * p.X + projectionMatrix[0, 1] * p.Y + projectionMatrix[0, 2] * p.Z + projectionMatrix[0, 3] * 1) / tmp;
result.Y = (projectionMatrix[1, 0] * p.X + projectionMatrix[1, 1] * p.Y + projectionMatrix[1, 2] * p.Z + projectionMatrix[1, 3] * 1) / tmp;
result.Z = (projectionMatrix[2, 0] * p.X + projectionMatrix[2, 1] * p.Y + projectionMatrix[2, 2] * p.Z + projectionMatrix[2, 3] * 1) / tmp;
return result;
}
}
}
Form
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
namespace KPG3D
{
public partial class Form1 : Form
{
private Bitmap canvasBitmap; //bitmap of drawing area
private Graphics g; //access to graffics
private List<Point3D> vertexBuffer = new List<Point3D>(); //list of all vertices
private List<int> indexBuffer = new List<int>();//list of all indices
private Point3D centroid = new Point3D(0, 0, 0);// center of object
private ProjectionMatrix projection = null; //projection matrix
private bool rotation = false;
private int objectID = 0;
public Form1()
{
InitializeComponent();
//create bitmap and set to canvas
canvasBitmap = new Bitmap(canvas.Width, canvas.Height);
canvas.Image = canvasBitmap;
//prepare grafics
g = Graphics.FromImage(canvasBitmap);
g.SmoothingMode = SmoothingMode.AntiAlias;
Matrix origin = new Matrix();
origin.Translate(canvas.Width / 2, canvas.Height / 2);
g.Transform = origin;
}
/// <summary>
/// Reaction on click on start button
/// </summary>
private void buttonStart_Click(object sender, EventArgs e)
{
if (projection == null)
{
projection = new ProjectionMatrix(1, 100, 1, 1);
//createBox();
//createTetrahedron();
createChosenObject();
}
Timer t = new Timer();
t.Tick += new EventHandler(timerDrawing);//set tick method
t.Interval = 30; //every 30 ms
t.Enabled = true;
t.Start();
}
/// <summary>
/// Create tetrahedron
/// </summary>
private void createTetrahedron()
{
Point3D a = new Point3D(-3,-3, 7);//h
Point3D b = new Point3D( 3,-3,13);//c
Point3D c = new Point3D( 3, 3, 7);//a
Point3D d = new Point3D(-3, 3,13);//f
vertexBuffer.Add(a);
vertexBuffer.Add(b);
vertexBuffer.Add(c);
vertexBuffer.Add(d);
//acb
indexBuffer.Add(0);
indexBuffer.Add(2);
indexBuffer.Add(1);
//adc
indexBuffer.Add(0);
indexBuffer.Add(3);
indexBuffer.Add(2);
//cdb
indexBuffer.Add(2);
indexBuffer.Add(3);
indexBuffer.Add(1);
//adb
indexBuffer.Add(0);
indexBuffer.Add(3);
indexBuffer.Add(1);
centroid = new Point3D(0, 0, 10);
}
/// <summary>
/// Create chosen object
/// </summary>
private void createChosenObject() {
switch (objectID) {
case 1:
createTetrahedron();
break;
case 2:
createHexahedron();
break;
case 3:
createOctahedron();
break;
case 4:
createDodecahedron();
break;
case 5:
createIcosahedron();
break;
default:
//do nothing
break;
}
}
/// <summary>
/// Rotate direcion vector by the specified angle
/// </summary>
/// <param name="vector">Direction vector</param>
/// <param name="angle">Angle of rotation</param>
/// <returns></returns>
private Point3D rotateVector(Point3D vector, Point3D centroid, double angle)
{
vector.X -= centroid.X;
vector.Y -= centroid.Y;
vector.Z -= centroid.Z;
Point3D result;
result.X = vector.X * (float)Math.Cos(angle) - vector.Z * (float)Math.Sin(angle);
result.Z = vector.X * (float)Math.Sin(angle) + vector.Z * (float)Math.Cos(angle);
result.Y = vector.Y;
result.X += centroid.X;
result.Y += centroid.Y;
result.Z += centroid.Z;
return result;
}
/// <summary>
/// Reaction on timer
/// </summary>
private void timerDrawing(Object obj, EventArgs ea)
{
//rotation of object
if (rotation == true)
{
for (int i = 0; i < vertexBuffer.Count; i++)
{
vertexBuffer[i] = rotateVector(vertexBuffer[i], centroid, 0.02);
}
}
//drawing of object
draw(vertexBuffer, indexBuffer);
//refresh what is on canvas
canvas.Invalidate();
}
/// <summary>
///Draw point and triangles
/// </summary>
/// <param name="vert"></param>
/// <param name="ind"></param>
private void draw(List<Point3D> vert, List<int> ind)
{
//clear drawing area
g.Clear(Color.Maroon);
//prepare pen and brush
Pen pen = new Pen(Color.Black, 1);
SolidBrush brush = new SolidBrush(Color.Black);
SolidBrush faceBrush = new SolidBrush(Color.FromArgb(75, Color.Green));
//draw edges
for (int i = 0; i < ind.Count / 3; i++)
{
Point3D A = projection.multiply(vert[ind[3 * i + 0]]);
Point3D B = projection.multiply(vert[ind[3 * i + 1]]);
Point3D C = projection.multiply(vert[ind[3 * i + 2]]);
//count to 2D
PointF a = new PointF(A.X * 200, -A.Y * 200);
PointF b = new PointF(B.X * 200, -B.Y * 200);
PointF c = new PointF(C.X * 200, -C.Y * 200);
g.FillPolygon(faceBrush, new PointF[] { a, b, c });
//draw triangle
g.DrawLine(pen, a, b);
g.DrawLine(pen, b, c);
g.DrawLine(pen, c, a);
}
//draw element
for (int i = 0; i < vert.Count; i++)
{
Point3D p = projection.multiply(vert[i]); //projection from 3D to 2D
g.FillRectangle(brush, p.X * 200 - 2, -p.Y * 200 - 2, 4, 4);
}
}
/// <summary>
/// Printscreen to file
/// </summary>
private void buttonPrintScreen_Click(object sender, EventArgs e)
{
canvas.Image.Save("out.png");
}
private void checkBoxRotate_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxRotate.Checked) rotation = true;
else rotation = false;
}
private void objectChooser_SelectedIndexChanged(object sender, EventArgs e)
{
//set ide of chosen object
if(objectChooser.SelectedItem.Equals("Tetrahedron")) objectID = 1;
else if (objectChooser.SelectedItem.Equals("Box")) objectID = 2;
else if (objectChooser.SelectedItem.Equals("Octahedron")) objectID = 3;
else if(objectChooser.SelectedItem.Equals("Dodecahedron")) objectID = 4;
else if (objectChooser.SelectedItem.Equals("Icosahedron")) objectID = 5;
}
}
}
If you have to account for intersecting triangles (see image), I think the easiest solution will involve a z-buffer (they are pretty easy to create and use). If you do not have to render intersecting triangles, your proposed solution of z sorting will be simpler and will work just fine.

Categories