C# class for creating cool looking boxes using Windows Forms






4.67/5 (17 votes)
Windows Forms don't have to look boring!
Introduction
In Visual Studio, you can choose between the "old fashioned" Windows Forms and the better looking WPF. If you prefer to work with Windows Forms however, you can still make your application look very fancy. In this article, I will show a tool to create some graphical parts programmatically. It is limited to a box that can show text or a picture, but it might give you some inspiration to create your own graphical objects.
I think this code is not very difficult if you have ever used GDI+. To freshen up: if an Image is a painting, then a Bitmap is the canvas with the paint, and Graphics are your brushes (and the artist is you, of course).
The class GraphicTools
is able to create boxes like the one on the left in this picture:
Using the Code
Parameters include the image where you would like to draw the box, the text inside the box, the location and size of the box, the width of the border (here 16), and finally the four colors you want to use. (This function is overloaded with a very similar one that puts an image in the box.)
public static Image DrawBox(this Image image, string text, Point p, Size size, int border,
Color foreColor, Color backColor, Color fillColor, Color textColor)
{
// Draw the borders
image.CreateBorders(p, size, border, foreColor, backColor);
// Fill the inside and add the text
using (var g = Graphics.FromImage(image))
{
g.FillRectangle(new SolidBrush(fillColor),
new Rectangle(p.X + border, p.Y + border, size.Width
- (2 * border), size.Height - (2 * border)));
StringFormat stringFormat = new StringFormat();
// Horizontal alignment
stringFormat.Alignment = StringAlignment.Center;
// Vertical alignment
stringFormat.LineAlignment = StringAlignment.Center;
g.DrawString(text, new Font("Arial", 10, FontStyle.Bold),
new SolidBrush(textColor), p.X + (size.Width / 2),
p.Y + (size.Height / 2), stringFormat);
}
return image;
}
The most important part is the creation of the borders. The four corner pieces are created using a gradient filled circle (foreColor
in the middle,
backColor
on the outside).
private static Image CornerBox(int border, Color foreColor, Color backColor)
{
Image cornerImage = CreateImage(new Size(border * 2, border * 2), backColor);
using (var g = Graphics.FromImage(cornerImage))
{
var p = new GraphicsPath();
p.AddEllipse(0, 0, border * 2, border * 2);
var pgb = new PathGradientBrush(p);
pgb.SurroundColors = new Color[] { backColor };
pgb.CenterColor = foreColor;
g.FillRectangle(pgb, 0, 0, border * 2, border * 2);
}
return cornerImage;
}
Each quadrant of this circle forms one of the corners of the box, so in the CreateBorders
function, we can use:
private static Image CreateBorders(this Image image, Point p, Size size,
int border, Color foreColor, Color backColor)
{
// Draw the corners
Image cornerBox = CornerBox(border, foreColor, backColor);
image.Merge(p, cornerBox,
new Rectangle(0, 0, border, border)); // Left Top
image.Merge(new Point(p.X + size.Width - border, p.Y), cornerBox,
new Rectangle(border, 0, border, border)); // Right Top
image.Merge(new Point(p.X, p.Y + size.Height - border), cornerBox,
new Rectangle(0, border, border, border)); // Left Bottom
image.Merge(new Point(p.X + size.Width - border,
p.Y + size.Height - border), cornerBox,
new Rectangle(border, border, border, border)); // Right Bottom
The box also contains four rectangular pieces, so this function continues:
// Draw the horizontal bars
if (size.Width > (2 * border))
{
// Else the width of the horizontal bars is equal to or less than zero
var rectangle = new Rectangle(0, 0, size.Width - (2 * border), border);
using (var img = CreateImage(
new Size(rectangle.Width, rectangle.Height), backColor))
{
using (var g = Graphics.FromImage(img))
{
// Top
g.FillRegion(new LinearGradientBrush(rectangle,
backColor, foreColor, 90f), new Region(rectangle));
image.Merge(new Point(p.X + border, p.Y), img, rectangle);
// Bottom
g.FillRegion(new LinearGradientBrush(rectangle,
foreColor, backColor, 90f), new Region(rectangle));
image.Merge(new Point(p.X + border, p.Y + size.Height - border)
, img, rectangle);
}
}
}
// Draw the vertical bars
// Very similar...
The way the different parts of the box are created might seem a little bit strange: instead of immediately drawing on the image of the box, smaller images are created, drawn upon,
and then merged with the image of the box. This is because of the behavior of the LinearGradientBrush
: it does not start with the first color on the place where you start drawing,
but at the beginning of the image on which you draw. This seemed the most practical solution, but I admit it might not be the most elegant one.
There are two rather technical functions used: CreateImage
, which creates an image of a certain size by creating a bitmap of that size (as Image
is in fact an abstract class), and Merge
, which puts the smaller image parts on the box. You can find them in the download of course.
Can the result indeed be as fancy as the title of this article predicts? Well, let's make a little application that creates a visual representation of a spider graph. I made a class of animal (how original), with the name of the animal and a picture as fields. Let's fill up a list of animals:
private void FillAnimalList()
{
string fileLoc = Application.StartupPath;
Animals.Add(new Animal("Tiger",
Image.FromFile(fileLoc + "\\tiger.bmp")));
Animals.Add(new Animal("Black panther",
Image.FromFile(fileLoc + "\\black panther.bmp")));
Animals.Add(new Animal("Lion",
Image.FromFile(fileLoc + "\\lion.bmp")));
Animals.Add(new Animal("Leopard",
Image.FromFile(fileLoc + "\\leopard.bmp")));
Animals.Add(new Animal("Bobcat",
Image.FromFile(fileLoc + "\\bobcat.bmp")));
Animals.Add(new Animal("Jaguar",
Image.FromFile(fileLoc + "\\jaguar.bmp")));
}
"Tiger" is my main actor here, the others are his friends. I want each animal to be represented by a box containing its picture and a smaller box with its name.
private static Image DrawAnimal(Image imageToDrawOn, Image imageOfAnimal,
string name, Point location, Color color)
{
// Box with picture
imageToDrawOn.DrawBox(imageOfAnimal,
new Point(location.X-80, location.Y-96),
new Size(160, 160), 16, color, Color.Black);
// Box with name
imageToDrawOn.DrawBox(name,
new Point(location.X-80, location.Y + 64),
new Size(160, 32), 16, color, Color.Black, color, Color.Black);
return imageToDrawOn;
}
"Tiger" has to be in the middle, with a connection to each friend. We draw this on a PictureBox
:
private void frmMain_Load(object sender, EventArgs e)
{
FillAnimalList();
// Fill the picturebox with a solid color
pictureBox.Image = GraphicTools.CreateImage(pictureBox.Size, Color.Black);
int mX = pictureBox.Width/2;
int mY = pictureBox.Height/2;
List<Point> pointList = new List<Point>();
// This is the location where tiger is going to be
pointList.Add(new Point(mX, mY));
int distance = pictureBox.Height / 3;
double cornerBetweenAnimals = (2 * Math.PI) / (Animals.Count - 1);
// Draw connections between the animals
using(var g = Graphics.FromImage(pictureBox.Image))
{
g.SmoothingMode = SmoothingMode.AntiAlias;
for (int i = 0; i < 5; i++)
{
// Location of the other animals
Point p = new Point(
(int)(mX - (distance * Math.Cos((Math.PI/4)
+ (cornerBetweenAnimals * i)))),
(int)(mY - (distance * Math.Sin((Math.PI/4)
+ (cornerBetweenAnimals * i)))));
pointList.Add(p);
var pen = new Pen(new LinearGradientBrush(
pointList[0],p, Color.Blue, Color.Black));
pen.Width = 10;
// Draw a connection from tiger to a friend
g.DrawLine(pen, pointList[0], p);
}
}
// Draw the animals
for (int i = 0; i < 6; i++)
{
pictureBox.Image = DrawAnimal(pictureBox.Image,
Animals[i].image, Animals[i].name,
pointList[i], Color.Blue);
}
}
And that results in this beautiful friendship:
Points of Interest
I did learn something about big cats while making the example.
History
First version.