Custom Button - Issues with Focus Border, Text Color, etc.






4.86/5 (6 votes)
MyButton is a simple .NET class for WinForms that helps in setting an image, drawing a rectangle while on focus, and taking care of the text of the button.
Introduction
MyButton is a simple .NET class for WinForms that helps in setting an image, drawing a rectangle while on focus, and taking care of the text of the button.
Background
Recently I was working on a project where I had to use images for buttons and rounded textboxes. Images for a button seemed to be simple but
in my UpdateLayeredWindow
, the default button component wasn't enough for all aspects. So I was using a
third party library (Krypton Toolkit).
That made my work very simple and easy. At the time of final version of the project, I thought to reduce the package size and the only thing I could
eliminate was the third part library. I started developing a custom button and gradually got all the things working as was Krypton's button and my requirements.
I had to do RnD on different sites for different tasks to get it working. So I thought why not share the whole thing in one place and help others who
are kind of looking for the same or similar.
The solution
First we will need an image for the button background. You can pick up any image of any shape and/or size. The image I use is:
Add the above image to the project. Create a component class named MyButton
. Right click on your project, Add->Component, select Component Class,
and name it MyButton
. You should see the screen with the MyButton.cs file open as:
Click on "Click here to switch to code view", to switch to code view and actually start coding. Inherit your class
from Button
rather
than the Component
class. The initial code should look like this:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
namespace MyComponentsPrj.components
// Your namescape an be anything.
{
public partial class MyButton : Button
{
public MyButton()
{
InitializeComponent();
}
public MyButton(IContainer container)
{
container.Add(this);
InitializeComponent();
}
}
}
With the above code, we just have a newly developed component called MyButton
of type Button
. Yet, it doesn't have any special features.
To add special features, first go to the Design mode of the MyButton
class.
Button design settings
- Select
BackColor
asTransparent
- as we want the surrounding of the image to be transparent and not any different - Set the buttonBkgd.png added to the project as
BackgroundImage
- Set
Hand
asCursor
from the drop down - Select desired font
- Select desired forecolor
- Set
UseVisualStyleBackColor
toFalse
FlatAppearance
:BorderColor
should beNone
or not selected,BorderSize
as 0,MouseDownBackColor
andMouseOverBackColor
as Transparent - we don't want a border around the button under any circumstance- Select
FlatStyle
asFlat
Note: If you have a custom color set for any Color property like
ForeColor
, BorderColor
, etc., in
Design mode
then that won't be supportive. So it is better to define them in the constructor or
as a separate method and call the method in both constructors.
Like I call the below init();
in both the constructors:
private void init()
{
base.ForeColor = Color.FromArgb(0, 255, 254, 255);
base.FlatAppearance.BorderColor = Color.FromArgb(0, 255, 255, 254);
// Transparent border
}
With the above settings, the button will have a background, won't have any border around it, and all basic settings are set. If we don't add FlatAppearance.BorderColor
then if our button has focus and some other application is active, then our button will look like this:
Focus border
Now comes that solid inner border on the button when it has focus by navigating through the Tab key or otherwise. That bothers me. Well, I have two options: either don't support focus itself or have another sort of border. To remove the focus border, I override the following method in my class:
/// <summary>
/// Avoids the inner solid rectangle shown on focus
/// </summary>
protected override bool ShowFocusCues
{
get
{
return false;
}
}
With the above code added, the button won't show any indication when it has focus. Let's get the indication of focus by showing a dotted border instead of
a solid line.
For that, override onPaint()
and implement the below code:
protected override void OnPaint(PaintEventArgs pevent)
{
base.OnPaint(pevent);
// We want to draw dotted border only when the button has focus, not otherwise
if (base.ContainsFocus)
{
// Draw inner dotted rectangle when button is on focus
Pen pen = new Pen(Color.Gray, 10);
Point p = base.Location;
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
Rectangle rectangle = new Rectangle(4, 4, Size.Width - 8, Size.Height - 8);
ControlPaint.DrawFocusRectangle(pevent.Graphics, rectangle);
pen.Dispose();
}
}
Hmmm, looks cool. Now we can also manage the focus issue and get an excellent inner dotted border when on focus.
But, one more thing is bothering - when I click
the button, the text turns to a Gray shade. This is Windows style, has no default property to set/change it. I want the ForeColor
to be as it
is and not change to gray or any other color. Different PCs with different themes may also affect this. I want my button to behave the same on each and every PC.
Let's look at how to handle that issue.
Text color remains same on click
To achieve the goal, we will have to print the text manually and set the Text
property to "". Create a property displayText
which will be used
in place of the Text
property. Create a member variable textColor
of type Color
.
private Color textColor = Color.White;
/// <summary>
/// Text used to display the contents rather than using Text property.
/// </summary>
public string displayText
{ get; set; }
Add the following code at the bottom of onPaint()
:
// Draw the string to screen
// Find the size of the text
SizeF sf = pevent.Graphics.MeasureString(displayText, this.Font, this.Width);
Point ThePoint = new Point();
// Get the center location to print the text
ThePoint.X = (int)((this.Width / 2) - (sf.Width / 2));
ThePoint.Y = (int)((this.Height / 2) - (sf.Height / 2));
// Draw the text
//pevent.Graphics.DrawString(displayText, Font, new SolidBrush(textColor), ThePoint);
TextRenderer.DrawText(pevent.Graphics, displayText, Font, ThePoint, textColor);
this.Text = "";
Add ForeColorChanged
event and add the code:
/// <summary>
/// Whenever ForeColor is changed, the Alpha for the textColor
// will also be changed and thus updated.
/// </summary>
private void MyButton_ForeColorChanged(object sender, EventArgs e)
{
this.textColor = Color.FromArgb(255, ForeColor.R, ForeColor.G, ForeColor.B);
}
Wondering what is textColor
doing and why it is used? Remember, I had said earlier
that the custom colors are not completely supportive in setting any color.
See the difference between the base.ForeColor
and textColor
settings. In base.ForeColor
the
Opacity
is set to 0 whereas in textColor
,
the Opacity
is set to 255. If you use just ForeColor
to print text, then you might not see anything. Hence we need to change the opacity. With the above event,
whatever the ForeColor
is set to, our text will be printed properly without any doubt.
Why have I commented DrawString
and used DrawText
? With DrawString
, there was no spacing in between characters of the text.
Using DrawText
solved it. This may be due to the font and/or system. The Font
that I have used is "Arial MT Bold". With the same settings on
another system,
DrawString
had no issues whereas on my system I had. So you can use whichever suits you.
With this, the MyButton
button component is complete. Rebuild your project and it is ready for use from
the ToolBox. How to use it? Open a Form
,
drag the MyButton
from ToolBox. If required, change properties else just add
a text to display for the displayText
property. Save it and execute your project.
You can see the look and feel of the button remains the same when clicked or not. The dotted lined inner border
appears when it has focus. Get rid of all other default borders.
Results of button on different cases:
Points of iInterest
You can see how a single and easily developed component helped us override the default Windows style for the button and get the look and feel we wanted for our UI.
The files for the MyButton
class are included in the zip. Enjoy playing with it!