用于创建酷炫 Windows 窗体的 C# 类






4.67/5 (17投票s)
Windows 窗体不必看起来很无聊!
引言
在 Visual Studio 中,您可以选择“老式”Windows 窗体或外观更好的 WPF。如果您喜欢使用 Windows 窗体,您仍然可以使您的应用程序看起来非常漂亮。在本文中,我将展示一个以编程方式创建一些图形部分的工具。它仅限于可以显示文本或图片的框,但它可能会给您一些创建自己的图形对象的灵感。
我认为如果您曾经使用过 GDI+,这段代码并不难。复习一下:如果 Image 是一幅画,那么 Bitmap 就是带有颜料的画布,而 Graphics 是你的画笔(当然,艺术家就是你)。
GraphicTools
类能够创建如下图左侧的盒子
Using the Code
参数包括您想要绘制盒子的图像、盒子内的文本、盒子的位置和大小、边框的宽度(此处为 16),以及您想要使用的四种颜色。(此函数重载了一个非常相似的函数,该函数将图像放入框中。)
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;
}
最重要的部分是边框的创建。四个角件是使用渐变填充圆创建的(中间为 foreColor
,外部为 backColor
)。
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;
}
这个圆的每个象限形成盒子的一个角,所以在 CreateBorders
函数中,我们可以使用
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
该框还包含四个矩形部分,因此此函数继续
// 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...
盒子的不同部分被创建的方式可能看起来有点奇怪:不是立即绘制在盒子的图像上,而是创建更小的图像,在其上绘制,然后与盒子的图像合并。这是因为 LinearGradientBrush
的行为:它不是从您开始绘制的地方开始使用第一种颜色,而是在您绘制的图像的开头。这似乎是最实用的解决方案,但我承认它可能不是最优雅的解决方案。
使用了两个相当技术性的函数:CreateImage
,它通过创建该大小的位图来创建特定大小的图像(因为 Image
实际上是一个抽象类),以及 Merge
,它将较小的图像部分放在框上。您当然可以在下载中找到它们。
结果真的能像本文标题预测的那样精美吗?好吧,让我们创建一个小应用程序,它创建一个蜘蛛图的可视化表示。我创建了一个动物类(多么原创),其中包含动物的名称和图片作为字段。让我们填写一个动物列表
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")));
}
“老虎”是我的主要演员,其他的是他的朋友。我希望每只动物都用一个包含其图片的盒子和一个包含其名称的较小盒子来表示。
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;
}
“老虎”必须在中间,与每个朋友都有联系。我们将其绘制在 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);
}
}
结果就产生了这种美好的友谊
关注点
在制作示例时,我确实学到了一些关于大型猫科动物的知识。
历史
First version.