65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 C# 进行 Windows 应用程序图表制作。

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.69/5 (16投票s)

2009年4月7日

GPL3

4分钟阅读

viewsIcon

113425

downloadIcon

5519

使用 Windows 应用程序和 C# 中的 .NET Framework 内置函数创建图表

引言

绘制图表或图形是许多项目的常见需求。如果您是一名软件专业人士,您一定遇到过绘制图表的情况。大多数时候,我们倾向于依赖昂贵的第三方控件。

背景

我将演示 .NET 的内置函数来绘制一些基本图表。到文章结尾,您应该可以在您的 Windows 应用程序中绘制基本图表。我这里使用 C#。

Using the Code

我创建了一个 Windows 应用程序,并编写了以下四个函数来处理四种不同类型的图表。

Charts

  1. 绘制切片图 
  2. 绘制饼图
  3. 绘制柱状图
  4. 绘制折线图

在我们的示例中,我们将输入权重为 (13, 23, 33, 15, 20, 10, 4, 11)。我们将此数组传递给上述所有函数。让我们一一讨论所有函数。

绘制切片图

可以将此看作是根据权重将蛋糕切成片。在此图表中,切片的角度与权重呈线性比例。为了理解这一点,我们将讨论 .NET 支持的 `FillPie` 函数。

`FillPie` 函数的签名如下:

Void FillPie(Brush brush, int x,int y, int width,int height,
				int startAngle, int sweepAngle)

如果扫描角度为 360 度,则此函数用于填充椭圆。为了使其成为一个圆形,我们将高度和宽度都设置为圆的直径。

height = width = 2*radius;

让我们来看一下参数的细节。

  • Brush brush:这是一个用于填充饼图的 `brush` 对象。

  • Int x:定义饼图扇形所在的椭圆的边界矩形的左上角的 `x` 坐标。因此,如果我们的圆心 `x` 坐标为 `x0`,半径为 `r`

    x = x0 – r;
  • Int y:与 `x` 类似。

    y = y0 – r;
  • Int width:定义椭圆的边界矩形的 `width`,从该椭圆中获取饼图扇形。

    width = 2*radius;
  • Int startAngle:从 `x` 轴到饼图扇形第一边的角度(顺时针测量)。因此,我们从 0 度开始,并将其与 `sweepAngle` 递增,以便下一个切片紧接着前一个切片结束。

  • Int sweepAngle:从 `startAngle` 参数到饼图扇形第二边的角度(顺时针测量)。因此,这是基于权重的角度跨度。所以 360 度根据权重按比例分为 8 部分。

    对于我们示例中的权重 `23`:

    Int totalWeight = 13+23+33+15+20+10+4+11;
    	sweepAngle = (23 * 360)/totalWeight;

然后我使用另一个函数来绘制形状周围的边框:`DrawPie`。它与 `FillPie` 唯一的区别是它接受 `Pen` 对象而不是 `Brush`。

private void DrawSliceChart(PaintEventArgs e, int[] alWeight)
{
    int numberOfSections = alWeight.Length
    int x0 = 100;
    int y0 = 100;
    int radius = 100;
    int startAngle = 0;
    int sweepAngle = 0;
    int[] height = new int[numberOfSections];
    int total = SumOfArray(alWeight);
    Random rnd = new Random();
    SolidBrush brush = new SolidBrush(Color.Aquamarine);
    Pen pen = new Pen(Color.Black);
    for (int i = 0; i < numberOfSections; i++)
    {
        brush.Color = Color.FromArgb(rnd.Next(200, 255),
			rnd.Next(255), rnd.Next(255), rnd.Next(255));
        // Since we have taken integer so last slice needs to
        // be rounded off to fit into the remaining part.
        if (i == numberOfSections - 1)
            sweepAngle = 360 - startAngle;
        else
            sweepAngle = (360 * alWeight[i]) / total;
        e.Graphics.FillPie(brush, x0 - height[i], y0 - height[i],
			2*radius, 2*radius, startAngle , sweepAngle);
        e.Graphics.DrawPie(pen, x0 - height[i], y0 - height[i],
			2*radius, 2*radius, startAngle, sweepAngle);
        startAngle += sweepAngle;
        brush.Color = Color.FromKnownColor(KnownColor.Black);
    }
} 

