Set CircularPictureBox Border Color - c#

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.

Related

Clear property Text in inerited Button class

I have created a Windows Forms Custom Control inheriting from class Button in order to get a round button:
public partial class RoundButton : Button
{
public RoundButton()
{
InitializeComponent();
Width = 16;
Height = 16;
FlatStyle = FlatStyle.Flat;
FlatAppearance.BorderSize = 0;
BackColor = SystemColors.ControlDark;
}
public sealed override Color BackColor
{
get => base.BackColor;
set => base.BackColor = value;
}
protected override void OnPaint(PaintEventArgs pe)
{
GraphicsPath grPath = new GraphicsPath();
grPath.AddEllipse(0, 0, ClientSize.Width, ClientSize.Height);
Region = new Region(grPath);
base.OnPaint(pe);
}
}
When I add the RoundButton control to a form in the designer its property Text is automatically assigned a value (roundButton1). I have tried to "clear" the property in the constructor, but that doesn't work:
Text = string.Empty;
How do I make sure that the property Text has no value when adding the control in the designer? I don't want to "clear" the property in the Properties window in the designer. The property should be "cleared" by default when added in the designer.

c# WinForms - Programmatically disable the method Application.EnableVisualStyles()?

Is is possible to disable Application.EnableVisualStyles(); programmatically? I wanted to turn off the visual styles for a certain part of my application where I could have a colored progress bar. I know you can use System.Drawing to draw it on, but this is much more simpler if I could turn it off temporarily. Is this possible or am I going to have to draw it?
Credits go to GreatJobBob for linking me the MSDN page, the following achieved what I was looking for.
using System.Windows.Forms.VisualStyles;
Application.VisualStyleState = VisualStyleState.NonClientAreaEnabled;
This let me change the color of my progress bar without changing the rest of my controls and form.
Create your own progress bar class. Disabling Application.EnableVisualStyles will cause problems in other UIs such as the MessageBox. Here's a basic class to get you started, just change the forecolor to what you want it to be.
using System;
using System.Drawing;
using System.Windows.Forms;
class MyProgressBar : Control
{
public MyProgressBar()
{
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.Selectable, false);
Maximum = 100;
this.ForeColor = Color.Red; //This is where you choose your color
this.BackColor = Color.White;
}
public decimal Minimum { get; set; }
public decimal Maximum { get; set; }
private decimal mValue;
public decimal Value
{
get { return mValue; }
set { mValue = value; Invalidate(); }
}
protected override void OnPaint(PaintEventArgs e)
{
var rc = new RectangleF(0, 0, (float)(this.Width * (Value - Minimum) / Maximum), this.Height);
using (var br = new SolidBrush(this.ForeColor))
{
e.Graphics.FillRectangle(br, rc);
}
base.OnPaint(e);
}
}

Alternate background color of rows in a CheckedListBox?

I need to alternate the color of the items in my CheckedListBox but "alternatingColors" is not a property of CheckedListBox.
How do I go about making the item's colors alternate?
The OnDrawItem event is inaccessible by default, but if you derive a new control based on CheckedListBox, then you can override the base event.
public class MyCheckedListBox : CheckedListBox
{
private SolidBrush primaryColor = new SolidBrush(Color.White);
private SolidBrush alternateColor = new SolidBrush(Color.LightGreen);
[Browsable(true)]
public Color PrimaryColor
{
get { return primaryColor.Color; }
set { primaryColor.Color = value; }
}
[Browsable(true)]
public Color AlternateColor
{
get { return alternateColor.Color; }
set { alternateColor.Color = value; }
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
base.OnDrawItem(e);
if (Items.Count <= 0)
return;
var contentRect = e.Bounds;
contentRect.X = 16;
e.Graphics.FillRectangle(e.Index%2 == 0 ? primaryColor : alternateColor, contentRect);
e.Graphics.DrawString(Convert.ToString(Items[e.Index]), e.Font, Brushes.Black, contentRect);
}
}
It'll alternate between white and green by default. Make adjustments in the Properties panel at design time, or during runtime.
I don't think this is even possible. You may want to consider using a different control for what you need. A DataGridView might be able to work for you better.

