Draw character in center of custom control - Font Awesome Glyph - c#

I'm trying to build custom user control that will display Font Awesome Glyphs inside winforms Button.
I found GitHub repo with similar control, but I would like to use Button as base of my control.
I'm able to show glyph, but I can't align it correctly:
Green dotted line shows button size, blue lines indicates center of control and red lines show rectangle that graphics.MeasureString is returning.
My OnPaint method looks like this:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var graphics = e.Graphics;
// Set best quality
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
if(!DesignMode)
{
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.SmoothingMode = SmoothingMode.HighQuality;
}
var letter = char.ConvertFromUtf32((int)_icon);
Brush b;
if (!Enabled)
{
b = Brushes.LightGray;
}
else if (_mouseDown)
{
b = new SolidBrush(_clickColor);
}
else if (_mouseOver)
{
b = new SolidBrush(_hoverColor);
}
else
{
b = new SolidBrush(ForeColor);
}
SizeF s = graphics.MeasureString(letter, new Font(Fonts.Families[0], _iconSize, GraphicsUnit.Point), Width);
float w = s.Width;
float h = s.Height;
// center icon
float left = Padding.Left + (Width - w)/2;
float top = Padding.Top + (Height - h)/2;
if (DesignMode)
{
graphics.DrawRectangle(Pens.Red, top, left, w, h);
}
graphics.DrawString(letter, new Font(Fonts.Families[0], _iconSize, GraphicsUnit.Point), b, new PointF(left, top));
if (DesignMode)
{
var pen = new Pen(_hoverColor, 1) { DashStyle = DashStyle.Dash, Alignment = PenAlignment.Inset };
graphics.DrawRectangle(pen, Padding.Left, Padding.Top, Width - Padding.Left - Padding.Right - 1, Height - Padding.Top - Padding.Bottom - 1);
graphics.DrawLine(Pens.Blue, Padding.Left, Padding.Top, Width - Padding.Left, Height - Padding.Top);
graphics.DrawLine(Pens.Blue, Width - Padding.Left, Padding.Top, Padding.Left, Height - Padding.Top);
}
}
I tried using solution from this question but without any luck.
How can I draw single character (glyph) in my control exact center (center of control and center of glyph should align)
Here is code for my control:
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace MyControls
{
class FontButton:Button
{
#region Public
public FontButton()
{
base.FlatStyle = FlatStyle.Flat;
base.FlatAppearance.BorderSize = 0;
base.FlatAppearance.MouseOverBackColor = Color.Transparent;
base.FlatAppearance.MouseDownBackColor = Color.Transparent;
base.Text = "";
base.MinimumSize = new Size(32,32);
Size = new Size(32,32);
_hoverColor = Color.FromArgb(144, 188, 0);
_clickColor = Color.Green;
_icon=IconType.Android;
_iconSize = 40;
}
private int _iconSize;
[DefaultValue(typeof(int), "40"), Category("Appearance"), Description("Icon size in points")]
public int IconSize
{
get { return _iconSize; }
set
{
_iconSize = value;
Invalidate();
}
}
private Color _hoverColor;
[DefaultValue(typeof(Color), "144, 188, 0"), Category("Appearance"), Description("Color when mouse over")]
public Color HoverColor
{
get { return _hoverColor; }
set
{
_hoverColor = value;
Invalidate();
}
}
private Color _clickColor;
[DefaultValue(typeof(Color), "Green"), Category("Appearance"), Description("Color when mouse click")]
public Color ClickColor
{
get { return _clickColor; }
set
{
_clickColor = value;
Invalidate();
}
}
private IconType _icon;
[DefaultValue(typeof(IconType), "Android"), Category("Appearance"), Description("Icon")]
public IconType Icon
{
get { return _icon; }
set
{
_icon = value;
Invalidate();
}
}
#endregion
#region Static
static FontButton()
{
InitialiseFont();
}
#region NATIVE
[DllImport("gdi32.dll")]
private static extern IntPtr AddFontMemResourceEx(byte[] pbFont, int cbFont, IntPtr pdv, out uint pcFonts);
#endregion
private static readonly PrivateFontCollection Fonts = new PrivateFontCollection();
private static void InitialiseFont()
{
try
{
byte[] fontdata = ImageButtonTest.Properties.Resources.fontawesome_webfont;
IntPtr ptrFont = Marshal.AllocCoTaskMem(fontdata.Length);
uint cFonts;
AddFontMemResourceEx(fontdata, fontdata.Length, IntPtr.Zero, out cFonts);
Marshal.Copy(fontdata, 0, ptrFont, fontdata.Length);
Fonts.AddMemoryFont(ptrFont, fontdata.Length);
Marshal.FreeCoTaskMem(ptrFont);
}
catch (Exception)
{
Debug.WriteLine("Error");
throw;
}
}
#endregion
#region Overrides
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var graphics = e.Graphics;
// Set best quality
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
if(!DesignMode)
{
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.SmoothingMode = SmoothingMode.HighQuality;
}
var letter = char.ConvertFromUtf32((int)_icon);//char.ConvertFromUtf32(0xf190);
Brush b;
if (!Enabled)
{
b = Brushes.LightGray;
}
else if (_mouseDown)
{
b = new SolidBrush(_clickColor);
}
else if (_mouseOver)
{
b = new SolidBrush(_hoverColor);
}
else
{
b = new SolidBrush(ForeColor);
}
SizeF s = graphics.MeasureString(letter, new Font(Fonts.Families[0], _iconSize, GraphicsUnit.Point), Width);
//SizeF s = TextRenderer.MeasureText(letter, new Font(Fonts.Families[0], _iconSize, GraphicsUnit.Point), new Size(20, 20), TextFormatFlags.NoPadding);
//Debug.WriteLine(stringSize);
//Debug.WriteLine(s);
float w = s.Width;
float h = s.Height;
// center icon
float left = Padding.Left + (Width - w)/2;
float top = Padding.Top + (Height - h)/2;
if (DesignMode)
{
graphics.DrawRectangle(Pens.Red, top, left, w, h);
}
graphics.DrawString(letter, new Font(Fonts.Families[0], _iconSize, GraphicsUnit.Point), b, new PointF(left, top));
if (DesignMode)//Process.GetCurrentProcess().ProcessName == "devenv")
{
var pen = new Pen(_hoverColor, 1) { DashStyle = DashStyle.Dash, Alignment = PenAlignment.Inset };
graphics.DrawRectangle(pen, Padding.Left, Padding.Top, Width - Padding.Left - Padding.Right - 1, Height - Padding.Top - Padding.Bottom - 1);
graphics.DrawLine(Pens.Blue, Padding.Left, Padding.Top, Width - Padding.Left, Height - Padding.Top);
graphics.DrawLine(Pens.Blue, Width - Padding.Left, Padding.Top, Padding.Left, Height - Padding.Top);
}
}
private bool _mouseOver;
protected override void OnMouseEnter(EventArgs e)
{
_mouseOver = true;
base.OnMouseEnter(e);
}
protected override void OnMouseLeave(EventArgs e)
{
_mouseOver = false;
base.OnMouseLeave(e);
}
private bool _mouseDown;
protected override void OnMouseDown(MouseEventArgs e)
{
_mouseDown = true;
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
_mouseDown = false;
base.OnMouseUp(e);
}
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new FlatStyle FlatStyle { get; set; }
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new FlatButtonAppearance FlatAppearance { get; set; }
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new string Text { get; set; }
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new Size MinimumSize { get; set; }
#endregion
}
}
and IconType enum:
namespace MyControls
{
public enum IconType
{
Adjust = 0xf042,
Adn = 0xf170,
AlignCenter = 0xf037,
AlignJustify = 0xf039,
AlignLeft = 0xf036,
AlignRight = 0xf038,
Ambulance = 0xf0f9,
Anchor = 0xf13d,
Android = 0xf17b,
ArrowCircleDown = 0xf0ab,
ArrowCircleLeft = 0xf0a8,
ArrowCircleODown = 0xf01a,
ArrowCircleOLeft = 0xf190,
ArrowCircleORight = 0xf18e,
ArrowCircleOUp = 0xf01b,
ArrowCircleRight = 0xf0a9,
ArrowCircleUp = 0xf0aa,
ArrowDown = 0xf063,
ArrowLeft = 0xf060,
ArrowRight = 0xf061,
ArrowUp = 0xf062,
Arrows = 0xf047,
ArrowsAlt = 0xf0b2,
ArrowsH = 0xf07e,
ArrowsV = 0xf07d,
User = 0xf007,
UserMd = 0xf0f0,
Users = 0xf0c0,
Stop = 0xf04d
}
}
All it needs is to include fontawesome_webfont.ttf in resources.

