I have used ProgressBar Control in my c# desktop application.I have used it in a thread other then the thread in which control has been declared.Its working Fine.
Now I am wondering how i can show some text inside progress bar control like "Initiating Registration" etc.Also I want to use it as Marquee progress bar.Please help me.
You will have to override the OnPaint method, call the base implementation and the paint your own text.
You will need to create your own CustomProgressBar and then override OnPaint to draw what ever text you want.
Custom Progress Bar Class
namespace ProgressBarSample
{
public enum ProgressBarDisplayText
{
Percentage,
CustomText
}
class CustomProgressBar: ProgressBar
{
//Property to set to decide whether to print a % or Text
public ProgressBarDisplayText DisplayStyle { get; set; }
//Property to hold the custom text
public String CustomText { get; set; }
public CustomProgressBar()
{
// Modify the ControlStyles flags
//http://msdn.microsoft.com/en-us/library/system.windows.forms.controlstyles.aspx
SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
}
protected override void OnPaint(PaintEventArgs e)
{
Rectangle rect = ClientRectangle;
Graphics g = e.Graphics;
ProgressBarRenderer.DrawHorizontalBar(g, rect);
rect.Inflate(-3, -3);
if (Value > 0)
{
// As we doing this ourselves we need to draw the chunks on the progress bar
Rectangle clip = new Rectangle(rect.X, rect.Y, (int)Math.Round(((float)Value / Maximum) * rect.Width), rect.Height);
ProgressBarRenderer.DrawHorizontalChunks(g, clip);
}
// Set the Display text (Either a % amount or our custom text
int percent = (int)(((double)this.Value / (double)this.Maximum) * 100);
string text = DisplayStyle == ProgressBarDisplayText.Percentage ? percent.ToString() + '%' : CustomText;
using (Font f = new Font(FontFamily.GenericSerif, 10))
{
SizeF len = g.MeasureString(text, f);
// Calculate the location of the text (the middle of progress bar)
// Point location = new Point(Convert.ToInt32((rect.Width / 2) - (len.Width / 2)), Convert.ToInt32((rect.Height / 2) - (len.Height / 2)));
Point location = new Point(Convert.ToInt32((Width / 2) - len.Width / 2), Convert.ToInt32((Height / 2) - len.Height / 2));
// The commented-out code will centre the text into the highlighted area only. This will centre the text regardless of the highlighted area.
// Draw the custom text
g.DrawString(text, f, Brushes.Red, location);
}
}
}
}
Sample WinForms Application
using System;
using System.Linq;
using System.Windows.Forms;
using System.Collections.Generic;
namespace ProgressBarSample
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Set our custom Style (% or text)
customProgressBar1.DisplayStyle = ProgressBarDisplayText.CustomText;
customProgressBar1.CustomText = "Initialising";
}
private void btnReset_Click(object sender, EventArgs e)
{
customProgressBar1.Value = 0;
btnStart.Enabled = true;
}
private void btnStart_Click(object sender, EventArgs e)
{
btnReset.Enabled = false;
btnStart.Enabled = false;
for (int i = 0; i < 101; i++)
{
customProgressBar1.Value = i;
// Demo purposes only
System.Threading.Thread.Sleep(100);
// Set the custom text at different intervals for demo purposes
if (i > 30 && i < 50)
{
customProgressBar1.CustomText = "Registering Account";
}
if (i > 80)
{
customProgressBar1.CustomText = "Processing almost complete!";
}
if (i >= 99)
{
customProgressBar1.CustomText = "Complete";
}
}
btnReset.Enabled = true;
}
}
}
I have written a no blinking/flickering TextProgressBar
You can find the source code here: https://github.com/ukushu/TextProgressBar
WARNING: It's a little bit buggy! But still, I think it's better than another answers here. As I have no time for fixes, if you will do sth with them, please send me update by some way:) Thanks.
Samples:
AVOID FLICKERING TEXT
The solution provided by Barry above is excellent, but there's is the "flicker-problem".
As soon as the Value is above zero the OnPaint will be envoked repeatedly and the text will flicker.
There is a solution to this. We do not need VisualStyles for the object since we will be drawing it with our own code.
Add the following code to the custom object Barry wrote and you will avoid the flicker:
[DllImportAttribute("uxtheme.dll")]
private static extern int SetWindowTheme(IntPtr hWnd, string appname, string idlist);
protected override void OnHandleCreated(EventArgs e)
{
SetWindowTheme(this.Handle, "", "");
base.OnHandleCreated(e);
}
I did not write this myself. It found it here: https://stackoverflow.com/a/299983/1163954
I've testet it and it works.
I wold create a control named for example InfoProgresBar, that provide this functionality with a label or two (Main Job, Current Job) and ProgressBar and use it instead of that ProgressBar.
I have used this simple code, and it works!
for (int i = 0; i < N * N; i++)
{
Thread.Sleep(50);
progressBar1.BeginInvoke(new Action(() => progressBar1.Value = i));
progressBar1.CreateGraphics().DrawString(i.ToString() + "%", new Font("Arial",
(float)10.25, FontStyle.Bold),
Brushes.Red, new PointF(progressBar1.Width / 2 - 10, progressBar1.Height / 2 - 7));
}
It just has one simple problem and this is it: when progress bar start to rising, percentage some times hide, and then appear again.
I did't write it myself.I found it here:
text on progressbar in c#
I used this code, and it does work.
I tried placing a label with transparent background over a progress bar but never got it to work properly. So I found Barry's solution here very useful, although I missed the beautiful Vista style progress bar. So I merged Barry's solution with http://www.dreamincode.net/forums/topic/243621-percent-into-progress-bar/ and managed to keep the native progress bar, while displaying text percentage or custom text over it. I don't see any flickering in this solution either. Sorry to dig up and old thread but I needed this today and so others may need it too.
public enum ProgressBarDisplayText
{
Percentage,
CustomText
}
class ProgressBarWithCaption : ProgressBar
{
//Property to set to decide whether to print a % or Text
private ProgressBarDisplayText m_DisplayStyle;
public ProgressBarDisplayText DisplayStyle {
get { return m_DisplayStyle; }
set { m_DisplayStyle = value; }
}
//Property to hold the custom text
private string m_CustomText;
public string CustomText {
get { return m_CustomText; }
set {
m_CustomText = value;
this.Invalidate();
}
}
private const int WM_PAINT = 0x000F;
protected override void WndProc(ref Message m)
{
base.WndProc(m);
switch (m.Msg) {
case WM_PAINT:
int m_Percent = Convert.ToInt32((Convert.ToDouble(Value) / Convert.ToDouble(Maximum)) * 100);
dynamic flags = TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine | TextFormatFlags.WordEllipsis;
using (Graphics g = Graphics.FromHwnd(Handle)) {
using (Brush textBrush = new SolidBrush(ForeColor)) {
switch (DisplayStyle) {
case ProgressBarDisplayText.CustomText:
TextRenderer.DrawText(g, CustomText, new Font("Arial", Convert.ToSingle(8.25), FontStyle.Regular), new Rectangle(0, 0, this.Width, this.Height), Color.Black, flags);
break;
case ProgressBarDisplayText.Percentage:
TextRenderer.DrawText(g, string.Format("{0}%", m_Percent), new Font("Arial", Convert.ToSingle(8.25), FontStyle.Regular), new Rectangle(0, 0, this.Width, this.Height), Color.Black, flags);
break;
}
}
}
break;
}
}
}
Just want to point out something on #codingbadger answer. When using "ProgressBarRenderer" you should always check for "ProgressBarRenderer.IsSupported" before using the class. For me, this has been a nightmare with Visual Styles errors in Win7 that I couldn't fix. So, a better approach and workaround for the solution would be:
Rectangle clip = new Rectangle(rect.X, rect.Y, (int)Math.Round(((float)Value / Maximum) * rect.Width), rect.Height);
if (ProgressBarRenderer.IsSupported)
ProgressBarRenderer.DrawHorizontalChunks(g, clip);
else
g.FillRectangle(new SolidBrush(this.ForeColor), clip);
Notice that the fill will be a simple rectangle and not chunks. Chunks will be used only if ProgressBarRenderer is supported
I have created a InfoProgressBar control which uses a TransparentLabel control. Testing on a form with a Timer, I get some slight glitches displaying the text every 30-40 value changes if using a timer interval of less than 250 milliseconds (probably because of the time required to update the screen is greater than the timer interval).
It would be possible to modify UpdateText method to insert all the calculated values into CustomText but it isn't something that I have needed yet. This would remove the need for the DisplayType property and enumerate.
The TransparentLabel class was created by adding a new UserControl and changing it to inheriting from Label with the following implementation:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace Utils.GUI
{
public partial class TransparentLabel : Label
{
// hide the BackColor attribute as much as possible.
// setting the base value has no effect as drawing the
// background is disabled
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override Color BackColor
{
get
{
return Color.Transparent;
}
set
{
}
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20; // WS_EX_TRANSPARENT
return cp;
}
}
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
if(Parent != null) Parent.Invalidate(Bounds, false);
}
}
public override ContentAlignment TextAlign
{
get
{
return base.TextAlign;
}
set
{
base.TextAlign = value;
if(Parent != null) Parent.Invalidate(Bounds, false);
}
}
public TransparentLabel()
{
InitializeComponent();
SetStyle(ControlStyles.Opaque, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
base.BackColor = Color.Transparent;
}
protected override void OnMove(EventArgs e)
{
base.OnMove(e);
RecreateHandle();
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
// do nothing
}
}
}
I did not make any changes to the related designer code but here it is for completeness.
namespace Utils.GUI
{
partial class TransparentLabel
{
/// <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()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}
I then created another new UserControl and changed it to derive from ProgressBar with the following implementation:
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Windows.Forms.Design.Behavior;
namespace Utils.GUI
{
[Designer(typeof(InfoProgressBarDesigner))]
public partial class InfoProgressBar : ProgressBar
{
// designer class to add font baseline snapline by copying it from the label
private class InfoProgressBarDesigner : ControlDesigner
{
public override IList SnapLines
{
get
{
IList snapLines = base.SnapLines;
InfoProgressBar control = Control as InfoProgressBar;
if(control != null)
{
using(IDesigner designer = TypeDescriptor.CreateDesigner(control.lblText, typeof(IDesigner)))
{
if(designer != null)
{
designer.Initialize(control.lblText);
ControlDesigner boxDesigner = designer as ControlDesigner;
if(boxDesigner != null)
{
foreach(SnapLine line in boxDesigner.SnapLines)
{
if(line.SnapLineType == SnapLineType.Baseline)
{
snapLines.Add(new SnapLine(SnapLineType.Baseline, line.Offset, line.Filter, line.Priority));
break;
}
}
}
}
}
}
return snapLines;
}
}
}
// enum to select the type of displayed value
public enum ProgressBarDisplayType
{
Custom = 0,
Percent = 1,
Progress = 2,
Remain = 3,
Value = 4,
}
private string _customText;
private ProgressBarDisplayType _displayType;
private int _range;
[Bindable(false)]
[Browsable(true)]
[DefaultValue("{0}")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
// {0} is replaced with the result of the selected calculation
public string CustomText
{
get
{
return _customText;
}
set
{
_customText = value;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(ProgressBarDisplayType.Percent)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public ProgressBarDisplayType DisplayType
{
get
{
return _displayType;
}
set
{
_displayType = value;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
// don't use the lblText font as if it is null, it checks the parent font (i.e. this property) and gives an infinite loop
public override Font Font
{
get
{
return base.Font;
}
set
{
base.Font = value;
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(100)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new int Maximum
{
get
{
return base.Maximum;
}
set
{
base.Maximum = value;
_range = base.Maximum - base.Minimum;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(0)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new int Minimum
{
get
{
return base.Minimum;
}
set
{
base.Minimum = value;
_range = base.Maximum - base.Minimum;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(ContentAlignment.MiddleLeft)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public ContentAlignment TextAlign
{
get
{
return lblText.TextAlign;
}
set
{
lblText.TextAlign = value;
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(typeof(Color), "0x000000")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public Color TextColor
{
get
{
return lblText.ForeColor;
}
set
{
lblText.ForeColor = value;
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(0)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new int Value
{
get
{
return base.Value;
}
set
{
base.Value = value;
UpdateText();
}
}
public InfoProgressBar()
{
InitializeComponent();
CustomText = "{0}";
DisplayType = ProgressBarDisplayType.Percent;
Maximum = 100;
Minimum = 0;
TextAlign = ContentAlignment.MiddleLeft;
TextColor = Color.Black;
Value = 0;
// means the label gets drawn in front of the progress bar
lblText.Parent = this;
_range = base.Maximum - base.Minimum;
}
protected void UpdateText()
{
switch(DisplayType)
{
case ProgressBarDisplayType.Custom:
{
lblText.Text = _customText;
break;
}
case ProgressBarDisplayType.Percent:
{
if(_range > 0)
{
lblText.Text = string.Format(_customText, string.Format("{0}%", (int)((Value * 100) / _range)));
}
else
{
lblText.Text = "100%";
}
break;
}
case ProgressBarDisplayType.Progress:
{
lblText.Text = string.Format(_customText, (Value - Minimum));
break;
}
case ProgressBarDisplayType.Remain:
{
lblText.Text = string.Format(_customText, (Maximum - Value));
break;
}
case ProgressBarDisplayType.Value:
{
lblText.Text = string.Format(_customText, Value);
break;
}
}
}
public new void Increment(int value)
{
base.Increment(value);
UpdateText();
}
public new void PerformStep()
{
base.PerformStep();
UpdateText();
}
}
}
And the designer code:
namespace Utils.GUI
{
partial class InfoProgressBar
{
/// <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.lblText = new Utils.GUI.TransparentLabel();
this.SuspendLayout();
//
// lblText
//
this.lblText.BackColor = System.Drawing.Color.Transparent;
this.lblText.Dock = System.Windows.Forms.DockStyle.Fill;
this.lblText.Location = new System.Drawing.Point(0, 0);
this.lblText.Name = "lblText";
this.lblText.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.lblText.Size = new System.Drawing.Size(100, 23);
this.lblText.TabIndex = 0;
this.lblText.Text = "transparentLabel1";
this.ResumeLayout(false);
}
#endregion
private TransparentLabel lblText;
}
}
Alliteratively you can try placing a Label control and placing it on top of the progress bar control. Then you can set whatever the text you want to the label. I haven't done this my self. If it works it should be a simpler solution than overriding onpaint.
Related
I'm creating a custom PictureBox.
As you can see, it's a PictureBox designed for profile photos
Well, this is the class of the CircularPictureBox
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Hector.Framework.Controls
{
public class CircularPictureBox : PictureBox
{
private Color _idlecolor = Color.White;
public CircularPictureBox()
{
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
this.DoubleBuffered = true;
this.BackColor = Color.White;
this.SizeMode = PictureBoxSizeMode.StretchImage;
this.Size = new Size(100, 100);
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
using (var gpath = new GraphicsPath())
{
var brush = new SolidBrush(this.IdleBorderColor);
var pen = new Pen(brush, 5);
var outrect = new Rectangle(-1, -1, this.Width + 5, this.Height + 5);
gpath.AddEllipse(outrect);
pe.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
pe.Graphics.DrawPath(pen, gpath);
brush.Dispose();
pen.Dispose();
gpath.Dispose();
}
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
using (var gpath = new GraphicsPath())
{
var rect = new Rectangle(1, 1, this.Width - 1, this.Height - 1);
gpath.AddEllipse(rect);
this.Region = new Region(gpath);
gpath.Dispose();
}
}
public Color IdleBorderColor
{
get => this._idlecolor;
set => this._idlecolor = value;
}
}
}
My problem is that since it is a control that can be used from the designer, I want it to have properties such as edge width or border color.
I started testing with the color, but it is that whenever I change the color,
Visual Studio shows me an error message saying that The value of the property is not valid
I made a few modifications to your code, to highlight some features that can be useful in the design of Custom Control.
The modifications I've made I think are self-explanatory.
However, take a look at the OnPaint event. The e.Graphics.Clip Region lets you hide all graphics parts that are not in the selected region. This implies that when you drag the control in Design Mode, the Image will be clipped and won't be seen outside the region area.
The PixelOffsetMode.HighQuality and SmoothingMode.AntiAlias contributes to the overall quality of the rendering (there are commented out options that can useful in other situations).
The calculation of the Border offset must reference the BorderSize width, and scaled accordingly. The Pen object draws starting from the middle of its size. If a Pen has a size of 3 pixels, 1 Pixel is drawn on the border, one outside the area and one inside (weird? Maybe).
The transparency settings is just a "fake" here.
It might be used effectively in other situations (it should read "Platforms").
public class CircularPictureBox : PictureBox
{
private Bitmap bitmap;
private Color borderColor;
private int penSize;
private Color alphaColor = Color.FromArgb(0, 255,255,255);
private bool enhancedBuffering;
public CircularPictureBox()
{
InitializeComponent();
this.SetStyle(ControlStyles.SupportsTransparentBackColor |
ControlStyles.ResizeRedraw |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer, true);
}
private void InitializeComponent()
{
this.enhancedBuffering = true;
this.bitmap = null;
this.borderColor = Color.Silver;
this.penSize = 7;
this.BackColor = alphaColor;
this.SizeMode = PictureBoxSizeMode.StretchImage;
this.Size = new Size(100, 100);
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.CompositingMode = CompositingMode.SourceOver;
//e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
//e.Graphics.InterpolationMode = InterpolationMode.Bicubic;
e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
if (this.Region != null) e.Graphics.Clip = this.Region;
var rect = this.ClientRectangle;
if (bitmap != null) {
e.Graphics.DrawImage(bitmap, rect);
}
rect.Inflate(-penSize / 2 + 1, -penSize / 2 + 1);
using (var pen = new Pen(borderColor, penSize)) {
e.Graphics.DrawEllipse(pen, rect);
}
}
protected override void OnResize(EventArgs e)
{
using (var path = new GraphicsPath()) {
path.AddEllipse(this.ClientRectangle);
path.CloseFigure();
using (Region region = new Region(path)) {
this.Region = region.Clone();
}
}
}
[Description("Gets or Sets the Image displayed by the control"), Category("Appearance")]
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true)]
public Bitmap Bitmap
{
get { return bitmap; }
set { bitmap = value; Invalidate(); }
}
[Description("Gets or Sets the size of the Border"), Category("Behavior")]
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true)]
public int BorderSize
{
get { return penSize; }
set { penSize = value; Invalidate(); }
}
[Description("Gets or Sets the Color of Border drawn around the Image.")]
[Category("Appearance")]
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true)]
public Color BorderColor
{
get { return borderColor; }
set { borderColor = value; Invalidate(); }
}
[Description("Enables or disables the control OptimizedDoubleBuffering feature")]
[Category("Useful Features")] //<= "Useful feature" is a custom category
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true)]
public bool EnhancedBuffering
{
get { return enhancedBuffering; }
set { enhancedBuffering = value;
SetStyle(ControlStyles.OptimizedDoubleBuffer, value);
UpdateStyles();
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
public new Image ErrorImage
{
get { return null; }
set { base.ErrorImage = null; }
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
public new Image InitialImage
{
get { return null; }
set { base.InitialImage = null; }
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
public new Image BackgroundImage
{
get { return null; }
set { base.BackgroundImage = null; }
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Never), BrowsableAttribute(false)]
public new Image Image {
get { return null; }
set { base.Image = null; }
}
}
Some System.ComponentModel Attributes that can help shaping the Control.
For example, Description and Category attributes:
(These have been inserted in the custom Property BorderColor of your control).
[Description("Gets or Sets the Color of the Border drawn around the Image.")
[Category("Appearance")]
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true)]
Description of course explains the user what the Property is for.
Category is used to give the Properties an organic disposition inside the PropertyGrid. You can use standard names (Appearance, Behavior etc.) or specify anything else.
Give the Category a custom name and it will be listed among the others, when the Categorized view is in use.
The Image property of the Custom Control has been hidden and substituted with a Bitmap Property:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Never), BrowsableAttribute(false)]
The EditorBrowsable Attribute is a hint to Intellisense that lets you determine whether to show a property or method in the Popup menu. It can be Never, Always or Advanced (for those who know how to reach VS Options). Properties and Methods will be Hidden when the Custom Control is deployed (as a dll), not while you are designing it.
The BrowsableAttribute Attribute (or just [Browsable]) allows to specify whether that Property should be shown in the PropertyGrid.
The DesignerSerializationVisibility
With the DesignerSerializationVisibility Attribute, you can indicate
whether the value for a property is Visible, and should be persisted
in initialization code, Hidden, and should not be persisted in
initialization code, or consists of Content, which should have
initialization code generated for each public, not hidden property of
the object assigned to the property.
Also interesting:
TypeConverter(typeof(System.ComponentModel.ExpandableObjectConverter))
With this Attribute, you can instruct to list the Public Properties of a Class Object in the PropertyGrid.
This Class Object can be an internal Class that serializes a complex Property of a Control.
The TypeConverter Class is very interesting itself.
I've created a project with multiple user controls to support transparency, gradient and themes for winforms.
I was looking for a way to create a replacement for textbox, since winforms textboxes are not using the regular OnPaint and OnPaintBackground that other winforms control use, and sure enough, I've found something I can work with right here on stackoverflow. Brian's comment gave me the solution - Wrapping a transparent RichTextBox inside my own control.
However, this posed a new problem that I can't figure out how to solve - The TabIndex property dosn't operate as expected.
With normal textboxes, when you have multiple textboxes and each one have a different tab index, the focus goes from one textbox to the other in the order specified by the tab index. In my case, it doesn't. Instead, it's unpredictable.
I've tried multiple forms with different layouts and controls on them, but I can't seem to find any predictable pattern of behavior that would suggest the problem.
Here is the relevant control's code (the parent, ZControl inherits UserControl if that matters):
/// <summary>
/// A stylable textbox.
/// <Remarks>
/// The solution for writing a stylable textbox was inspired by this SO post and Brian's comment:
/// https://stackoverflow.com/a/4360341/3094533
/// </Remarks>
/// </summary>
[DefaultEvent("TextChanged")]
public partial class ZTextBox : ZControl
{
#region ctor
public ZTextBox()
{
TextBox = new TransparentRichTextBox();
TextBox.BackColor = Color.Transparent;
TextBox.BorderStyle = BorderStyle.None;
TextBox.Multiline = false;
TextBox.TextChanged += TextBox_TextChanged;
TextBox.TabStop = true;
TextBox.AcceptsTab = false;
InitializeComponent();
AdjustTextBoxRectangle();
this.Controls.Add(TextBox);
this.RoundedCorners.PropertyChanged += RoundedCorners_PropertyChanged;
}
#endregion ctor
#region properties
private TransparentRichTextBox TextBox { get; }
public override string Text
{
get
{
return TextBox.Text;
}
set
{
TextBox.Text = value;
}
}
[DefaultValue(false)]
public bool Multiline
{
get
{
return this.TextBox.Multiline;
}
set
{
this.TextBox.Multiline = value;
}
}
public override Font Font
{
get
{
return base.Font;
}
set
{
if (base.Font != value)
{
base.Font = value;
if (TextBox != null)
{
TextBox.Font = value;
}
}
}
}
public new int TabIndex
{
get
{
return this.TextBox.TabIndex;
}
set
{
this.TextBox.TabIndex = value;
}
}
#region hidden properties
[
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never)
]
public override Color ForeColor
{
get
{
return TextBox.ForeColor;
}
set
{
TextBox.ForeColor = value;
}
}
[
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never)
]
public override ContentAlignment TextAlign
{
get
{
return base.TextAlign;
}
set
{
base.TextAlign = value;
}
}
[
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never)
]
public override Point TextLocationOffset
{
get
{
return base.TextLocationOffset;
}
set
{
base.TextLocationOffset = value;
}
}
#endregion hidden properties
#endregion properties
#region methods
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
TextBox.Focus();
}
protected override void DrawText(Graphics graphics, string text, ContentAlignment textAlign, Point locationOffset, Size stringSize)
{
// Do nothing - The transparent rich textbox is responsible for drawing the text...
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
AdjustTextBoxRectangle();
}
private void AdjustTextBoxRectangle()
{
var corners = this.RoundedCorners.Corners;
var leftAdjustment = ((corners & RoundedEdges.TopLeft) == RoundedEdges.TopLeft || (corners & RoundedEdges.BottomLeft) == RoundedEdges.BottomLeft) ? this.RoundedCorners.ArcSize / 2 : 0;
var rightAdjustment = ((corners & RoundedEdges.TopRight) == RoundedEdges.TopRight || (corners & RoundedEdges.BottomRight) == RoundedEdges.BottomRight) ? this.RoundedCorners.ArcSize / 2 : 0;
TextBox.Top = 0;
TextBox.Left = leftAdjustment;
TextBox.Width = this.Width - leftAdjustment - rightAdjustment;
TextBox.Height = this.Height;
}
#endregion methods
#region event handlers
private void RoundedCorners_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
AdjustTextBoxRectangle();
}
private void TextBox_TextChanged(object sender, EventArgs e)
{
OnTextChanged(e);
}
#endregion event handlers
#region private classes
private class TransparentRichTextBox : RichTextBox
{
public TransparentRichTextBox()
{
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.SetStyle(ControlStyles.Opaque, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
}
protected override CreateParams CreateParams
{
get
{
CreateParams parms = base.CreateParams;
parms.ExStyle |= 0x20; // Turn on WS_EX_TRANSPARENT
return parms;
}
}
}
#endregion private classes
}
And the designer code, if that's relevant:
partial class ZTextBox
{
/// <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();
//
// ZTextBox
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.Name = "ZTextBox";
this.RoundedCorners.ArcSize = 50;
this.RoundedCorners.Corners = Zohar.UserControls.RoundedEdges.None;
this.Size = new System.Drawing.Size(100, 20);
this.Style.DisabledStyle.BackColor = System.Drawing.Color.Empty;
this.Style.DisabledStyle.BackgroundImage = null;
this.Style.DisabledStyle.BorderColor = System.Drawing.Color.Empty;
this.Style.DisabledStyle.ForeColor = System.Drawing.Color.Empty;
this.Style.DisabledStyle.Gradient.Angle = 0F;
this.Style.DisabledStyle.Gradient.BackColor = System.Drawing.Color.Empty;
this.Style.DisabledStyle.Image = null;
this.Style.DisabledStyle.Name = null;
this.Style.EnabledStyle.BackColor = System.Drawing.Color.Empty;
this.Style.EnabledStyle.BackgroundImage = null;
this.Style.EnabledStyle.BorderColor = System.Drawing.Color.Empty;
this.Style.EnabledStyle.ForeColor = System.Drawing.Color.Empty;
this.Style.EnabledStyle.Gradient.Angle = 0F;
this.Style.EnabledStyle.Gradient.BackColor = System.Drawing.Color.Empty;
this.Style.EnabledStyle.Image = null;
this.Style.EnabledStyle.Name = null;
this.Style.HoverStyle.BackColor = System.Drawing.Color.Empty;
this.Style.HoverStyle.BackgroundImage = null;
this.Style.HoverStyle.BorderColor = System.Drawing.Color.Empty;
this.Style.HoverStyle.ForeColor = System.Drawing.Color.Empty;
this.Style.HoverStyle.Gradient.Angle = 0F;
this.Style.HoverStyle.Gradient.BackColor = System.Drawing.Color.Empty;
this.Style.HoverStyle.Image = null;
this.Style.HoverStyle.Name = null;
this.ResumeLayout(false);
}
#endregion
}
The issue is caused by the following code:
public new int TabIndex
{
get
{
return this.TextBox.TabIndex;
}
set
{
this.TextBox.TabIndex = value;
}
}
You should never do this for UserControl (actually for any control). The documentation for Control.TabIndex property states:
Gets or sets the tab order of the control within its container.
In other words, the control TabIndex property is not global for the form, but scoped to the control container (parent).
The effect is that the form designer code where your control resides will call the shadow TabOrder setter, but the tab navigation handling will simply call the base Control property, leading to undetermined behavior.
Also note that setting the TabIndex of the inner TextBox makes no any sense since it's the only control inside the container (your control). While what you really need is to set the TabIndex of your control inside its container.
With that being said, simply remove the above code and everything will work as expected.
that's probably because you have not added your textbox's in order or maybe you deleted some of them while adding them, anyway you can choose the order on the properties of the controls => TabIndex
I am creating a custom control in my C# application in order to add a new property (MyProperty below). It is inheriting from Label. One thing I would like it to do, is display at a particular size when I drag it on to my form (200x132). I'd also like it to display no text. However, no matter how I try to do this, it doesn't seem to work. I am able to set BackColor and BorderStyle with no problem, however. I'm fairly new to C#, so maybe I'm missing something obvious.
Here is my code:
using System.Drawing;
using System.Windows.Forms;
namespace MyProgram
{
public enum MyEnum
{
Value1, Value2, Value3
}
public partial class MyControl : Label
{
public MyControl()
{
BackColor = Color.LightCoral;
BorderStyle = BorderStyle.FixedSingle;
AutoSize = false;
Size = new Size(200, 132);
Text = "";
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
private MyEnum myProperty;
public MyEnum MyProperty
{
get { return myProperty; }
set { myPropery = value; }
}
}
}
The answer provided via Dispersia's link has a bug, in my opinion. The text reset should happen once and then whatever a user does after that shouldn't matter. In Dispersia's link you can't actually set the text back to the control name because it will keep blanking it out.
The answer provided by cramopy doesn't technically answer your question, it is a way to do it by using the defaults on a UserControl though. You'll also need to bind the Text property of the UserControl to the label's.
The following should work while inheriting from a Label and will only reset the Text property once.
public partial class MyControl : Label
{
#region fields
private IComponentChangeService _changeService;
private bool canResetText = false;
#endregion
#region properties
protected override Size DefaultSize
{
get { return new Size(200, 132); }
}
[Browsable(false)]
public override bool AutoSize
{
get { return false; }
set { base.AutoSize = false; }
}
public override ISite Site
{
get { return base.Site; }
set
{
base.Site = value;
if (!base.DesignMode)
return;
this._changeService = (IComponentChangeService)base.GetService(typeof(IComponentChangeService));
if (this._changeService != null)
this._changeService.ComponentChanged += new ComponentChangedEventHandler(this.OnComponentChanged);
}
}
#endregion
#region constructors
public MyControl()
{
base.BackColor = Color.LightCoral;
base.BorderStyle = BorderStyle.FixedSingle;
}
#endregion
#region methods
protected override void InitLayout()
{
base.InitLayout();
this.canResetText = true;
}
private void OnComponentChanged(object sender, ComponentChangedEventArgs ce)
{
if (ce.Component != null &&
ce.Component == this &&
ce.Member.Name == "Text" &&
base.DesignMode &&
this.canResetText)
{
((MyControl)ce.Component).Text = string.Empty;
this.canResetText = false;
if (this._changeService != null)
this._changeService.ComponentChanged -= new ComponentChangedEventHandler(this.OnComponentChanged);
}
}
#endregion
}
#Dispersia reply only answers the myControl1 thing. (deleted meanwhile)
Here comes a full guide for solving your problem:
Add a new UserControl named MyLabel
Change the following within Designer Mode:
BorderStyle:= FixedSingle
Size:= 200; 132
Now Drag&Drop a new Label onto the control
Edit those Label values (also within Designer Mode):
AutoSize:= false
BackColor:= LightCoral
Dock:= Fill
Text:= clear/empty this box!! (don't write this inside the box, you really have to clear it!)
TextAlign:= MiddleCenter
Just recompile your project && add a MyLabel control from the Toolbar.
Now it show up as you wanted!!
I'm going to post my code first since it is short and easy to understand, then i'll ask my question.
public class BatteryLabel : Control
{
private Color _captionColor = SystemColors.Control;
private Color _textColor = SystemColors.Info;
private Color _failColor = Color.Red;
private Color _passColor = Color.Green;
private string _caption;
string text2;
string text3;
bool battery1Fail = false;
bool battery2Fail = false;
bool battery3Fail = false;
public BatteryLabel()
{
}
public Color BackgroundTextColor
{
get{ return _textColor;}
set{_textColor = value; Invalidate();}
}
public string Caption
{
get
{
return _caption;
}
set
{
_caption = value;
Invalidate();
}
}
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
Invalidate();
}
}
public string Text2
{
get { return text2; }
set { text2 = value; Invalidate(); }
}
public string Text3
{
get { return text3; }
set { text3 = value; Invalidate(); }
}
public bool Battery1Fail
{
get { return battery1Fail; }
set { battery1Fail = value; Invalidate(); }
}
public bool Battery2Fail
{
get { return battery2Fail; }
set { battery2Fail = value; Invalidate(); }
}
public bool Battery3Fail
{
get { return battery3Fail; }
set { battery3Fail = value; Invalidate(); }
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawRectangle(Pens.Black, 0,0, Width-1, Height-1);
var x1 = 50;
var x2 = 98;
var x3 = 146;
var color1 = battery1Fail?_failColor:BackgroundTextColor;
var color2 = battery2Fail?_failColor:BackgroundTextColor;
var color3 = battery3Fail?_failColor:BackgroundTextColor;
e.Graphics.FillRectangle(new SolidBrush(color1),x1+1, 1, 47, Height-2);
e.Graphics.FillRectangle(new SolidBrush(color2),x2+1, 1, 47, Height-2);
e.Graphics.FillRectangle(new SolidBrush(color3),x3+1, 1, 47, Height-2);
e.Graphics.DrawLine(Pens.Black, x1,0, x1, Height-1);
e.Graphics.DrawLine(Pens.Black, x2,0, x2, Height-1);
e.Graphics.DrawLine(Pens.Black, x3,0, x3, Height-1);
var BoldFont = new Font(this.Font, FontStyle.Bold);
e.Graphics.DrawString(Caption, BoldFont, new SolidBrush(ForeColor), 0,0);
e.Graphics.DrawString(Text, this.Font, new SolidBrush(ForeColor), x1,0);
e.Graphics.DrawString(Text2, this.Font, new SolidBrush(ForeColor), x2,0);
e.Graphics.DrawString(Text3, this.Font, new SolidBrush(ForeColor), x3,0);
}
}
The controls size is meant to be 195,14 just in case you decide to try to use it. I have 8 of these in a panel that is 200,200 running on a 1.6Ghz atom processor. It is used to display values from up to 3 batteries on a computer. The labels get refreshed every 500ms. As you may have gathered there is a little bit of flickering, but it is tolerable. I'd just like to have even less if possible. So I started looking into using Update, and moving some of my code around such as the background bit I thought maybe i should move that to OnPaintBackground(), but in a test frame that i made up the Update method does not change anything, and when I use Invalidate method it runs both OnPaintBackground and OnPaint. Here is what I tried in that case.
public class InformationLabel : Control
{
Random r = new Random();
protected override void OnPaintBackground(PaintEventArgs e)
{
base.OnPaintBackground(e);
Color randomCOlor = Color.FromArgb(r.Next());
e.Graphics.FillRectangle(new SolidBrush(randomCOlor),0,0, Width-1, Height-1);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Color randomCOlor = Color.FromArgb(r.Next());
e.Graphics.FillPie(new SolidBrush(randomCOlor),15,15,15,15, 0.0f, 120.0f);
}
}
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
void Button1Click(object sender, EventArgs e)
{
informationLabel1.Update();
}
void Button2Click(object sender, EventArgs e)
{
informationLabel1.Invalidate();
}
}
I made that one usercontrol about 300,300 so i could be sure of what i was seeing. I forgot to mention that in the battery control in my 500ms timer i just renew text, text2, and text3. I'm thinking that if the value of that text is out of spec that I'll set the battery fail flag and then invalidate.. but i'm not sure. So how should I go about updating only the text???
You can get rid of the flickering by adding this line in your constructor:
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.Opaque | ControlStyles.AllPaintingInWmPaint, true);
Now paint both background and everything else in your paint handler.
Optimizing can be done by passing a Rectangle to Invalidate for only the area that needs repainting. Then in your OnPaint override you use e.ClipRectangle to figure out what to draw. This is probably not necessary for such a simple drawing.
I believe you are looking in the wrong place to eliminate flicker. I can use your BatteryLabel to get flickerless updating of the text with basically a single line. Change your constructor to look like this:
public BatteryLabel()
{
this.SetStyle(ControlStyles.OptimizedDoubleBuffer,true);
}
This tells the control to double-buffer its graphics, which makes the flicker go away.
To test with a 100ms refresh interval:
Timer t;
public Form1()
{
InitializeComponent();
t = new Timer();
t.Interval = 100;
t.Tick += new EventHandler(t_Tick);
t.Start();
}
void t_Tick(object sender, EventArgs e)
{
string ticks = DateTime.Now.Ticks.ToString();
string ticks1 = ticks.Substring(ticks.Length-4),
ticks2 = ticks.Substring(ticks.Length - 5,4),
ticks3 = ticks.Substring(ticks.Length - 6,4);
batteryLabel1.Text = ticks1;
batteryLabel1.Text2 = ticks2;
batteryLabel1.Text3 = ticks3;
batteryLabel1.Battery1Fail = ticks1.StartsWith("1");
batteryLabel1.Battery2Fail = ticks2.StartsWith("1");
batteryLabel1.Battery3Fail = ticks3.StartsWith("1");
}
Does this help, or have I misunderstood you?
How do I make a button in a round shape rather than the conventional rectangle.
I am using winforms(2.0)
First make a class. Give it name: "RoundButton".
Then write the code directly as this:
using System;
using System.Collections.Generic;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Linq;
using System.Text;
namespace WindowsFormsApplication1
{
public class RoundButton : Button
{
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
GraphicsPath grPath = new GraphicsPath();
grPath.AddEllipse(0, 0, ClientSize.Width, ClientSize.Height);
this.Region = new System.Drawing.Region(grPath);
base.OnPaint(e);
}
}
}
Then, build your application and close this.
Now go to the toolbox and you will see a control named RoundButton.
Then drag and drop this on your Windows form and test it.
GraphicsPath p = new GraphicsPath();
p.AddEllipse(1, 1, button1.Width - 4, button1.Height - 4);
button1.Region = new Region(p);
Code project has many articles about these kinds of things, especially the article RoundButton Windows Control - Ever Decreasing Circles might be of interest since it shows you have to do different kinds of round buttons.
This or this could help if you need to implement your own Button class based on the default Windows Forms button. You can also search online for more examples.
What about 'GDI'?
Implementation Example:
#region <Round Corners> : (Properties)
// [Use Round Corners]
private bool useRoundCorners = false;
[Category("Control Corners"), DisplayName("Round Corners")]
[Description("Set Round Corners.")]
[Browsable(true)]
public bool UseRoundBorders
{
get { return useRoundCorners; }
set { if (useRoundCorners != value) { useRoundCorners = value; Invalidate(); } }
}
// [Ellipse Radius]
private int ellipseRadius = 20;
[Category("Control Corners"), DisplayName("Radius")]
[Description("Set Corner (Ellipse) Radius")]
[Browsable(true)]
public int EllipseRadius
{
get { return ellipseRadius.FixedValue(0, 90); }
set { if (ellipseRadius != value.FixedValue(0, 90)) { ellipseRadius = value; Invalidate(); } }
}
#endregion
#region <Round Corners> : (Draw)
[DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")]
private static extern IntPtr CreateRoundRectRgn
(
int nLeftRect, // x-coordinate of upper-left corner
int nTopRect, // y-coordinate of upper-left corner
int nRightRect, // x-coordinate of lower-right corner-
int nBottomRect, // y-coordinate of lower-right corner
int nWidthEllipse, // width of ellipse
int nHeightEllipse // height of ellipse
);
/// <summary> Draw Corners (Round or Square). </summary>
/// <param name="e"></param>
private void DrawCorners()
{
if (useRoundCorners) { this.Region = Region.FromHrgn(CreateRoundRectRgn(0, 0, Width, Height, ellipseRadius, ellipseRadius)); }
else { this.Region = Region.FromHrgn(CreateRoundRectRgn(0, 0, Width, Height, 0, 0)); }
}
#endregion
/// <summary> Redraw (Update) the Control. </summary>
/// <param name="e"></param>
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
DrawCorners();
}
Extension to Limit Round Corner Radius (*Range: 0-90)
Author: Salvador
// Extension to Set Min. & Max. Values to Properties
public static class Extensions
{
public static int FixedValue(this int value, int min, int max)
{
if (value >= min && value <= max) { return value; }
else if (value > max) { return max; }
else if (value < min) { return min; }
else { return 1; }
}
}
This is what you want
public class RoundButton : Control
{
private readonly Label lbl;
public RoundButton() : base()
{
lbl = new Label
{
Text = Text,
ForeColor = ForeColor,
BackColor = BackColor,
Font = Font
};
CenterInParent();
}
private void CenterInParent()
{
lbl.Left = (Width - lbl.Width) / 2;
lbl.Top = (Height - lbl.Height) / 2;
}
protected override void OnPaint(PaintEventArgs e)
{
GraphicsPath grPath = new GraphicsPath();
grPath.AddEllipse(0, 0, ClientSize.Width, ClientSize.Height);
Region = new Region(grPath);
base.OnPaint(e);
}
protected override void OnMove(EventArgs e)
{
CenterInParent();
base.OnMove(e);
}
protected override void OnTextChanged(EventArgs e)
{
lbl.Text = Text;
base.OnTextChanged(e);
}
protected override void OnForeColorChanged(EventArgs e)
{
lbl.ForeColor = ForeColor;
base.OnForeColorChanged(e);
}
protected override void OnBackColorChanged(EventArgs e)
{
lbl.BackColor = BackColor;
base.OnBackColorChanged(e);
}
protected override void OnFontChanged(EventArgs e)
{
lbl.Font = Font;
base.OnFontChanged(e);
}
}
Pros:
No cuts
Round
Cons:
Wrong designer loader for round button
public class OptionsMenu : Button
{
public OptionsMenu(int NoOfOptions, Point Location, ControlCollection controls,
Size ButtonSize, int DistanceBetweenOptions)
{
Button[] buttons = new Button[NoOfOptions];
for (int i = 0; i < NoOfOptions; i++)
{
buttons[i] = new Button()
{
Size = ButtonSize,
};
GraphicsPath p = new GraphicsPath();
p.AddEllipse(1, 1, buttons[i].Width - 4, buttons[i].Height - 4);
buttons[i].Region = new Region(p);
buttons[i].Location = new Point(Location.X, Location.Y + DistanceBetweenOptions * i);
controls.Add(buttons[i]);
}
}
}
You can call it like this:
OptionsMenu menu = new OptionsMenu(4, new Point(50, 50), Controls, new Size(20, 20), 40);
Controls.AddRange(new Control[]
{
menu
});