TrackBar with Moving Percentage Label - c#

I cobbled together a custom TrackBar with a percentage representing the current Slider position using the answers from here and here
My custom control is as follows:
using System;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace CustomControls
{
public partial class TrackBarPercent : TrackBar
{
/// <summary>
/// Initializes a new instance of <see cref="TrackBarPercent"/>.
/// </summary>
public TrackBarPercent() : base()
{
InitializeComponent();
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
}
private Font _percentFont = SystemFonts.DefaultFont;
private Color _percentColor = Color.Black;
private SolidBrush _drawBrush = new SolidBrush(Color.Black);
/// <summary>
/// Gets or sets the font of the percent text.
/// </summary>
[Description("Gets or sets the font of the percent text.")]
[Browsable(true)]
public Font PercentFont
{
get { return _percentFont; }
set { _percentFont = value; }
}
/// <summary>
/// Gets or sets the color of the percent text.
/// </summary>
[Description("Gets or sets the color of the percent text.")]
[Browsable(true)]
public Color PercentColor
{
get { return _percentColor; }
set { _percentColor = value; _drawBrush = new SolidBrush(_percentColor); }
}
private Rectangle Slider
{
get
{
try
{
RECT rc = new RECT();
SendMessageRect(this.Handle, TBM_GETTHUMBRECT, IntPtr.Zero, ref rc);
return new Rectangle(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
}
catch
{
return new Rectangle();
}
}
}
private const int TBM_GETTHUMBRECT = 0x400 + 25;
private struct RECT { public int left, top, right, bottom; }
[DllImport("user32.dll", EntryPoint = "SendMessageW")]
private static extern IntPtr SendMessageRect(IntPtr hWnd, int msg, IntPtr wp, ref RECT lp);
private int lastValue = 0;
protected override void WndProc(ref Message m)
{
try
{
base.WndProc(ref m);
if (m.Msg == 0x0F)
{
if (this.Value != lastValue)
{
lastValue = this.Value;
this.Invalidate();
}
using (Graphics graphics = Graphics.FromHwnd(Handle))
{
DrawPercentText(graphics);
}
}
}
catch { }
}
private void DrawPercentText(Graphics g)
{
try
{
Point pctTxtPoint;
if (this.Orientation == System.Windows.Forms.Orientation.Horizontal)
{
pctTxtPoint = new Point((Slider.Left + Slider.Right) / 2, Slider.Bottom);
}
else
{
pctTxtPoint = new Point(Slider.Right + 5, Slider.Top - 2);
}
double percent = (double)this.Value * 100.0 / (double)this.Maximum;
string pctStr = string.Format("{0:0.##}%", percent);
g.DrawString(pctStr, _percentFont, _drawBrush, pctTxtPoint);
}
catch { }
}
[DllImport("user32.dll")]
public extern static int SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
private static int MakeParam(int loWord, int hiWord)
{
return (hiWord << 16) | (loWord & 0xffff);
}
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
SendMessage(this.Handle, 0x0128, MakeParam(1, 0x1), 0);
}
}
}
This works, for the most part, but the text gets redrawn every time I hover my mouse over the slider causing the text to look bold. This effect goes away when another control gets focus. Additionally, I'm getting periodic exceptions thrown by the control that I do not understand.
I keep track of the last value that was set for this control so that it invalidates the view if the value has changed. I needed this to prevent the control from drawing the percentage text over the previous text. I don't think this approach was very elegant but I'm not sure how to go about doing it better.
How can I fix this code up so that the text doesn't become "bold" and fix the exceptions I'm getting?

Related

Tab height does not reflect high DPI on custom/userpaint TabControl