This is from MSDN :
The MeasureString method is designed for use with individual strings
and includes a small amount of extra space before and after the string
to allow for overhanging glyphs. Also, the DrawString method adjusts
glyph points to optimize display quality and might display a string
narrower than reported by MeasureString. To obtain metrics suitable
for adjacent strings in layout (for example, when implementing
formatted text), use the MeasureCharacterRanges method or one of the
MeasureString methods that takes a StringFormat and pass
GenericTypographic. Also ensure the TextRenderingHint for the Graphics
is AntiAlias.
So to make the MeasureString leave out all extra space pass in the GenericTypographic like this:
SizeF s = graphics.MeasureString(letter, new Font(Fonts.Families[0],
_iconSize, GraphicsUnit.Point), Width, StringFormat.GenericTypographic);
I found this to help a lot, although I haven't had to do it quite as exact as you..

I've got best result using GraphicsPath:
var path = new GraphicsPath()
path.AddString(_letter, font.FontFamily, (int) font.Style, font.Size, new Point(0, 0), StringFormat.GenericTypographic);
Rectangle area = Rectangle.Round(path.GetBounds());

Related

Trouble with creating custom control in C#

I want to have a rounded custom button, for this aim I created a class extended from Button class:
public class CustomButton : Button
{
//Fields
private int borderSize = 0;
private int borderRadius = 40;
private Color borderColor = Color.PaleVioletRed;
[Category("Custom Controls")]
public int BorderSize
{
get
{
return borderSize;
}
set
{
borderSize = value;
this.Invalidate();
}
}
[Category("Custom Controls")]
public int BorderRadius
{
get
{
return borderRadius;
}
set
{
borderRadius = value;
this.Invalidate();
}
}
[Category("Custom Controls")]
public Color BorderColor {
get
{
return borderColor;
}
set
{
borderColor = value;
this.Invalidate();
}
}
[Category("Custom Controls")]
public Color TextColor
{
get
{
return this.ForeColor;
}
set
{
this.ForeColor = value;
}
}
//Constructor
CustomButton()
{
this.FlatStyle = FlatStyle.Flat;
this.FlatAppearance.BorderSize = 0;
this.Size = new Size(150,40);
this.BackColor = Color.MediumSlateBlue;
this.ForeColor = Color.White;
}
//Methods
private GraphicsPath GetFigurePath(RectangleF rect,float radius)
{
GraphicsPath path = new GraphicsPath();
path.StartFigure();
path.AddArc(rect.X, rect.Y, radius, radius, 180, 90);
path.AddArc(rect.Width - radius, rect.Y, radius, radius, 270, 90);
path.AddArc(rect.Width - radius, rect.Height - radius, radius, radius, 0, 90);
path.AddArc(rect.X, rect.Height-radius, radius, radius, 90, 90);
path.CloseFigure();
return path;
}
protected override void OnPaint(PaintEventArgs pevent)
{
base.OnPaint(pevent);
pevent.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
RectangleF rectSurface = new RectangleF(0, 0, this.Width, this.Height);
RectangleF rectBorder = new RectangleF(1, 1, this.Width - 0.8F, this.Height - 1);
if(borderRadius > 2) //Rounded Button
{
using (GraphicsPath pathSurface = GetFigurePath(rectSurface, borderRadius))
using (GraphicsPath pathBorder = GetFigurePath(rectBorder, borderRadius-1F))
using (Pen penSurface = new Pen(this.Parent.BackColor, 2))
using (Pen penBorder = new Pen(borderColor, borderSize))
{
penBorder.Alignment = PenAlignment.Inset;
//button Surface
this.Region = new Region(pathSurface);
//Draw Surface Border for HD result
pevent.Graphics.DrawPath(penBorder, pathBorder);
//Button border
if (borderSize >= 1)
//Draw Control border
pevent.Graphics.DrawPath(penBorder, pathBorder);
}
}
else //Normal Button
{
//Button surface
this.Region = new Region(rectSurface);
//Button border
if(borderSize >= 1)
{
using (Pen penBorder = new Pen(borderColor, borderSize))
{
penBorder.Alignment = PenAlignment.Inset;
pevent.Graphics.DrawRectangle(penBorder, 0, 0, this.Width - 1, this.Height - 1);
}
}
}
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);base.OnHandleCreated(e);
this.Parent.BackColorChanged += new EventHandler(Container_BackColorChanged);
}
private void Container_BackColorChanged(object sender, EventArgs e)
{
if (this.DesignMode)
this.Invalidate();
}
private void InitializeComponent()
{
this.SuspendLayout();
this.ResumeLayout(false);
}
}
}
But when I use my custom button, I get this warning:
Field 'frmMainPanel.customButton1' is never assigned to, and will always have its default value null
And when running app, get:
in this error I get 'Object reference not set to an instance of an object.' but can't resolve this problem
The default constructor CustomButton is private. Change it to public.
public CustomButton()