绘制饼图

这是另一个图表,它也使用相同的内置函数,只需对之前的代码稍作修改。在此图表中,所有切片的角度相同,但半径根据权重而变化。

因此,我们首先找出最大权重 `MaxWeight`。然后 `MaxWeight` 将等于半径,而其他切片的半径将小于 `MaxWeight` 的比例。

例如,对于权重 `23`:

MaxWeight = 33;
	width = (23*radius)/MaxWeight;
	sweepAngle = 360/TotalNumberOfWeights; 

`startAngle` 将从 0 开始,并由 `sweepAngle` 递增。

startAngle += sweepAngle; 
private void DrawPieChart(PaintEventArgs e, int[] alWeight)
        {
            int numberOfSections = alWeight.Length;
            int x0 = 600;
            int y0 = 500;
            int radius = 200;
            int startAngle = 0;
            int sweepAngle = 360/numberOfSections;
            int[] height = new int[numberOfSections];
            int maxWeight = MaxValue(alWeight);
            Random rnd = new Random(10);
            SolidBrush brush = new SolidBrush(Color.Aquamarine);
            Pen pen = new Pen(Color.Black);
            for(int i =0;i<numberofsections;i++){
                height[i] = ((Convert.ToInt32(alWeight[i])) * radius) / maxWeight;
                brush.Color = Color.FromArgb(rnd.Next(200, 255), rnd.Next(255),
                    rnd.Next(255), rnd.Next(255));
                e.Graphics.FillPie
		  (brush, x0 - height[i], y0 - height[i], 2 * height[i],
                    2 * height[i],(startAngle+i*45), sweepAngle);
                e.Graphics.DrawPie(pen, x0 - height[i], y0 - height[i], 2 * height[i],
                    2 * height[i], (startAngle + i * 45), sweepAngle);
                //e.Graphics.FillPie(Brushes.Blue, x0 - height[i], y0 - height[i],
                //2 * height[i], 2 * height[i], (startAngle + i * 45 -2), 2);
            }
        }

绘制柱状图

在此,我们使用了内置函数 '`DrawRectangle`'。这里的想法是首先创建一个定义柱状图边界的大矩形。`DrawRectangle` 函数接受以下参数:

  1. `Pen` – 定义边框的颜色和样式
  2. `Rectangle` – 要创建的 `Rectangle` 对象

我们将仔细研究 `rectangle` 对象。

Rectangle(int x, int y, int width,int height) 

`X` 和 `y` 是矩形左上角的坐标。

`Width` 和 `height` 是矩形的 `width` 和 `height`。

所以,要绘制一个条形,我们将使用相同的函数。唯一需要确定的是 `Rectangle` 的这四个参数。`Width` 是恒定的,等于外部矩形总长度除以条形总数。`Height` 计算如下:

Height = (weight of current array element *
		height of outer rectangle )/ maximum weight. 

每次创建新条形时,`X` 坐标都会按条形宽度递增。

`Y` 坐标通过以下公式计算:

y = y coordinate of outer rectangle + height of outer rectangle –
			height of bar calculated using above formula 

像 `DrawPie` 和 `DrawSlice` 一样,我们使用相应的填充函数填充形状。