I have written a custom TabControl class, but I'm unable to make the tabs adapt their heights on high DPI screens. On a screen with 200% scaling, the tabs are halfway covered by the actual tab page and its controls, like this:
Apparently the TabControl doesn't adapt the tab heights to fit the larger font, and as a result, the top of the actual page is too high up and covers my tabs. What can I do to enforce the tabs to adapt?
The form has AutoScaleMode set to Dpi, and everything else looks fine, except for this. I'm targeting .NET 4.5.2, and the dpiAware setting is set to true in the manifest file.
Here is my code for the custom TabControl:
/// <summary>
/// A TabControl without 3D borders and other annoyances. Taken from
/// https://stackoverflow.com/questions/27469886/change-color-of-unused-space-of-tabcontrol/27472230
/// and modified.
/// </summary>
public class CleanTabControl : TabControl
{
private class NativeMethods
{
[DllImport("user32.dll")] public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
}
private const int WM_SETFONT = 0x30;
private const int WM_FONTCHANGE = 0x1d;
private int hoverTab = -1;
public event MouseEventHandler CloseClick;
public CleanTabControl()
{
// Take over the painting completely, we want transparency and double-buffering
SetStyle(ControlStyles.UserPaint | ControlStyles.SupportsTransparentBackColor, true);
DoubleBuffered = ResizeRedraw = true;
}
protected override void OnCreateControl()
{
// Necessary to give tabs the correct width
base.OnCreateControl();
OnFontChanged(EventArgs.Empty);
}
protected override void OnFontChanged(EventArgs e)
{
// Necessary to give tabs the correct width
base.OnFontChanged(e);
IntPtr hFont = Font.ToHfont();
NativeMethods.SendMessage(Handle, WM_SETFONT, hFont, (IntPtr)(-1));
NativeMethods.SendMessage(Handle, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero);
UpdateStyles();
}
public override Color BackColor
{
// Override TabControl.BackColor, we need transparency
get { return Color.Transparent; }
set { base.BackColor = Color.Transparent; }
}
protected override void OnPaint(PaintEventArgs e)
{
// ... lot of painting code
}
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
if (SelectedTab != null)
{
if (GetImageRectangle(SelectedIndex).Contains(e.Location))
CloseClick(this, e);
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
hoverTab = -1;
for (int i = 0; i < TabCount; i++)
{
if (GetTabRect(i).Contains(e.Location))
{
if (GetImageRectangle(i).Contains(e.Location))
TabPages[i].ImageIndex = 1;
else
{
hoverTab = i;
TabPages[i].ImageIndex = 0;
}
}
else
TabPages[i].ImageIndex = 0;
}
Invalidate();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
hoverTab = -1;
for (int i = 0; i < TabCount; i++)
{
TabPages[i].ImageIndex = 0;
}
Invalidate();
}
private Rectangle GetImageRectangle(int index)
{
Rectangle r = GetTabRect(index);
int width = ImageList.ImageSize.Width;
int height = ImageList.ImageSize.Height;
int x = r.Right - width - Padding.X;
int y = (r.Top + r.Height - height) / 2 + 1;
if (index != SelectedIndex)
y += 1;
return new Rectangle(x, y, width, height);
}
}
}
I found the solution. In OnCreateControl(), add:
ItemSize = new Size(ItemSize.Width, ItemSize.Height * DpiRatio);
where DpiRatio is the scale factor (e.g. 2 for 200% scaling).

Change combobox drop down list border color in C#