WinForms rounded button has a black box behind it

So... I have made an override to my OnPaint event to get rounded corners, and it sorta worked, but it had a huge problem: no antialiasing due to doing it with a Region.
So I dropped that and switched to e.Graphics.DrawPath, which, again, sorta works. It gives me the shape I want and smooth edges. Just one problem... There seems to be a black rectangle behind it (as per the image below)
My code is as follows (and no, I didn't forget base.OnPaint(e), I tried putting that in many spots on this code and it just made things worse):
public class Button : System.Windows.Forms.Button
{
public BorderRadius BorderRadius { get; set; } = new BorderRadius(0);
public Color BorderColor { get; set; } = Color.Transparent;
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
SolidBrush brush = new(BackColor);
GraphicsPath gp = Transform.BorderRadius(ClientRectangle, new(40));
gp.CloseFigure();
StringFormat formatter = new() {
LineAlignment = StringAlignment.Center,
Alignment = StringAlignment.Center
};
RectangleF rectangle = new(0, 0, e.ClipRectangle.Width, e.ClipRectangle.Height);
e.Graphics.FillRectangle(new SolidBrush(Color.Transparent), ClientRectangle);
e.Graphics.FillPath(brush, gp);
e.Graphics.DrawString(Text, Font, new SolidBrush(ForeColor), rectangle, formatter);
}
}
If you are interested in the Transform and BorderRadius classes they are in the snippet below, but I don't think they are the problem:
class Transform
{
public static GraphicsPath BorderRadius(Rectangle pRect, BorderRadius borderRadius) {
GraphicsPath gp = new GraphicsPath();
if (borderRadius.TopLeft > 0) {
gp.AddArc(
pRect.X,
pRect.Y,
borderRadius.TopLeft,
borderRadius.TopLeft,
180,
90
);
} else {
gp.AddLine(
pRect.X,
pRect.Y,
pRect.X + pRect.Width - borderRadius.TopRight,
pRect.Y
);
}
if (borderRadius.TopRight > 0) {
gp.AddArc(
pRect.X + pRect.Width - borderRadius.TopRight,
pRect.Y,
borderRadius.TopRight,
borderRadius.TopRight,
270,
90
);
} else {
gp.AddLine(
pRect.X + borderRadius.TopLeft,
pRect.Y,
pRect.X + pRect.Width,
pRect.Y
);
}
if (borderRadius.BottomRight > 0) {
gp.AddArc(
pRect.X + pRect.Width - borderRadius.BottomRight,
pRect.Y + pRect.Height - borderRadius.BottomRight,
borderRadius.BottomRight,
borderRadius.BottomRight,
0,
90
);
} else {
gp.AddLine(
pRect.X + pRect.Width,
pRect.Y + pRect.Height,
pRect.X + borderRadius.BottomLeft,
pRect.Y + pRect.Height
);
}
if (borderRadius.BottomLeft > 0) {
gp.AddArc(
pRect.X,
pRect.Y + pRect.Height - borderRadius.BottomLeft,
borderRadius.BottomLeft,
borderRadius.BottomLeft,
90,
90
);
} else {
gp.AddLine(
pRect.X + pRect.Width - borderRadius.BottomRight,
pRect.Y + pRect.Height,
pRect.X,
pRect.Y + pRect.Height
);
}
return gp;
}
}
public class BorderRadius {
public int TopLeft { get; init; } = 0;
public int TopRight { get; init; } = 0;
public int BottomLeft { get; init; } = 0;
public int BottomRight { get; init; } = 0;
public BorderRadius(int all) {
TopLeft = TopRight = BottomLeft = BottomRight = all;
}
public BorderRadius(int topLeftAndBottomRight, int topRightAndBottomLeft) {
TopLeft = BottomRight = topLeftAndBottomRight;
TopRight = BottomLeft = topRightAndBottomLeft;
}
public BorderRadius(int topLeft, int topRightAndBottomLeft, int bottomRight) {
TopRight = BottomLeft = topRightAndBottomLeft;
TopLeft = topLeft;
BottomRight = bottomRight;
}
public BorderRadius(int topLeft, int topRight, int bottomRight, int bottomLeft) {
TopLeft = topLeft;
TopRight = topRight;
BottomLeft = bottomLeft;
BottomRight = bottomRight;
}
public void Deconstruct(out int topLeft, out int topRight, out int bottomRight, out int bottomLeft) {
topLeft = TopLeft;
topRight = TopRight;
bottomRight = BottomRight;
bottomLeft = BottomLeft;
}
}