private void DrawBarChart(PaintEventArgs e, int[] alWeight)
        {
            int numberOfSections = alWeight.Length;
            int lengthArea = 400;
            int heightArea = 250;
            int topX = 400;
            int topY = 20;
            int maxWeight = MaxValue(alWeight);
            int[] height = new int[numberOfSections];
            int total = SumOfArray(alWeight);
            Random rnd = new Random();
            SolidBrush brush = new SolidBrush(Color.Aquamarine);
            Pen pen = new Pen(Color.Gray);
            Rectangle rec = new Rectangle(topX,topY,lengthArea,heightArea);
            e.Graphics.DrawRectangle(pen,rec);
            pen.Color = Color.Black;
            int smallX = topX;
            int smallY = 0;
            int smallLength = (lengthArea/alWeight.Length);
            int smallHeight = 0;
            for (int i = 0; i < numberOfSections; i++)
            {
                 brush.Color = Color.FromArgb(rnd.Next(200, 255),
			rnd.Next(255), rnd.Next(255), rnd.Next(255));
                 smallHeight = ((alWeight[i] * heightArea )/ maxWeight);
                 smallY = topY + heightArea - smallHeight;
                 Rectangle rectangle = new Rectangle
			(smallX, smallY, smallLength, smallHeight);
                 e.Graphics.DrawRectangle(pen,rectangle);
                 e.Graphics.FillRectangle(brush, rectangle);
                 brush.Color = Color.FromKnownColor(KnownColor.Black);
                 e.Graphics.DrawRectangle(pen, rectangle);
                 smallX = smallX + smallLength;
             }
        }

绘制折线图

要绘制折线图,我们首先使用内置函数 '`DrawRectangle`' 创建边界。然后我们确定绘制直线的点。

`X` 坐标按相等距离递增,`y` 坐标基于权重设置。

在这里,为了突出显示每个点,我使用了 `DrawDots` 函数,通过创建一个半径为 5 的圆来突出显示该点。

private void DrawLineChart(PaintEventArgs e,int[] alWeight)
{
    int numberOfSections = alWeight.Length;
    int lengthArea = 400;
    int heightArea = 250;
    int topX = 20;
    int topY = 400;
    int maxWeight = MaxValue(alWeight);
    int[] height = new int[numberOfSections];
    int total = SumOfArray(alWeight);
    Random rnd = new Random();
    SolidBrush brush = new SolidBrush(Color.Aquamarine);
    Pen pen = new Pen(Color.Gray);
    Rectangle rec = new Rectangle(topX, topY, lengthArea, heightArea);
    e.Graphics.DrawRectangle(pen, rec);
    pen.Color = Color.Black;
    int smallX = topX;
    int smallY = 0;
    int smallLength = (lengthArea / (alWeight.Length + 1));
    int smallHeight = 0;
    Point p1 = new Point();
    Point p2 = new Point();
    for (int i = 0; i < numberOfSections; i++)
    {
        brush.Color = Color.FromArgb(rnd.Next(200, 255),
		rnd.Next(255), rnd.Next(255), rnd.Next(255));
        p1 = p2;
        p2.X = p2.X + smallLength;
        smallHeight = ((alWeight[i] * heightArea) / maxWeight);
        p2.Y = topY + heightArea - smallHeight;
        if (p1.X != 0 && p1.Y != 0)
        {
            e.Graphics.DrawLine(pen, p1, p2);
        }
        DrawDots(e,p2);
        smallX = smallX + smallLength;
    }
}

这是突出显示点的函数:

private void DrawDots(PaintEventArgs e, Point p1)
{
    Pen pen = new Pen(Color.SeaGreen);
    e.Graphics.DrawPie(pen, p1.X-5 , p1.Y-5, 10, 10, 0, 360);
    e.Graphics.FillPie(new SolidBrush(Color.Purple),
			p1.X - 5, p1.Y - 5, 10, 10, 0, 360);
}

关注点

因此,这些是我们能创建的一些图表。我们还可以根据自己的需求进行自定义。如果您的需求只是绘制类似于这三个图表中的任何一个,只需复制粘贴该函数,然后根据您的要求更改坐标和颜色即可。此外,我还使用了另外两个简单的函数来获取数组的最大值和总和。您可以在附件中找到完整的源代码。

需要注意的重要一点是,所有这四个函数都从 `Paint` 事件处理函数调用,并接受 `PaintEventArgs` 和权重数组作为参数。

历史

  • 2009年4月7日:初稿
© . All rights reserved.