Contents
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.
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.
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 of Form
.
- Customize your form by adding icon holder buttons, menu icons, status bar items, etc.
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.
Rounded border
Inclined border
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
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.
-
Rectangle fill titlebar style
When using this style, the titlebar area is divided into two rectangles filled using start and end gradient colors.
-
Texture fill titlebar style
The titlebar is filled using a custom texture. It can be any valid image file.
-
Linear gradient titlebar style
The basic titlebar style filled using custom start / end gradient colors.
-
Glow style
The upper area of the titlebar is filled with a smooth glow.
-
None
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 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
When set to Pixeled
style, XCoolForm provides three different filling modes:
-
None
Only the button symbol is highlighted using the highlight color.
-
Upper glow
Upper glow is drawn when the button is hovered.
-
Full fill
This style involves three steps:
- First, the button is filled using a solid color.
- Next, the glow is rendered.
- Finally, the upper shine is drawn.
-
MacOS style
Titlebar buttons have a MacOS look and feel. When a button is hovered, the symbol and lower shine are drawn.
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
Title to identify the holder button.
-
Description
Short description about the holder button.
-
Panel
Container for a button caption, description, and image. You can specify transparency using the FrameAlpha
property.
-
Image
Background image for the panel.
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.
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. |
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)
{
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;
}
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);
}
}
g.DrawImage(
xbtn.ButtonImage,
rcIcon
);
xbtn.ButtonRectangle = rcIcon;
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:
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
}
g.ResetClip();
break;
}
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
As shown at image below, you can use standard Windows Forms Controls to build your user interface.
-
Vista theme
-
MacOS theme
-
Alien theme
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.
-
Adobe Media Player theme
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.