Which method need to override to round the corner of NotiIcon's ContextMenuStrip?

In windows 11, I have found that almost every Systray Icon's Context Menu has a round corner.
As there is no Systray Menu for WPF application, I have used Window's form NotiIcon and ContextMenuStrip.
I have fully customized ToolStripProfessionalRenderer with ProfessionalColorTable to support dark mode
as per clients' requirements. Now I'm looking for rounding the corner. I was used "RoundedEdges = true;"
but not worked. I need to know exactly which method need to override to round the corner of ContextMenuStrip.
Here below is my renderer sample code.
///Menu Renderer
public class MenuRenderer : ToolStripProfessionalRenderer
{
//Fields
private Color primaryColor;
private Color textColor;
private int arrowThickness;
private WindowsTheme systrayTheme;
[Browsable(false)]
public WindowsTheme SystrayTheme
{
get { return systrayTheme; }
set { systrayTheme = value; }
}
//Constructor
public MenuRenderer(bool isMainMenu, Color primaryColor, Color textColor, Color menuItemMouseOverColor, Color menuItemMouseOverBorderColor, WindowsTheme theme)
: base(new MenuColorTable(isMainMenu, primaryColor, menuItemMouseOverColor, menuItemMouseOverBorderColor, theme))
{
RoundedEdges = true;
this.primaryColor = primaryColor;
this.systrayTheme = theme;
if (isMainMenu)
{
arrowThickness = 2;
if (textColor == Color.Empty) //Set Default Color
this.textColor = Color.Gainsboro;
else//Set custom text color
this.textColor = textColor;
}
else
{
arrowThickness = 1;
if (textColor == Color.Empty) //Set Default Color
this.textColor = Color.DimGray;
else//Set custom text color
this.textColor = textColor;
}
}
//Overrides
protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e)
{
base.OnRenderItemText(e);
e.Item.ForeColor = e.Item.Selected ? Color.White : textColor;
}
protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e)
{
//Fields
var graph = e.Graphics;
var arrowSize = new Size(5, 10);
var arrowColor = e.Item.Selected ? Color.White : primaryColor;
var rect = new Rectangle(e.ArrowRectangle.Location.X, (e.ArrowRectangle.Height - arrowSize.Height) / 2,
arrowSize.Width, arrowSize.Height);
using (GraphicsPath path = new GraphicsPath())
using (Pen pen = new Pen(arrowColor, arrowThickness))
{
//Drawing
graph.SmoothingMode = SmoothingMode.AntiAlias;
path.AddLine(rect.Left, rect.Top, rect.Right, rect.Top + rect.Height / 2);
path.AddLine(rect.Right, rect.Top + rect.Height / 2, rect.Left, rect.Top + rect.Height);
graph.DrawPath(pen, path);
}
}
protected override void OnRenderGrip(ToolStripGripRenderEventArgs e)
{
Rectangle rectangle = new Rectangle(e.AffectedBounds.X, e.AffectedBounds.Y, e.AffectedBounds.Width, e.AffectedBounds.Height);
GraphicsPath graphicsPath = RoundedRect(rectangle, 8);
using (Pen pen = new Pen(Color.Green,5))
{
e.Graphics.DrawPath(pen, graphicsPath);
//e.Graphics.FillPath(pen.Brush, graphicsPath);
}
}
protected override void OnRenderToolStripPanelBackground(ToolStripPanelRenderEventArgs e)
{
//base.OnRenderToolStripPanelBackground(e);
Rectangle rectangle = new Rectangle(e.ToolStripPanel.Location.X, e.ToolStripPanel.Location.Y,
e.ToolStripPanel.ClientRectangle.Width, e.ToolStripPanel.ClientRectangle.Height);
GraphicsPath graphicsPath = RoundedRect(rectangle, 8);
using (Pen pen = new Pen(Color.Green, 5))
{
//e.Graphics.DrawPath(pen, graphicsPath);
e.Graphics.FillPath(pen.Brush, graphicsPath);
}
}
protected override void OnRenderStatusStripSizingGrip(ToolStripRenderEventArgs e)
{
//base.OnRenderStatusStripSizingGrip(e);
Rectangle rectangle = new Rectangle(e.AffectedBounds.X, e.AffectedBounds.Y, e.AffectedBounds.Width, e.AffectedBounds.Height);
GraphicsPath graphicsPath = RoundedRect(rectangle, 8);
using (Pen pen = new Pen(Color.Green,5))
{
e.Graphics.DrawPath(pen, graphicsPath);
//e.Graphics.FillPath(pen.Brush, graphicsPath);
}
}
protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e)
{
//Fields
//var graph = e.Graphics;
//var borderZise = new Size(e.AffectedBounds.Width + 10, e.AffectedBounds.Height + 10);
//var borderColor = Color.DeepPink;
//var rect = new Rectangle(e.AffectedBounds.X, (e.AffectedBounds.Height - borderZise.Height) / 2,
// borderZise.Width, borderZise.Height);
//using (GraphicsPath path = new GraphicsPath())
//using (Pen pen = new Pen(borderColor, arrowThickness))
//{
// //Drawing
// graph.SmoothingMode = SmoothingMode.AntiAlias;
// path.AddLine(rect.Left, rect.Top, rect.Right, rect.Top + rect.Height / 2);
// path.AddLine(rect.Right, rect.Top + rect.Height / 2, rect.Left, rect.Top + rect.Height);
// graph.DrawPath(pen, path);
//}
//DrawRoundedRectangle(e.Graphics, e.AffectedBounds.X, e.AffectedBounds.Y, e.AffectedBounds.Width - 5, e.AffectedBounds.Height, 15, Color.Red);
//Rectangle rectangle = new Rectangle(e.AffectedBounds.X, e.AffectedBounds.Y, e.AffectedBounds.Width, e.AffectedBounds.Height);
//GraphicsPath graphicsPath = RoundedRect(rectangle, 8);
//using (Pen pen = new Pen(Color.Green, 2))
//{
// //e.Graphics.DrawPath(pen, graphicsPath);
// e.Graphics.FillPath(pen.Brush, graphicsPath);
//}
}
public GraphicsPath RoundedRect(Rectangle bounds, int radius)
{
int diameter = radius * 2;
Size size = new Size(diameter, diameter);
Rectangle arc = new Rectangle(bounds.Location, size);
GraphicsPath path = new GraphicsPath();
if (radius == 0)
{
path.AddRectangle(bounds);
return path;
}
// top left arc
path.AddArc(arc, 180, 90);
// top right arc
arc.X = bounds.Right - diameter;
path.AddArc(arc, 270, 90);
// bottom right arc
arc.Y = bounds.Bottom - diameter;
path.AddArc(arc, 0, 90);
// bottom left arc
arc.X = bounds.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
return path;
}
}
////////Custom Menu
public class CustomContextMenu : ContextMenuStrip
{
//Fields
private bool isMainMenu;
private int menuItemHeight = 20;
private int menuItemWidth = 20;
private Color menuItemTextColor = Color.Empty;
private Color primaryColor = Color.Empty;
private Color MouseOverColor = Color.Empty;
private Color MouseOverBorderColor = Color.Empty;
private WindowsTheme systrayTheme = WindowsTheme.Light;
private Bitmap menuItemHeaderSize;
//Constructor
public CustomContextMenu()
{
}
//Properties
[Browsable(false)]
public bool IsMainMenu
{
get { return isMainMenu; }
set { isMainMenu = value; }
}
[Browsable(false)]
public int MenuItemHeight
{
get { return menuItemHeight; }
set { menuItemHeight = value; }
}
[Browsable(false)]
public int MenuItemWidth
{
get { return menuItemWidth; }
set { menuItemWidth = value; }
}
[Browsable(false)]
public Color MenuItemTextColor
{
get { return menuItemTextColor; }
set { menuItemTextColor = value; }
}
[Browsable(false)]
public Color PrimaryColor
{
get { return primaryColor; }
set { primaryColor = value; }
}
[Browsable(false)]
public Color MenuItemMouseOverColor
{
get { return MouseOverColor; }
set { MouseOverColor = value; }
}
[Browsable(false)]
public Color MenuItemMouseOverBorderColor
{
get { return MouseOverBorderColor; }
set { MouseOverBorderColor = value; }
}
[Browsable(false)]
public WindowsTheme SystrayTheme
{
get { return systrayTheme; }
set { systrayTheme = value; }
}
//Private methods
private void LoadMenuItemHeight()
{
if (isMainMenu)
menuItemHeaderSize = new Bitmap(menuItemWidth, menuItemHeight);
else menuItemHeaderSize = new Bitmap(menuItemWidth-5, menuItemHeight);
foreach (Forms.ToolStripMenuItem menuItemL1 in this.Items)
{
menuItemL1.ImageScaling = ToolStripItemImageScaling.None;
if (menuItemL1.Image == null) menuItemL1.Image = menuItemHeaderSize;
foreach (Forms.ToolStripMenuItem menuItemL2 in menuItemL1.DropDownItems)
{
menuItemL2.ImageScaling = ToolStripItemImageScaling.None;
if (menuItemL2.Image == null) menuItemL2.Image = menuItemHeaderSize;
foreach (Forms.ToolStripMenuItem menuItemL3 in menuItemL2.DropDownItems)
{
menuItemL3.ImageScaling = ToolStripItemImageScaling.None;
if (menuItemL3.Image == null) menuItemL3.Image = menuItemHeaderSize;
foreach (Forms.ToolStripMenuItem menuItemL4 in menuItemL3.DropDownItems)
{
menuItemL4.ImageScaling = ToolStripItemImageScaling.None;
if (menuItemL4.Image == null) menuItemL4.Image = menuItemHeaderSize;
///Level 5++
}
}
}
}
}
//Overrides
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if (this.DesignMode == false)
{
switch (SystrayTheme)
{
case WindowsTheme.Light:
{
menuItemTextColor = Color.Black;
}
break;
case WindowsTheme.Dark:
{
menuItemTextColor = Color.White;
}
break;
case WindowsTheme.HighContrast:
{
menuItemTextColor = Utility.ToDrawingColor(System.Windows.SystemColors.MenuTextColor);
}
break;
}
this.Renderer = new MenuRenderer(isMainMenu, primaryColor, menuItemTextColor, MouseOverColor, MouseOverBorderColor, SystrayTheme);
LoadMenuItemHeight();
}
}
}
Update:
Recently I was tried to override OnPaint() method. But the round corner is not smooth and also sub-menu is not rounded.
private GraphicsPath GetGraphicsPath(RectangleF rect,float radious)
{
GraphicsPath path = new GraphicsPath();
path.StartFigure();
path.AddArc(rect.X, rect.Y, radious, radious, 180, 90);
path.AddArc(rect.Width - radious, rect.Y, radious, radious, 270, 90);
path.AddArc(rect.Width - radious, rect.Height - radious, radious, radious, 0, 90);
path.AddArc(rect.X, rect.Height - radious, radious, radious, 90, 90);
path.CloseFigure();
return path;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
RectangleF rectSurface = new RectangleF(0, 0, this.Width, this.Height);
RectangleF rectBorder = new RectangleF(1, 1, this.Width - 0.8F, this.Height - 1);
if (borderRadius > 2) //Rounder
{
using (GraphicsPath pathSurface = GetGraphicsPath(rectSurface, borderRadius))
using (GraphicsPath pathBorder = GetGraphicsPath(rectBorder, borderRadius - 1F))
using (Pen penSurface = new Pen(this.BackColor, 2))
using (Pen penBorder = new Pen(borderColor, borderSize))
{
penBorder.Alignment = PenAlignment.Inset;
//Menu surface
this.Region = new Region(pathSurface);
//Draw surface border for HD result
e.Graphics.DrawPath(penSurface, pathSurface);
//Menu Border
if (borderSize >= 1)
e.Graphics.DrawPath(penBorder, pathBorder);
}
}
else //Normal contex menu
{
//Menu surface
this.Region = new Region(rectSurface);
//Menu border
if (borderSize >= 1)
{
using (Pen penBorder = new Pen(borderColor, borderSize))
{
penBorder.Alignment = PenAlignment.Inset;
e.Graphics.DrawRectangle(penBorder, 0, 0, this.Width - 1, this.Height - 1);
}
}
}
}
//Overrides
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if (this.DesignMode == false)
{
//this.Parent.BackColorChanged += Parent_BackColorChanged;
switch (SystrayTheme)
{
case WindowsTheme.Light:
{
menuItemTextColor = Color.Black;
}
break;
case WindowsTheme.Dark:
{
menuItemTextColor = Color.White;
}
break;
case WindowsTheme.HighContrast:
{
menuItemTextColor = Utility.ToDrawingColor(System.Windows.SystemColors.MenuTextColor);
}
break;
}
this.RenderMode = ToolStripRenderMode.Professional;
this.Renderer = new MenuRenderer(isMainMenu, primaryColor, menuItemTextColor, MouseOverColor, MouseOverBorderColor, SystrayTheme);
LoadMenuItemHeight();
}
}
private void Parent_BackColorChanged(object sender, EventArgs e)
{
if (this.DesignMode)
this.Invalidate();
}
Thanks in Advance. Any help will be appreciated.
You can find the solution on this page: https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/apply-rounded-corners
I also include the solution here just in case the external link gets deleted.
// The enum flag for DwmSetWindowAttribute's second parameter, which tells the function what attribute to set.
public enum DWMWINDOWATTRIBUTE
{
DWMWA_WINDOW_CORNER_PREFERENCE = 33
}
// The DWM_WINDOW_CORNER_PREFERENCE enum for DwmSetWindowAttribute's third parameter, which tells the function
// what value of the enum to set.
public enum DWM_WINDOW_CORNER_PREFERENCE
{
DWMWCP_DEFAULT = 0,
DWMWCP_DONOTROUND = 1,
DWMWCP_ROUND = 2,
DWMWCP_ROUNDSMALL = 3
}
// Import dwmapi.dll and define DwmSetWindowAttribute in C# corresponding to the native function.
[DllImport("dwmapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern long DwmSetWindowAttribute(IntPtr hwnd,
DWMWINDOWATTRIBUTE attribute,
ref DWM_WINDOW_CORNER_PREFERENCE pvAttribute,
uint cbAttribute);
And to apply the rounded corners to the ContextMenuStrip:
ContextMenuStrip notifyIconMenu = new ContextMenuStrip();
...
var attribute = DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE;
var preference = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND;
DwmSetWindowAttribute(notifyIconMenu.Handle, attribute, ref preference, sizeof(uint));
UPD
Here is the implementation of the submenu:
ToolStripMenuItem submenu = new ToolStripMenuItem("submenu");
notifyIconMenu.Items.Add(submenu);
ToolStripMenuItem submenuItem1 = new ToolStripMenuItem("submenu1");
submenu.DropDownItems.Add(submenuItem1);
DwmSetWindowAttribute(submenu.DropDown.Handle, attribute, ref preference, sizeof(uint));
In case you want to go the proprietary implementation route for rounded corners, here's a method for creating "smooth" ones:
Graphics graphics = ...;
...
graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
Did you try to set different RendererMode in CustomContextMenu other than the default one?
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if (this.DesignMode == false)
{
switch (SystrayTheme)
{
case WindowsTheme.Light:
{
menuItemTextColor = Color.Black;
}
break;
case WindowsTheme.Dark:
{
menuItemTextColor = Color.White;
}
break;
case WindowsTheme.HighContrast:
{
menuItemTextColor = Utility.ToDrawingColor(System.Windows.SystemColors.MenuTextColor);
}
break;
}
this.RenderMode = ToolStripRenderMode.Professional;
this.Renderer = new MenuRenderer(isMainMenu, primaryColor, menuItemTextColor, MouseOverColor, MouseOverBorderColor, SystrayTheme);
LoadMenuItemHeight();
}
}