Is it possible to change the border color of a combobox dropdown list in c#?
I want to change the white border to a darker shade to match the dark scheme. I searched through the .net documentation and found the DropDownList.BorderStyle property. However, I'm not sure if this will work. I am using WinForms.
Here is the class I'm using for the combobox:
public class FlattenCombo : ComboBox
{
private Brush BorderBrush = new SolidBrush(SystemColors.WindowFrame);
private Brush ArrowBrush = new SolidBrush(SystemColors.ControlText);
private Brush DropButtonBrush = new SolidBrush(SystemColors.Control);
private Color _borderColor = Color.Black;
private ButtonBorderStyle _borderStyle = ButtonBorderStyle.Solid;
private static int WM_PAINT = 0x000F;
private Color _ButtonColor = SystemColors.Control;
public Color ButtonColor
{
get { return _ButtonColor; }
set
{
_ButtonColor = value;
DropButtonBrush = new SolidBrush(this.ButtonColor);
this.Invalidate();
}
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case 0xf:
Graphics g = this.CreateGraphics();
Pen p = new Pen(Color.Black);
g.FillRectangle(BorderBrush, this.ClientRectangle);
//Draw the background of the dropdown button
Rectangle rect = new Rectangle(this.Width - 17, 0, 17, this.Height);
g.FillRectangle(DropButtonBrush, rect);
//Create the path for the arrow
System.Drawing.Drawing2D.GraphicsPath pth = new System.Drawing.Drawing2D.GraphicsPath();
PointF TopLeft = new PointF(this.Width - 13, (this.Height - 5) / 2);
PointF TopRight = new PointF(this.Width - 6, (this.Height - 5) / 2);
PointF Bottom = new PointF(this.Width - 9, (this.Height + 2) / 2);
pth.AddLine(TopLeft, TopRight);
pth.AddLine(TopRight, Bottom);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//Determine the arrow's color.
if (this.DroppedDown)
{
ArrowBrush = new SolidBrush(SystemColors.HighlightText);
}
else
{
ArrowBrush = new SolidBrush(SystemColors.ControlText);
}
//Draw the arrow
g.FillPath(ArrowBrush, pth);
break;
default:
break;
}
}
[Category("Appearance")]
public Color BorderColor
{
get { return _borderColor; }
set
{
_borderColor = value;
Invalidate(); // causes control to be redrawn
}
}
[Category("Appearance")]
public ButtonBorderStyle BorderStyle
{
get { return _borderStyle; }
set
{
_borderStyle = value;
Invalidate();
}
}
protected override void OnLostFocus(System.EventArgs e)
{
base.OnLostFocus(e);
this.Invalidate();
}
protected override void OnGotFocus(System.EventArgs e)
{
base.OnGotFocus(e);
this.Invalidate();
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
this.Invalidate();
}
}
While the FlatComboBoxAdapter is indeed not available. It is still possible to capture the WM_CTLCOLORLISTBOX windows message and apply a native GDI rectangle to the drop down border. It is a bit of work, but it does the job. (There's a link to an example at the bottom if you wish to skip this)
First off, if you're not familiar with the WM_CTLCOLORLISTBOX windows message, it is described as such:
"Sent to the parent window of a list box before the system draws the list box. By responding to this message, the parent window can set the text and background colors of the list box by using the specified display device context handle."
The message constant would be define like so:
const int WM_CTLCOLORLISTBOX = 0x0134;
Once the message constant is defined, condition for it within your custom ComboBox's overridden WndProc() event:
protected override void WndProc(ref Message m)
{
// Filter window messages
switch (m.Msg)
{
// Draw a custom color border around the drop down pop-up
case WM_CTLCOLORLISTBOX:
base.WndProc(ref m);
DrawNativeBorder(m.LParam);
break;
default: base.WndProc(ref m); break;
}
}
The DrawNativeBorder() method is where we are going to use the Win API to draw our rectangle. It accepts the handle to the drop down as an argument. Before we can do that however, we need to define the native methods, enumerations, and structs that are going to be used:
public enum PenStyles
{
PS_SOLID = 0,
PS_DASH = 1,
PS_DOT = 2,
PS_DASHDOT = 3,
PS_DASHDOTDOT = 4
}
public enum ComboBoxButtonState
{
STATE_SYSTEM_NONE = 0,
STATE_SYSTEM_INVISIBLE = 0x00008000,
STATE_SYSTEM_PRESSED = 0x00000008
}
[StructLayout(LayoutKind.Sequential)]
public struct COMBOBOXINFO
{
public Int32 cbSize;
public RECT rcItem;
public RECT rcButton;
public ComboBoxButtonState buttonState;
public IntPtr hwndCombo;
public IntPtr hwndEdit;
public IntPtr hwndList;
}
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll")]
public static extern IntPtr SetFocus(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);
[DllImport("gdi32.dll")]
public static extern int ExcludeClipRect(IntPtr hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
[DllImport("gdi32.dll")]
public static extern IntPtr CreatePen(PenStyles enPenStyle, int nWidth, int crColor);
[DllImport("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hObject);
[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll")]
public static extern void Rectangle(IntPtr hdc, int X1, int Y1, int X2, int Y2);
public static int RGB(int R, int G, int B)
{
return (R | (G << 8) | (B << 16));
}
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public RECT(int left_, int top_, int right_, int bottom_)
{
Left = left_;
Top = top_;
Right = right_;
Bottom = bottom_;
}
public override bool Equals(object obj)
{
if (obj == null || !(obj is RECT))
{
return false;
}
return this.Equals((RECT)obj);
}
public bool Equals(RECT value)
{
return this.Left == value.Left &&
this.Top == value.Top &&
this.Right == value.Right &&
this.Bottom == value.Bottom;
}
public int Height
{
get
{
return Bottom - Top + 1;
}
}
public int Width
{
get
{
return Right - Left + 1;
}
}
public Size Size { get { return new Size(Width, Height); } }
public Point Location { get { return new Point(Left, Top); } }
// Handy method for converting to a System.Drawing.Rectangle
public System.Drawing.Rectangle ToRectangle()
{
return System.Drawing.Rectangle.FromLTRB(Left, Top, Right, Bottom);
}
public static RECT FromRectangle(Rectangle rectangle)
{
return new RECT(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom);
}
public void Inflate(int width, int height)
{
this.Left -= width;
this.Top -= height;
this.Right += width;
this.Bottom += height;
}
public override int GetHashCode()
{
return Left ^ ((Top << 13) | (Top >> 0x13))
^ ((Width << 0x1a) | (Width >> 6))
^ ((Height << 7) | (Height >> 0x19));
}
public static implicit operator Rectangle(RECT rect)
{
return System.Drawing.Rectangle.FromLTRB(rect.Left, rect.Top, rect.Right, rect.Bottom);
}
public static implicit operator RECT(Rectangle rect)
{
return new RECT(rect.Left, rect.Top, rect.Right, rect.Bottom);
}
}
The DrawNativeBorder() method is defined as:
/// <summary>
/// Non client area border drawing
/// </summary>
/// <param name="handle">The handle to the control</param>
public static void DrawNativeBorder(IntPtr handle)
{
// Define the windows frame rectangle of the control
RECT controlRect;
GetWindowRect(handle, out controlRect);
controlRect.Right -= controlRect.Left; controlRect.Bottom -= controlRect.Top;
controlRect.Top = controlRect.Left = 0;
// Get the device context of the control
IntPtr dc = GetWindowDC(handle);
// Define the client area inside the control rect so it won't be filled when drawing the border
RECT clientRect = controlRect;
clientRect.Left += 1;
clientRect.Top += 1;
clientRect.Right -= 1;
clientRect.Bottom -= 1;
ExcludeClipRect(dc, clientRect.Left, clientRect.Top, clientRect.Right, clientRect.Bottom);
// Create a pen and select it
Color borderColor = Color.Magenta;
IntPtr border = WinAPI.CreatePen(WinAPI.PenStyles.PS_SOLID, 1, RGB(borderColor.R,
borderColor.G, borderColor.B));
// Draw the border rectangle
IntPtr borderPen = SelectObject(dc, border);
Rectangle(dc, controlRect.Left, controlRect.Top, controlRect.Right, controlRect.Bottom);
SelectObject(dc, borderPen);
DeleteObject(border);
// Release the device context
ReleaseDC(handle, dc);
SetFocus(handle);
}
I used the color Magenta to clearly show the painting. That will do it for the border painting. There is however, one more problem. When the drop down displays and the mouse hasn't moved over a drop down item, the default border still shows. To handle that issue, you'd have to determine when the drop down is fully open. Then send a WM_CTLCOLORLISTBOX message of our own to update the border.
I crudely check for the drop down display moment using a timer. I tried various other options but they didn't pan out. Honestly, if someone has a better solution that works, that would be great.
You'll need a timer to check when the drop down actually drops fully:
private Timer _dropDownCheck = new Timer();
The timer is a field in your custom combobox. Set it up in your custom combobox constructor after InitializeComponent():
_dropDownCheck.Interval = 100;
_dropDownCheck.Tick += new EventHandler(dropDownCheck_Tick);
Override the custom combobox's OnDropDown() event, and set up the timer tick event:
/// <summary>
/// On drop down
/// </summary>
protected override void OnDropDown(EventArgs e)
{
base.OnDropDown(e);
// Start checking for the dropdown visibility
_dropDownCheck.Start();
}
/// <summary>
/// Checks when the drop down is fully visible
/// </summary>
private void dropDownCheck_Tick(object sender, EventArgs e)
{
// If the drop down has been fully dropped
if (DroppedDown)
{
// Stop the time, send a listbox update
_dropDownCheck.Stop();
Message m = GetControlListBoxMessage(this.Handle);
WndProc(ref m);
}
}
Lastly, create the following methods to get the drop down handle and to create a WM_CTLCOLORLISTBOX message to send to the control:
/// <summary>
/// Creates a default WM_CTLCOLORLISTBOX message
/// </summary>
/// <param name="handle">The drop down handle</param>
/// <returns>A WM_CTLCOLORLISTBOX message</returns>
public Message GetControlListBoxMessage(IntPtr handle)
{
// Force non-client redraw for focus border
Message m = new Message();
m.HWnd = handle;
m.LParam = GetListHandle(handle);
m.WParam = IntPtr.Zero;
m.Msg = WM_CTLCOLORLISTBOX;
m.Result = IntPtr.Zero;
return m;
}
/// <summary>
/// Gets the list control of a combo box
/// </summary>
/// <param name="handle">Handle of the combo box itself</param>
/// <returns>A handle to the list</returns>
public static IntPtr GetListHandle(IntPtr handle)
{
COMBOBOXINFO info;
info = new COMBOBOXINFO();
info.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(info);
return GetComboBoxInfo(handle, ref info) ? info.hwndList : IntPtr.Zero;
}
That about does that, if you're still confused, it's probably just easier to take a look at the control in this example VS 2010 custom combobox project I have provided:
http://www.pyxosoft.com/downloads/CustomComboBoxBorderColor.zip
I wrestled with this for far too long. I see from a previous question you asked that you've got code from: http://www.codeproject.com/Articles/2433/Flatten-that-Combobox and have set the BackColor:
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case 0xf:
base.BackColor = Color.Black;
Using the WndProc, this was the closest I got but it didn't color the dropdownlists' Popup/ItemSelection border:
...
if (this.DroppedDown)
{
ArrowBrush = new SolidBrush(SystemColors.HighlightText);
Rectangle dropDownBounds = new Rectangle(0, 0, Width,Height + DropDownHeight );
//ControlPaint.DrawBorder(g, dropDownBounds, _borderColor, _borderStyle);
ControlPaint.DrawBorder(g, dropDownBounds, _borderColor,1, ButtonBorderStyle.Dotted ,Color.GreenYellow,1,ButtonBorderStyle.Solid ,Color.Gold,1,ButtonBorderStyle.Dashed,Color.HotPink,1,ButtonBorderStyle.Solid);
}
It turns out that the class FlatComboBoxAdapter needed to do this is private and the recommendation is use WPF:
ComboBox DropDown-Area Border Color
More info: Combobox borderstyle - but even with LarsTech and Hans suggestions (using the non-client paint message) it still doesn't work and flickers horribly.
Other suggestion besides WPF, rewrite Combobox .Net Framework code: http://www.dotnetframework.org/default.aspx/FX-1434/FX-1434/1#0/untmp/whidbey/REDBITS/ndp/fx/src/WinForms/Managed/System/WinForms/ComboBox#cs/2/ComboBox#cs

Is it possible to define a left-handed ComboBox (ie. scrollbar on the left) in a winforms application?

I have a WinForms application that is being used on tablet PC's with touch screens. The application is developed with Visual Studio 2008 and used version 3.5 of the .Net framework. I have had a request from a left-handed client to put the scrollbar of the ComboBoxes on the left-hand side of the dropdown area rather than the right, but I am not sure how to do this, or if it is even possible to do. Has anyone done this before or know how it can be done?
There is a solution here using Win32 API calls for modifying the ComboBox: http://www.codeguru.com/csharp/csharp/cs_controls/custom/print.php/c15261
About halfway down, below the 'Aligning ComoBox Objects' heading.
There is also a link to some sample code at the end of the page.
Example - Left scrollbar & left text
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace ComboBoxMod
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new frmMain());
}
}
public partial class frmMain : Form
{
private ComboBoxMod.ComboBox cbTest; //this is the modified combo box
private System.Windows.Forms.Button btnToggleAlign;
private System.ComponentModel.IContainer components = null;
public frmMain()
{
this.cbTest = new ComboBoxMod.ComboBox();
this.btnToggleAlign = new Button();
InitialiseComponent();
for (int i = 0; i < 50; i++)
{
cbTest.Items.Add(i);
}
}
void btnToggleAlign_Click(object sender, EventArgs e)
{
if (this.cbTest.ScrollAlignment == ComboBox.Alignment.Right) //If Right Aligned
{
this.cbTest.ScrollAlignment = ComboBox.Alignment.Left; //Set To Left
}
else
{
this.cbTest.ScrollAlignment = ComboBox.Alignment.Right; //Set To Right
}
}
private void InitialiseComponent()
{
this.SuspendLayout();
this.cbTest.FormattingEnabled = true;
this.cbTest.Location = new System.Drawing.Point(12, 12);
this.cbTest.Name = "cbTest";
this.cbTest.Size = new System.Drawing.Size(180, 21);
this.cbTest.TabIndex = 0;
this.btnToggleAlign.Location = new System.Drawing.Point(12, 42);
this.btnToggleAlign.Name = "btnScrollAlign";
this.btnToggleAlign.Size = new System.Drawing.Size(180, 23);
this.btnToggleAlign.TabIndex = 0;
this.btnToggleAlign.Text = "Toggle Scrollbar Alignment";
this.btnToggleAlign.UseVisualStyleBackColor = true;
this.btnToggleAlign.Click += new EventHandler(btnToggleAlign_Click);
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(200, 75);
this.Controls.Add(this.cbTest);
this.Controls.Add(this.btnToggleAlign);
this.Name = "frmMain";
this.Text = "frmMain";
this.ResumeLayout(false);
}
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
}
public class ComboBox : System.Windows.Forms.ComboBox //Inherits ComboBox
{
[DllImport("user32", CharSet = CharSet.Auto)]
public extern static int GetWindowLong(IntPtr hwnd, int nIndex); //Retrieve Info About Specified Window
[DllImport("user32")]
public static extern int SetWindowLong(IntPtr hwnd, int nIndex, int dwNewLong); //Change An Attribute Of Specified Window
[DllImport("user32.dll")]
public static extern int GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi); //Retrieve Info About Specified Combo Box
[StructLayout(LayoutKind.Sequential)]
public struct COMBOBOXINFO //Contains ComboBox Status Info
{
public Int32 cbSize;
public RECT rcItem;
public RECT rcButton;
public ComboBoxButtonState caState;
public IntPtr hwndCombo;
public IntPtr hwndEdit;
public IntPtr hwndList;
}
[StructLayout(LayoutKind.Sequential)] //Describes Width, Height, And Location Of A Rectangle
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
public enum ComboBoxButtonState //Determines Current State Of ComboBox
{
STATE_SYSTEM_NONE = 0,
STATE_SYSTEM_INVISIBLE = 0x00008000,
STATE_SYSTEM_PRESSED = 0x00000008
}
/// <summary>
/// Alignment Enum For Left & Right
/// </summary>
public enum Alignment
{
Left = 0,
Right = 1
}
/// <summary>
/// Align ScrollBar
/// </summary>
public Alignment ScrollAlignment
{
get
{
return Scroll; //Get Value
}
set
{
if (Scroll == value) //If Not Valid Value
return;
Scroll = value; //Set Value
AlignScrollbar(); //Call AlignScroll Method
}
}
private const int GWL_EXSTYLE = -20; //ComboBox Style
private const int WS_EX_RIGHT = 4096; //Right Align Text
private const int WS_EX_LEFTSCROLLBAR = 16384; //Left ScrollBar
private const int WS_EX_RIGHTSCROLLBAR = 128; //Right ScrollBar
private IntPtr handle; //Handle Of ComboBox
private Alignment Scroll; //Alignment Options For ScrollBar
public ComboBox()
{
handle = CASGetHandle(this); //Get Handle Of ComboBox
Scroll = Alignment.Right; //default alignment
}
/// <summary>
/// Retrieves ComboBox Handle
/// </summary>
/// <param name="CASCombo"></param>
/// <returns></returns>
public IntPtr CASGetHandle(ComboBox CASCombo)
{
COMBOBOXINFO CASCBI = new COMBOBOXINFO(); //New ComboBox Settings Object
CASCBI.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(CASCBI); //Call In Correct Size
GetComboBoxInfo(CASCombo.Handle, ref CASCBI); //Obtain Handle
return CASCBI.hwndList; //Return Handle
}
/// <summary>
/// Align The ComboBox ScrollBar
/// </summary>
private void AlignScrollbar()
{
if (handle != IntPtr.Zero) //If Valid Handle
{
int style = GetWindowLong(handle, GWL_EXSTYLE); //Get ComboBox Style
switch (Scroll)
{
case Alignment.Left:
style = WS_EX_LEFTSCROLLBAR; //Align ScrollBare To The Left
break;
case Alignment.Right:
style = WS_EX_RIGHTSCROLLBAR; //Align ScrollBare To The Right
break;
}
SetWindowLong(handle, GWL_EXSTYLE, style); //Apply On ComboBox
}
}
}
}
in combobox control there is right to left property, just set it to true
That cannot be done in WinForms. Although you can come up with your own solution of control, representing the required structure.

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.

Round shaped buttons

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
});

Categories