Fancy Windows Forms






4.93/5 (126 votes)
Bring a fresh / cool look and feel to your applications.
Contents
Introduction
Are you tired of the standard Windows Forms, and want to add some cool interfaces to your application? Then, keep on reading this article, and you will find out how easily it can be done. XCoolForm is a library which allows you to paint fancy titlebars, borders, titlebar buttons, status bars, etc. It comes with a few pre-built styles, and you can easily make your own styles. It also comes with a huge number of properties, so it can fit the needs of every user. The icons I have used are free; I downloaded them from http://www.iconarchive.com, and I got the Visitor font from http://www.dafont.com.
Background
The idea for this project came a year ago, when I already had enough experience using GDI+, so I started to implement it. My next goal is to re-implement this project in WPF, as long as it offers some really good stuff and most work can be done using XAML.
Using the code
To start using the XCoolForm library, you should follow these three simple steps:
- Add a reference to the XCoolForm library.
- Make your forms inherit from
XCoolForm
instead ofForm
. - Customize your form by adding icon holder buttons, menu icons, status bar items, etc.
Structure
Elements
Border
For a 3D border to be correctly rendered, you need to provide six colors (two for the outer border, and four for the inner border). Then, the drawing routine will iterate through the arrays of colors and build the border. If you want a simple, flat border, then you only need to provide one color.
XCoolForm also supports rounded and inclined borders (see pictures below). You can specify the roundness or inclination using the Radius
and Inclination
properties.
Titlebar
One of the first elements that attracts the user is the titlebar. So, it's important to provide stylish features for the titlebar. XCoolForm offers a whole set of properties, allowing the user to configure the finest details of the titlebar. You can choose between six different styles, and a few titlebar types to control the shape of the titlebar, set the background image, caption, etc. In the following section, the most important titlebar styles and types are explained.
Titlebar styles
XCoolForm supports six titlebar styles used in the rendering process. I'll add new styles soon.
-
Advanced titlebar style
-
Rectangle fill titlebar style
-
Texture fill titlebar style
-
Linear gradient titlebar style
-
Glow style
-
None
The titlebar is rendered using a gradient mix and shine. You need to specify five colors from which the titlebar is drawn. This can be done by assigning the list of colors to the TitleBarMixColors
property. Note that if the number of colors is not equal to five, an exception will be thrown.
When using this style, the titlebar area is divided into two rectangles filled using start and end gradient colors.
The titlebar is filled using a custom texture. It can be any valid image file.
The basic titlebar style filled using custom start / end gradient colors.
The upper area of the titlebar is filled with a smooth glow.
When set to this style, only the inner and outer borders are rendered.
Titlebar types
Note, when the TitleBarType
property is set to the desired type, the icon area and the titlebar button box are automatically adapted according to the selected type.
-
Rectangular
-
Rounded
-
Angular
Titlebar buttons
Titlebar buttons are drawn inside a button box, which is automatically dimensioned according to the button's width and height. XCoolForm supports three styles for titlebar buttons when those are being hovered. Symbols for close, maximize, and minimize buttons are drawn using some pixel art. For the next release, I'm planning to add support for custom bitmaps which can then be used as button symbols.
-
Pixeled style
-
None
-
Upper glow
-
Full fill
- First, the button is filled using a solid color.
- Next, the glow is rendered.
- Finally, the upper shine is drawn.
-
MacOS style
When set to Pixeled
style, XCoolForm provides three different filling modes:
Only the button symbol is highlighted using the highlight color.
Upper glow is drawn when the button is hovered.
This style involves three steps:
Titlebar buttons have a MacOS look and feel. When a button is hovered, the symbol and lower shine are drawn.
Icon holder
Icon holder is a kind of quick access toolbar where you can place shortcuts or frequently used actions in your program. As shown on the image, it is formed of an array of icons. When the mouse is over the icon holder button, the frame is shown including the image, description, and caption.
-
Caption
-
Description
-
Panel
-
Image
Title to identify the holder button.
Short description about the holder button.
Container for a button caption, description, and image. You can specify transparency using the FrameAlpha
property.
Background image for the panel.
Status bar
As shown on the image below, the statusbar is built from different elements. The background of the statusbar is filled using simple gradients, using the StatusStartColor
and StatusEndColor
properties. To give a glossy effect for the statusbar, the upper area is filled with a glow. Then, the status bar items are rendered and bound by separators. You can also provide a background image. The last element to comment is the size grip. It's drawn overlapping several rectangles to give it a 3D look.
Implementation
UML diagram
Classes
The table below contains a brief description about the classes found in the XCoolForm library. Some classes also have internal classes, but I didn't put them here. For details, take a look at the library source code.
Class name | Description |
X3DBorderPrimitive |
Provides drawing routes for building a 3D border. Flat border functionality is also implemented here. |
XTitleBarButton |
Represents the titlebar button. The drawing process, which includes full fill, glow, etc., is implemented here. |
XTitleBar |
It implements methods for border drawing, titlebar filling, background image drawing, alignment, etc. |
XTitleBarIconHolder |
It also contains a XHolderButton class to correctly draw the gradient panel when the mouse is hovered. |
XStatusBar |
Methods for drawing items, background image, size grip, statusbar filling, and glow drawing. |
XCoolFormHelper |
Some utility methods for drawing rounded rectangles and building color blends. |
XAntiAlias |
Used for smooth drawing. It implements the IDisposable interface. |
XmlThemeLoader |
Utility class for loading themes from XML files. |
Code snippets
FillTitleBar
is the main method for the titlebar filling. Basically, it uses the LinearGradientBrush
and PathGradientBrush
classes to perform the painting logic. First, we must figure out which titlebar type is selected by calling BuildTitleBarShape
, which will return the GrapichsPath
object describing the shape.
private void FillTitleBar(
Graphics g,
Rectangle rcTitleBar
)
{
GraphicsPath XTitleBarPath = new GraphicsPath();
XTitleBarPath = BuildTitleBarShape(rcTitleBar);
using (XAntiAlias xaa = new XAntiAlias(g))
{
#region Fill titlebar
switch (m_eTitleBarFill)
{
case XTitleBarFill.AdvancedRendering:
using (LinearGradientBrush lgb = new LinearGradientBrush(
rcTitleBar,
m_TitleBarMix[0],
m_TitleBarMix[4],
LinearGradientMode.Vertical))
{
lgb.InterpolationColors = XCoolFormHelper.ColorMix(
m_TitleBarMix,
true
);
g.FillPath(
lgb,
XTitleBarPath
);
}
#region Draw titlebar glow
using (GraphicsPath XGlow = new GraphicsPath())
{
XGlow.AddEllipse(
rcTitleBar.Left,
rcTitleBar.Bottom / 2 + 4,
rcTitleBar.Width,
rcTitleBar.Height
);
using (PathGradientBrush pgb = new PathGradientBrush(XGlow))
{
pgb.CenterColor = Color.White;
pgb.SurroundColors =
new Color[] { Color.FromArgb(0, 229, 121, 13) };
g.SetClip(XTitleBarPath);
g.FillPath(
pgb,
XGlow
);
g.ResetClip();
}
}
#endregion
break;
case XTitleBarFill.Texture:
if (m_TitleBarTexture != null) {
using (TextureBrush tb = new TextureBrush(m_TitleBarTexture))
{
g.FillPath(
tb,
XTitleBarPath
);
}
}
break;
case XTitleBarFill.LinearRendering:
RectangleF rcLinearFill = XTitleBarPath.GetBounds();
g.SetClip(XTitleBarPath);
using (LinearGradientBrush lgbLinearFill = new LinearGradientBrush(
rcLinearFill,
m_clrFillStart,
m_clrFillEnd,
LinearGradientMode.Vertical
))
{
g.FillRectangle(
lgbLinearFill,
rcLinearFill
);
}
g.ResetClip();
break;
case XTitleBarFill.UpperGlow:
RectangleF rcGlow = XTitleBarPath.GetBounds();
g.SetClip(XTitleBarPath);
rcGlow.Height /= 2;
using (LinearGradientBrush lgbUpperGlow = new LinearGradientBrush(
rcGlow,
m_clrUpperFillStart,
m_clrUpperFillEnd,
LinearGradientMode.Vertical
))
{
g.FillRectangle(
lgbUpperGlow,
rcGlow
);
}
g.ResetClip();
break;
case XTitleBarFill.RectangleRendering:
RectangleF rcDownRect = XTitleBarPath.GetBounds();
RectangleF rcUpRect = XTitleBarPath.GetBounds();
g.SetClip(XTitleBarPath);
rcUpRect.Height /= 2;
using (LinearGradientBrush lgbUpperRect = new LinearGradientBrush(
rcUpRect,
m_clrUpperFillStart,
m_clrUpperFillEnd,
LinearGradientMode.Vertical
))
{
lgbUpperRect.WrapMode = WrapMode.TileFlipY;
g.FillRectangle(
lgbUpperRect,
rcUpRect
);
}
rcDownRect.Height = rcDownRect.Height / 2;
rcDownRect.Y = rcUpRect.Bottom;
using (LinearGradientBrush lgbDwnRect = new LinearGradientBrush(
rcDownRect,
m_clrFillStart,
m_clrFillEnd,
LinearGradientMode.Vertical
))
{
g.FillRectangle(
lgbDwnRect,
rcDownRect
);
}
g.ResetClip();
break;
}
#endregion
The method called from FillTitleBar
returns the GraphicsPath
object built using arcs and lines.
private GraphicsPath BuildTitleBarShape(
Rectangle rc
)
{
GraphicsPath e = new GraphicsPath();
switch (m_eTitleBarType)
{
case XTitleBarType.Rounded:
e.AddArc(
rc.Left,
rc.Top,
rc.Height,
rc.Height,
90,
180);
e.AddLine(
rc.Left + rc.Height / 2,
rc.Top,
rc.Right,
rc.Top
);
e.AddArc(
rc.Right,
rc.Top,
rc.Height,
rc.Height,
-90,
180);
e.AddLine(
rc.Right,
rc.Bottom,
rc.Left + rc.Height / 2,
rc.Bottom);
break;
case XTitleBarType.Angular:
e.AddLine(
rc.Left,
rc.Bottom,
rc.Left + 20,
rc.Top);
e.AddLine(
rc.Left + 20,
rc.Top,
rc.Right,
rc.Top);
e.AddLine(
rc.Right,
rc.Top,
rc.Right - 20,
rc.Bottom
);
e.AddLine(
rc.Right - 20,
rc.Bottom,
rc.Left,
rc.Bottom
);
break;
case XTitleBarType.Rectangular:
e.AddRectangle(rc);
break;
}
return e;
}
RenderHolderButtons
will iterate through the collection of XHolderButton
s, and when the button is hovered, the BuildHolderButtonFrame
method will be called to draw the panel. This method also calculates the position of the caption and description literals, and draws them.
public void RenderHolderButtons(
int x,
int y,
Graphics g
)
{
int lX = x;
Rectangle rcIcon = new Rectangle();
RectangleF rcImage = new RectangleF();
RectangleF rcFrame = new RectangleF();
foreach (XHolderButton xbtn in m_xhBtn)
{
if (xbtn.ButtonImage != null)
{
xbtn.Left = lX;
xbtn.Top = y + 1;
rcIcon = new Rectangle(
lX,
y + 1,
xbtn.ButtonImage.Size.Width,
xbtn.ButtonImage.Size.Height
);
if (xbtn.Hot)
{
using (XAntiAlias xaa = new XAntiAlias(g))
{
using (GraphicsPath XHolderBtnPath =
BuildHolderButtonFrame(rcIcon, 100, 40))
{
using (LinearGradientBrush lgb = new LinearGradientBrush(
XHolderBtnPath.GetBounds(),
Color.FromArgb(xbtn.FrameAlpha, xbtn.FrameStartColor),
Color.FromArgb(xbtn.FrameAlpha, xbtn.FrameEndColor),
LinearGradientMode.Vertical
))
{
g.FillPath(
lgb,
XHolderBtnPath
);
}
rcFrame = XHolderBtnPath.GetBounds();
}
int lFrameImageWidth = 0;
if (xbtn.FrameBackImage != null)
{
// draw frame image:
rcImage = new RectangleF(
rcFrame.Right - xbtn.FrameBackImage.Width,
rcFrame.Bottom - xbtn.FrameBackImage.Height,
xbtn.FrameBackImage.Width,
xbtn.FrameBackImage.Height
);
g.DrawImage(xbtn.FrameBackImage, rcImage);
lFrameImageWidth = xbtn.FrameBackImage.Height;
}
// draw caption / description:
g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
g.DrawString(
xbtn.XHolderButtonCaption,
xbtn.XHolderButtonCaptionFont,
new SolidBrush(xbtn.XHolderButtonCaptionColor),
rcFrame.Left + 2,
rcIcon.Bottom + 4
);
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Near;
sf.LineAlignment = StringAlignment.Near;
sf.Trimming = StringTrimming.EllipsisCharacter;
sf.FormatFlags = StringFormatFlags.LineLimit;
float fCaptionWidth = g.MeasureString(xbtn.XHolderButtonCaption,
xbtn.XHolderButtonCaptionFont).Height;
RectangleF rcDescription = new RectangleF(
rcFrame.Left + 2,
rcIcon.Bottom + fCaptionWidth,
rcFrame.Width,
rcFrame.Height - lFrameImageWidth);
g.DrawString(
xbtn.XHolderButtonDescription,
xbtn.XHolderButtonDescriptionFont,
new SolidBrush(xbtn.XHolderButtonDescriptionColor),
rcDescription,
sf);
}
}
// draw button:
g.DrawImage(
xbtn.ButtonImage,
rcIcon
);
xbtn.ButtonRectangle = rcIcon;
// update position:
lX += rcIcon.Width + 2;
}
}
}
To find the hovering button, the HitTestHolderButton
method will return the index of the button.
public int HitTestHolderButton(
int x,
int y,
Rectangle rcHolder
)
{
int lBtnIndex = -1;
if (x >= rcHolder.Left && x <= rcHolder.Right)
{
XHolderButton btn = null;
for (int i = 0; i < m_xhBtn.Count; i++)
{
btn = m_xhBtn[i];
if (y >= 4 && y <= btn.ButtonRectangle.Bottom)
{
if (x >= btn.Left)
{
if (x < btn.Left + btn.ButtonRectangle.Width)
{
lBtnIndex = i;
break;
}
}
}
}
}
return lBtnIndex;
}
The FillButton
method paints the buttons inside the button box area. In full fill mode, it's needed to clip the drawing inside the bounding of a button. LinearGradientBrush
and PathGradientBrush
are also used to perform the drawing process.
private void FillButton(
Rectangle rcBtn,
Graphics g,
Color clrStart,
Color clrEnd,
GraphicsPath XBoxClip
)
{
switch (m_eFillMode)
{
case XButtonFillMode.UpperGlow:
rcBtn.Height = 3;
using (LinearGradientBrush lgb = new LinearGradientBrush(
rcBtn,
clrStart,
clrEnd,
LinearGradientMode.Vertical))
{
g.FillRectangle(
lgb,
rcBtn
);
}
break;
case XButtonFillMode.FullFill:
// restrict drawing inside button box / rectangle:
g.SetClip(XBoxClip);
g.SetClip(rcBtn, CombineMode.Intersect);
#region Fill button
using (SolidBrush sb = new SolidBrush(clrStart))
{
g.FillRectangle(
sb,
rcBtn
);
}
#endregion
using (XAntiAlias xaa = new XAntiAlias(g))
{
#region Fill shine
using (GraphicsPath XBtnGlow = new GraphicsPath())
{
XBtnGlow.AddEllipse(rcBtn.Left - 5, rcBtn.Bottom -
rcBtn.Height / 2 + 3, rcBtn.Width + 11, rcBtn.Height + 11);
using (PathGradientBrush pgb = new PathGradientBrush(XBtnGlow))
{
pgb.CenterColor = Color.FromArgb(235, Color.White);
pgb.SurroundColors = new Color[] { Color.FromArgb(0, clrEnd) };
pgb.SetSigmaBellShape(0.8f);
g.FillPath(
pgb,
XBtnGlow
);
}
}
#endregion
#region Fill upper glow
rcBtn.Height = rcBtn.Height / 2 - 2;
using (LinearGradientBrush lgb = new LinearGradientBrush(
rcBtn,
Color.FromArgb(80, Color.White),
Color.FromArgb(140, Color.White),
LinearGradientMode.Vertical))
{
using (GraphicsPath XGlowPath =
XCoolFormHelper.RoundRect((RectangleF)rcBtn, 0, 0, 4, 4))
{
lgb.WrapMode = WrapMode.TileFlipXY;
g.FillPath(
lgb,
XGlowPath
);
}
}
#endregion
}
// reset clipping back:
g.ResetClip();
break;
}
Themes
Themes are loaded from XML configuration files using the XmlThemeLoader
class. First, set the target form using the TargetForm
property. Then, you can apply your theme by calling the ApplyTheme
method. XCoolForm comes with five themes, and I hope to make much more. Also, I would be glad to see themes made by other users.
-
Gray theme
-
Blue winter theme
-
Dark system theme
-
Animal kingdom theme
-
Valentine theme
-
White theme
-
Black theme
-
Standard Windows theme
-
Vista theme
-
MacOS theme
-
Alien theme
-
Adobe Media Player theme
As shown at image below, you can use standard Windows Forms Controls to build your user interface.
Time ago, I built an utility for process monitoring, so I used XCoolForm and some user controls to make custom interface as shown at image.
History
- 26th February, 2009: Initial version.
- 14th March, 2009
- New themes added (Vista, Standard Windows, Adobe Media Player, Alien, Black, White, Mac OS).
- Added border types: rounded and inclined.
- Added Mac titlebar button style.
- Added new button box fill style.
- Fixed various issues.
- Coming soon: Office 2007 Ribbon titlebar / buttons style + more cool features / themes.