C# Mixing up 2 different Custom Label Class

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

How can I add textbox control's behaviors into my custom control?

I have a custom rounded textbox. But I couldn't add the textbox behaviors like text editing, text selection etc. Those properties take much time if I decide make myself. How can I add this properties into my textbox?
My TextBox class:
public class AltoTextBox : Control
{
public AltoTextBox()
{
SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.SupportsTransparentBackColor |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.UserPaint, true);
BackColor = Color.Transparent;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
RoundedRectangleF strokeRect = new RoundedRectangleF(Width, Height, 10);
RoundedRectangleF innerRect = new RoundedRectangleF(Width - 0.5f, Height - 0.5f, 10f, 0.5f, 0.5f);
e.Graphics.DrawPath(Pens.Black, strokeRect.Path);
e.Graphics.FillPath(Brushes.White, innerRect.Path);
}
}
public class RoundedRectangleF
{
Point location;
float radius;
GraphicsPath grPath;
float x, y;
float width, height;
public RoundedRectangleF(float width, float height, float radius,float x = 0,float y = 0)
{
location = new Point(0, 0);
this.radius = radius;
RectangleF upperLeftRect = new RectangleF(x, y, 2 * radius, 2 * radius);
RectangleF upperRightRect = new RectangleF(width - 2 * radius - 1, x, 2 * radius, 2 * radius);
RectangleF lowerLeftRect = new RectangleF(x, height - 2 * radius - 1, 2 * radius, 2 * radius);
RectangleF lowerRightRect = new RectangleF(width - 2 * radius - 1, height - 2 * radius - 1, 2 * radius, 2 * radius);
grPath = new GraphicsPath();
grPath.AddArc(upperLeftRect, 180, 90);
grPath.AddArc(upperRightRect, 270, 90);
grPath.AddArc(lowerRightRect, 0, 90);
grPath.AddArc(lowerLeftRect, 90, 90);
grPath.CloseAllFigures();
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public RoundedRectangleF()
{
}
public GraphicsPath Path
{
get
{
return grPath;
}
}
public RectangleF Rect
{
get
{
return new RectangleF(x, y, width, height);
}
}
public float Radius
{
get
{
return radius;
}
set
{
radius = value;
}
}
}
I have found a solution from Hazeldev's custom controls.
In this solution we add a textbox control as our child control.
public class AltoTextBox : Control
{
int radius = 15;
public TextBox box = new TextBox();
GraphicsPath Shape;
public AltoTextBox()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.ResizeRedraw, true);
AddTextBox();
Controls.Add(box);
BackColor = Color.Transparent;
ForeColor = Color.DimGray;
Text = null;
Font = new Font("Comic Sans MS", 11);
Size = new Size(135, 33);
DoubleBuffered = true;
}
void AddTextBox()
{
box.Size = new Size(Width - 2*radius, Height - 6);
box.Location = new Point(radius, 3);
box.BorderStyle = BorderStyle.None;
box.TextAlign = HorizontalAlignment.Left;
box.Multiline = true;
box.Font = Font;
}
protected override void OnBackColorChanged(EventArgs e)
{
base.OnBackColorChanged(e);
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
box.Text = Text;
}
GraphicsPath innerRect;
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
box.Font = Font;
}
protected override void OnResize(System.EventArgs e)
{
base.OnResize(e);
Shape = new RoundedRectangleF(Width, Height, radius).Path;
innerRect = new RoundedRectangleF(Width - 0.5f, Height - 0.5f, radius, 0.5f, 0.5f).Path;
AddTextBox();
}
protected override void OnPaint(PaintEventArgs e)
{
Bitmap bmp = new Bitmap(Width, Height);
Graphics grp = Graphics.FromImage(bmp);
grp.SmoothingMode = SmoothingMode.HighQuality;
grp.DrawPath(Pens.Gray, Shape);
grp.FillPath(Brushes.White, innerRect);
e.Graphics.DrawImage((Image)bmp.Clone(), 0, 0);
base.OnPaint(e);
}
}

Categories