Why when adding new properties to custom control the properties not show?

I have a library class and i added a new User Control and added a code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CustomControl
{
public partial class ExtendedTextBox : UserControl
{
[PropertyTab("Data")]
[Browsable(true)]
[Category("Extended Properties")]
[Description("Set TextBox border Color")]
public string Texts
{
get { return textBox.Text; }
set { textBox.Text = value; }
}
private TextBox textBox;
public ExtendedTextBox()
{
InitializeComponent();
textBox = new TextBox();
textBox.Multiline = true;
textBox.BorderStyle = BorderStyle.None;
this.Controls.Add(textBox);
}
private void ExtendedTextBox_Load(object sender, EventArgs e)
{
}
private void ExtendedTextBox_Paint(object sender, PaintEventArgs e)
{
ControlPaint.DrawBorder(e.Graphics, this.ClientRectangle, Color.Red, ButtonBorderStyle.Solid);
}
private void ExtendedTextBox_Resize(object sender, EventArgs e)
{
textBox.Size = new Size(this.Width - 3, this.Height - 2);
textBox.Location = new Point(2, 1);
}
}
}
When i add the dll file to another windows forms project i drag the control to the designer but in the solution explorer under the control properties i don't see Data and not Extended Properties and not Set TextBox border Color.
I wanted to add a property that when you click on it will give you a sub property and click on that will open the colors pattern so you can change/set a new color to the paint event.
Now in the paint event it's set to Red but i want that there will be a property so the user can set any color.
Not so understand what you tried. But works great for me, after some changes are needed in your code.
public partial class ExtendedTextBox : UserControl
{
[PropertyTab("Data")]
[Browsable(true)]
[Category("Extended Properties")]
[Description("Set TextBox border Color")]
public Color BorderColor { get; set; }
[PropertyTab("Data")]
[Browsable(true)]
[Category("Extended Properties")]
[Description("Set TextBox Text")]
public string Texts
{
get { return textBox.Text; }
set { textBox.Text = value; }
}
private TextBox textBox;
public ExtendedTextBox()
{
textBox = new TextBox();
textBox.Multiline = true;
textBox.BorderStyle = BorderStyle.None;
this.Controls.Add(textBox);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
ControlPaint.DrawBorder(e.Graphics, this.ClientRectangle, BorderColor, ButtonBorderStyle.Solid);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
textBox.Size = new Size(this.Width - 3, this.Height - 2);
textBox.Location = new Point(2, 1);
}
}
EDIT: For apply changes immediately
you need refresh the control BorderColor property on catch the moment of set, which is not possible automatic property, but only full property. so:
//private field needy in full property.
private Color _BorderColor = Color.Red; //= Color.Red; for default color...
[PropertyTab("Data")]
[Browsable(true)]
[Category("Extended Properties")]
[Description("Set TextBox border Color")]
public Color BorderColor
{
get {return _BorderColor ;}
set
{
_BorderColor = value;
Invalidate(); //refresh, trigger new paint.
}
}
Where is property that stores textBox border color? I see only public string Texts property. You should add new property for textBox border:
[PropertyTab("Data")]
[Browsable(true)]
[Category("Extended Properties")]
[Description("Set TextBox border Color")]
public Color BorderColor { get; set; }
Where are you defining the Data class that inherits PropertyTab? If you are not defining it yourself, what Data class did you expect for the designer to use?
The other attributes — Category and Description — work fine for me. The custom property is shown in the "Properties" tab of the PropertyGrid control, in its own "Extended Properties" category (naturally, you have to be grouping properties by category, not alphabetically), with the correct description text (shown when the property is selected in the PropertyGrid).
The PropertyTab attribute has to specify a valid PropertyTab class. If you don't have a Data class for it to use, then obviously the property can't be displayed in the Data class's property tab.

How do I put text on ProgressBar?

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.

Categories