月度归档:2010年06月

性能监视工具的使用

一个产品需要进行压力测试,所以我决定使用Windows2003系统自带的Performance工具进行系统性能监视。

如果在测试环境中你有足够的权限,你可以方便地使用专门一台机器执行Performance工具,远程监视目标服务器,比起在目标服务器上执行监视工具,不会给目标服务器带来额外的资源负担。

当你执行"性能"工具,准备开始测试的时候,你会发现其监视当前系统状态的图表默认只记录小于二分钟的数据,而往往我们要监视的时间会长得多,那么怎么样使用该工具得到我们想要的监视数据呢?下面我将一步一步的介绍:

1. 右键"我的电脑",单击"计算机管理"。

2. 展开"系统工具"和"性能日志和警报"。

 

   点击查看原图

3. 右键单击"计数器日志"。

4. 选择"新的日志",在弹出的对话框中输入你希望的名字并单击"确定",将会出现如下窗口。

 点击查看原图

 

 

5. 单击"增加计数器"按钮。

   点击查看原图

 

   

6. 在弹出的窗口中,选择"从计算机上选择计数器"单选按钮。

7. 输入你要测试的计算机名字,例如:"\\MS-DAVIDCOMPUTE

",或是点击下拉列表进行选择。

8. 然后,点击"性能对象"的下拉列表。

此时要注意,如果你的Windows登录用户没有足够权限的话,该列表将不能正常展开,或是提示不能连接。你可以登录Windows时采用具有足够权限的账号,或是手动设置"性能日志与警报"服务的登录账号。

如果您要手动设置"性能日志与警报"服务的登录账号,你可以展开"服务与应用"选择"服务",在右侧列表中选择"性能日志与警报"。

   点击查看原图

 

   

9. 右键选择"属性",在弹出的属性窗口中选择"登录"标签,如下图,您可以手动修改该服务使用的登录账号。

   点击查看原图

 

   

10. 如果您确认有足够的权限,您将拥有如下界面。您选择"性能对象"后下面的列表将会列出相关的可监视的性能指标。

   点击查看原图

 

   

11. 选择列表中您要监视的性能指标,并单击"增加"按钮,然后单击"关闭"按钮。此时会如下图:

   点击查看原图

 

 

12. 如上图,您选择的监视指标会显示在"计数器"列表中,您还可以修改采样时间,默认为15秒。单击"进度表"标签您可以设定是手动起动或关闭日志还是定时起动或关闭日志,我们这里采用手动起动或关闭。

   点击查看原图

 

   

13. 单击"确定"按钮,将弹出提示您是否想要创建日志文件。

   点击查看原图

 

 

14. 单击"确定"按钮。

. 点击查看原图

 

   

15. 右键单击我们新建的日志"test",在弹出菜单中选择"开始",则其图标将变成绿色,如下图:

   点击查看原图

 

 

16. 经过一段时间,你可以停止该日志计数器,右键单击"test"选择"停止"即可。

17. 下面,我们就可以具体观察我们记录的日志了。点击"开始","控制面板",在"管理工具"中双击"性能"运行监视工具。

   点击查看原图

 

   

18. 在弹出的主窗体中,当前显示的默认数据是当前计算机的活动状态,我们需要把默认的个计数器删除,并重新添加我们上面记录的日志。选中列表中的项目,单击工具栏中的"删除"按钮,把所有的项全部删除。

   点击查看原图

 

   

19. 删除所有默认项后,如下图:

   点击查看原图

 

   

20. 右键单击空列表,在弹出菜单中选择"属性",如下图:

 点击查看原图

 

   

21. 选择"源"标签,"日志文件",这时"增加"按钮将会变成可用状态。

   点击查看原图

 

   

22. 单击"增加"按钮。

   点击查看原图

 

   

23. 在弹出的文件选择对话框中,选择您上次记录的日志文件,点击"打开"按钮。

   点击查看原图

 

   

24. 单击"时间范围"按钮,下边的时间带将会变成可用状态。

   点击查看原图

 

   

25. 拖动时间带上左右两个小方块,来改变你要观察的时间范围。点击"确定"按钮。

   点击查看原图

 

   

26. 右键单击空列表,选择"添加计数器"。

   点击查看原图

 

   

27. 在添加计数器窗口中,默认会显示你曾经记录日志的所有性能指标,你可以选中你想要观察的项目,单击"增加"按钮,然后单击"关闭"按钮。

   点击查看原图

 

   

28. 最后,你将看到你曾经记录的性能记录,其时间范围也是你指定的时间范围。

   

附注:整个过程中,很可能会遇到无法启动"计数器"的情况,其原因多半是其服务使用的账号权限不够,可以参考从第8步开始的介绍。

C#中用GDI+生成饼状图和柱状图

 

using System;
using System.IO;//用于文件存取


using System.Data;//用于数据访问


using System.Drawing;//提供画GDI+图形的基本功能
using System.Drawing.Text;//提供画GDI+图形的高级功能
using System.Drawing.Drawing2D;//提供画高级二维,矢量图形功能
using System.Drawing.Imaging;//提供画GDI+图形的高级功能
namespace lc.laili.Web.Code
{
/// <summary>
/// MyImage 的摘要说明。
/// </summary>
public class MyImage
{
public MyImage()
{
   //
   // TODO: 在此处添加构造函数逻辑
   //

}

 

/// <summary>
/// 数据源是PieChartValue对象的饼状图
/// </summary>
/// <param name="title">饼状图大标题</param>
/// <param name="subTitle">饼状图小标题</param>
/// <param name="width">图宽</param>
/// <param name="height">图高</param>
/// <param name="Mydata">PieChartValue对象</param>
/// <returns>饼状图路径</returns>
public string Render(string title, string subTitle, int width, int height, PieChartValue[] Mydata)
{
   const int SIDE_LENGTH = 400;
   const int PIE_DIAMETER = 200;
   //DataTable dt = DataTable;

   //通过输入参数,取得饼图中的总基数
   float sumData = 0;
   for (int i = 0; i < Mydata.Length; i++)
   {
    sumData += Convert.ToSingle(Mydata[i].MValue);
   }
   //产生一个image对象,并由此产生一个Graphics对象
   Bitmap bm = new Bitmap(width, height);
   Graphics g = Graphics.FromImage(bm);
   //设置对象g的属性
   g.ScaleTransform((Convert.ToSingle(width)) / SIDE_LENGTH, (Convert.ToSingle(height)) / SIDE_LENGTH);
   g.SmoothingMode = SmoothingMode.Default;
   g.TextRenderingHint = TextRenderingHint.AntiAlias;

   //画布和边的设定
   g.Clear(Color.White);
   g.DrawRectangle(Pens.Black, 0, 0, SIDE_LENGTH - 1, SIDE_LENGTH - 1);
   //画饼图标题
   g.DrawString(title, new Font("Tahoma", 14), Brushes.Black, new PointF(5, 5));
   //画饼图的图例
   g.DrawString(subTitle, new Font("Tahoma", 12), Brushes.Black, new PointF(7, 35));
   //画饼图
   float curAngle = 0;
   float totalAngle = 0;
   for (int j = 0; j < Mydata.Length; j++)
   {
    curAngle = Convert.ToSingle(Mydata[j].MValue) / sumData * 360;

    g.FillPie(new SolidBrush(ChartUtil.GetChartItemColor(j)), 100, 65, PIE_DIAMETER, PIE_DIAMETER, totalAngle, curAngle);
    g.DrawPie(Pens.Black, 100, 65, PIE_DIAMETER, PIE_DIAMETER, totalAngle, curAngle);
    totalAngle += curAngle;
   }
   //画图例框及其文字
   g.DrawRectangle(Pens.Black, 200, 300, 199, 99);
   g.DrawString("图表说明", new Font("Tahoma", 12, FontStyle.Bold), Brushes.Black, new PointF(200, 300));

   //画图例各项
   PointF boxOrigin = new PointF(210, 330);
   PointF textOrigin = new PointF(235, 326);
   float percent = 0;
   for (int k = 0; k < Mydata.Length; k++)
   {
    g.FillRectangle(new SolidBrush(ChartUtil.GetChartItemColor(k)), boxOrigin.X, boxOrigin.Y, 20, 10);
    g.DrawRectangle(Pens.Black, boxOrigin.X, boxOrigin.Y, 20, 10);
    percent = Convert.ToSingle(Mydata[k].MValue) / sumData * 100;
    g.DrawString(Mydata[k].MValue.ToString() + " - " + Mydata[k].Name.ToString() + " (" + percent.ToString("0") + "%)", new Font("Tahoma", 10), Brushes.Black, textOrigin);
    boxOrigin.Y += 15;
    textOrigin.Y += 15;
   }
   //回收资源
   g.Dispose();
//此处存储路径需要自己改

   System.Drawing.Image MyImage=(System.Drawing.Image)bm;
   string TruePath= System.Web.HttpContext.Current.Server.MapPath("~/images");
   string TrueName=DateTime.Now.ToString("yyyyMMddhhmmss")+DateTime.Now.Millisecond;
   MyImage.Save(TruePath+"\\"+TrueName+".jpg
", System.Drawing.Imaging.ImageFormat.Gif);
   return "~/images/"+TrueName+".jpg";
}

 

 

 

/// <summary>
/// 数据源是BarChartValue对象的柱状图
/// </summary>
/// <param name="title">大标题</param>
/// <param name="subTitle">小标题</param>
/// <param name="width">图宽</param>
/// <param name="height">图高</param>
/// <param name="Mydata">BarChartValue对象</param>
/// <returns>柱状图路径</returns>
public string Render(string title, string subTitle, int width, int height, BarChartValue[] Mydata)
{
   const int SIDE_LENGTH = 400;
   const int CHART_TOP = 75;
   const int CHART_HEIGHT = 200;
   const int CHART_LEFT = 50;
   const int CHART_WIDTH = 300;

   //计算最高的点
   float highPoint = 0;
   for (int i=0; i < Mydata.Length;i++ )
   {
    if (highPoint < Convert.ToSingle(Mydata[i].MValue))
    {
     highPoint = Convert.ToSingle(Mydata[i].MValue);
    }
   }
   //建立一个Graphics对象实例
   Bitmap bm = new Bitmap(width, height);

    Graphics g = Graphics.FromImage(bm);
    //设置条图图形和文字属性
    g.ScaleTransform((Convert.ToSingle(width)) / SIDE_LENGTH, (Convert.ToSingle(height)) / SIDE_LENGTH);
    g.SmoothingMode = SmoothingMode.Default;
    g.TextRenderingHint = TextRenderingHint.AntiAlias;

    //设定画布和边
    g.Clear(Color.White);
    g.DrawRectangle(Pens.Black, 0, 0, SIDE_LENGTH - 1, SIDE_LENGTH - 1);
    //画大标题
    g.DrawString(title, new Font("Tahoma", 14), Brushes.Black, new PointF(5, 5));
    //画小标题
    g.DrawString(subTitle, new Font("Tahoma", 12), Brushes.Black, new PointF(7, 35));
    //画条形图
    float barWidth = CHART_WIDTH / (Mydata.Length * 2);
    PointF barOrigin = new PointF(CHART_LEFT + (barWidth / 2), 0);
    float barHeight = Mydata.Length;
    for (int i = 0; i < Mydata.Length; i++)
    {
     barHeight = Convert.ToSingle(Mydata[i].MValue) * 200 / highPoint * 1;
     barOrigin.Y = CHART_TOP + CHART_HEIGHT - barHeight;
     g.FillRectangle(new SolidBrush(ChartUtil.GetChartItemColor(i)), barOrigin.X, barOrigin.Y, barWidth, barHeight);

     g.DrawString(Mydata[i].Name, new Font("Tahoma", 3), Brushes.Black, new PointF(barOrigin.X-3, 277));

 

     barOrigin.X = barOrigin.X + (barWidth * 2);
    }
    //设置边
    g.DrawLine(new Pen(Color.Black, 2), new Point(CHART_LEFT, CHART_TOP), new Point(CHART_LEFT, CHART_TOP + CHART_HEIGHT));
    g.DrawLine(new Pen(Color.Black, 2), new Point(CHART_LEFT, CHART_TOP + CHART_HEIGHT), new Point(CHART_LEFT + CHART_WIDTH, CHART_TOP + CHART_HEIGHT));
    //画图例框和文字

    //g.DrawRectangle(Pens.Black, 200, 300, 199, 99);
    //g.DrawString("图表说明", new Font("Tahoma", 12, FontStyle.Bold), Brushes.Black, new PointF(200, 300));

    g.DrawRectangle(new Pen(Color.Black, 1), 10, 290, 299, 110);
    g.DrawString("图表说明", new Font("Tahoma", 10, FontStyle.Bold), Brushes.Black, new PointF(11, 290));

    //画图例
    PointF boxOrigin = new PointF(10, 310);
    PointF textOrigin = new PointF(35, 306);
    for (int i = 0; i < Mydata.Length; i++)
    {
     g.FillRectangle(new SolidBrush(ChartUtil.GetChartItemColor(i)), boxOrigin.X, boxOrigin.Y, 20, 10);
     g.DrawRectangle(Pens.Black, boxOrigin.X, boxOrigin.Y, 2, 1);
     g.DrawString(Mydata[i].Name.ToString() + " - " + Mydata[i].MValue.ToString(), new Font("Tahoma", 10), Brushes.Black, textOrigin);
     if(i<5)
     {
      boxOrigin.Y += 15;
      textOrigin.Y += 15;
     }
     else if(i==5)
     {
      boxOrigin.X = 150;
      boxOrigin.Y = 310;
      textOrigin.X = 176;
      textOrigin.Y = 306;

     }
     else
     {
      boxOrigin.Y += 15;
      textOrigin.Y += 15;
     }

    }
    //输出图形
    g.Dispose();
//此处存储路径需要自己改

   System.Drawing.Image MyImage=(System.Drawing.Image)bm;
   string TruePath= System.Web.HttpContext.Current.Server.MapPath("~/images");
   string TrueName=DateTime.Now.ToString("yyyyMMddhhmmss")+DateTime.Now.Millisecond;
   MyImage.Save(TruePath+"\\"+TrueName+".jpg
", System.Drawing.Imaging.ImageFormat.Gif);
   return "~/images/"+TrueName+".jpg";

}
}

/// <summary>
/// 选择颜色
/// </summary>
public class ChartUtil
{
public ChartUtil()
{
}
public static Color GetChartItemColor(int itemIndex)
{
   Color selectedColor;
   switch (itemIndex)
   {
    case 0:
     selectedColor = Color.AliceBlue;
     break;
    case 1:
     selectedColor = Color.Red;
     break;
    case 2:
     selectedColor = Color.Yellow;
     break;
    case 3:
     selectedColor = Color.AntiqueWhite;
     break;
    case 4:
     selectedColor = Color.Aqua;
     break;
    case 5:
     selectedColor = Color.Aquamarine;
     break;
    case 6:
     selectedColor = Color.Azure;
     break;
    case 7:
     selectedColor = Color.Beige;
     break;
    case 8:
     selectedColor = Color.Black;
     break;
    case 9:
     selectedColor = Color.Brown;
     break;
    case 10:
     selectedColor = Color.Coral;
     break;
    case 11:
     selectedColor = Color.DarkCyan;
     break;
    case 12:
     selectedColor = Color.DarkOrange;
     break;
    default:
     selectedColor = Color.DarkViolet;
     break;
   }
   return selectedColor;
}
}

/// <summary>
/// 饼状图的数据对象
/// </summary>
public class PieChartValue
{
public PieChartValue()
{

}
public PieChartValue(string MyName, int Myvalue)
{
   Name = MyName;
   MValue = Myvalue;
}
private string name;
public string Name
{
   get
   {
    return name;
   }
   set
   {
    name = value;
   }
}

private int mvalue;
public int MValue
{
   get
   {
    return mvalue;
   }
   set
   {
    mvalue = value;
   }
}
}

/// <summary>
/// 柱状图的数据对象
/// </summary>
public class BarChartValue
{
public BarChartValue()
{

}
public BarChartValue(string MyName, int Myvalue)
{
   Name = MyName;
   MValue = Myvalue;
}
private string name;
public string Name
{
   get
   {
    return name;
   }
   set
   {
    name = value;
   }
}

private int mvalue;
public int MValue
{
   get
   {
    return mvalue;
   }
   set
   {
    mvalue = value;
   }
}
}
}

用法

   #region 生成柱状图

   MyImage MyMyImage = new MyImage();
   BarChartValue[] BarChartValue ={

          new BarChartValue("第1个月",5),
          new BarChartValue("第2个月",2),
          new BarChartValue("第3个月",3),
          new BarChartValue("第4个月",4),
          new BarChartValue("第5个月",5),
          new BarChartValue("第6个月",6),
          new BarChartValue("第7个月",7),
          new BarChartValue("第8个月",8),
          new BarChartValue("第9个月",9)

             };
   Image1.ImageUrl = MyMyImage.Render("全年统计", "98年", 1000, 1000, BarChartValue);
   #endregion

 

   #region 生成饼状图
   PieChartValue[] PieChartValue ={

              new PieChartValue("第一个仓库",1),
              new PieChartValue("第一个仓库",2)
             };

   Image2.ImageUrl = MyMyImage.Render("仓库统计饼状图", "小仓库", 500, 500, PieChartValue);
   #endregion

c# GDI+简单绘图

最近对
GDI+
这个东西接触的比较多,也做了些简单的实例,比如绘图板,仿
QQ
截图等.

  最早接触这个类,是因为想做仿
QQ
截图的效果.巧的很,学会了如何做截图后,
.NET
课堂上老师也正巧要讲关于
c#
绘图方面的知识,并且我自己又在网上学习金老师的培训班,也是要用到这个类.在学习中有一些体会,所以准备把这些体会记下来,因为内容比较多,可能我会分几次写.

  废话不多说了,我们先来认识一下这个
GDI+
,看看它到底长什么样
.

GDI+

Graphics Device Interface Plus
也就是图形设备接口
,
提供了各种丰富的图形图像处理功能
;

C#.NET
中,使用
GDI+
处理二维(
2D
)的图形和图像,使用
DirectX
处理三维(
3D
)的图形图像
,
图形图像处理用到的主要命名空间是
System . Drawing
:提供了对
GDI+
基本图形功能的访问,主要有
Graphics
类、
Bitmap
类、从
Brush
类继承的类、
Font
类、
Icon
类、
Image
类、
Pen
类、
Color
类等
.

大概了解了什么是
GDI+

,
我们来看一下绘图要用到的主要工具
,
要画图
,
肯定要画板

,

C#
中画板可以通过
Graphics
这个类来创建
,
有了画板
,
总得弄个
什么之类的吧

,
不然怎么画呀
,
难不成我们用手指画
.
笔又可以分好多种类
,
比如铅笔
,
画刷等
.
它们的区别主要是铅笔可以用来画线条
,
而画刷呢
,
嘿嘿
,
自己考虑下
.

c#
中我们可以用
Pen,Brush
类来实现类似功能
.
颜料则自然是用
Color
类了
.

有了工具
,
我们就可以开始动手了
!(所需命名空间:using System.Drawing;
)

实现效果
:
在空白窗体中画基本图形

首先

准备一个画板
:

创建一个画板主要有
3
种方式
:
A:

在窗体或控件的
Paint
事件中直接引用
Graphics
对象

B:

利用窗体或某个控件的
CreateGraphics
方法

C:

从继承自图像的任何对象创建
Graphics
对象


这次我们就先以
A
为例说明问题
:

 
private
 
void
 Form1_Paint(
object
 sender, PaintEventArgs e)
        

 

{
            Graphics g 

=
 e.Graphics; 
//
创建画板,这里的画板是由Form提供的.
 

         }





然后

,
我们要只笔
:

private
 
void
 Form1_Paint(
object
 sender, PaintEventArgs e)
        

 

{
            Graphics g 

=
 e.Graphics; 
//
创建画板,这里的画板是由Form提供的.
 

             Pen p 
=
 
new
 Pen(Color.Blue, 
2
);
//
定义了一个蓝色,宽度为的画笔
 

         }


接下来

我们就可以来画画了
.

private
 
void
 Form1_Paint(
object
 sender, PaintEventArgs e)
        

 

{
            Graphics g 

=
 e.Graphics; 
//
创建画板,这里的画板是由Form提供的.
 

             Pen p 
=
 
new
 Pen(Color.Blue, 
2
);
//
定义了一个蓝色,宽度为的画笔
 

             g.DrawLine(p, 
10

10

100

100
);
//
在画板上画直线,起始坐标为(10,10),终点坐标为(100,100)
 

             g.DrawRectangle(p, 
10

10

100

100
);
//
在画板上画矩形,起始坐标为(10,10),宽为,高为
 

             g.DrawEllipse(p, 
10

10

100

100
);
//
在画板上画椭圆,起始坐标为(10,10),外接矩形的宽为,高为
 

         }



        

效果图如下

:

 点击查看原图

 

1.
首先我们来看下上一片中我们使用过的
Pen.

Pen的属性主要有
: Color(颜色
),DashCap(短划线终点形状
),DashStyle(虚线样式
),EndCap(线尾形状
), StartCap(线头形状
),Width(粗细
)
.

我们可以用
Pen 来画虚线
,带箭头的直线等

Pen  p 
=
 
new
  Pen(Color.Blue, 
5
);
//
设置笔的粗细为,颜色为蓝色


Graphics  g 
=
 
this
.CreateGraphics();


//
画虚线


p.DashStyle 
=
 DashStyle.Dot;
//
定义虚线的样式为点


g.DrawLine(p, 
10

10

200

10
);


//
自定义虚线


p.DashPattern 
=
 
new
  
float
[] 

2

1
 }

;
//
设置短划线和空白部分的数组


g.DrawLine(p, 
10

20

200

20
);


//
画箭头,只对不封闭曲线有用


p.DashStyle 
=
 DashStyle.Solid;
//
恢复实线


p.EndCap 
=
 LineCap.ArrowAnchor;
//
定义线尾的样式为箭头


g.DrawLine(p, 
10

30

200

30
);

g.Dispose();
p.Dispose();


 

以上代码运行结果:
点击查看原图

 

2.
接下来我们来看下
Brush

的使用


作用
:我们可以用画刷填充各种图形形状,如矩形、椭圆、扇形、多边形和封闭路径等
,主要有几种不同类型的画刷
:

        

SolidBrush:画刷最简单的形式,用纯色进行绘制

        

HatchBrush:类似于
SolidBrush,但是可以利用该类从大量预设的图案中选择绘制时要使用的图案,而不是纯色

        

TextureBrush:使用纹理(如图像)进行绘制

        

LinearGradientBrush:使用沿渐变混合的两种颜色进行绘制

        

PathGradientBrush :基于编程者定义的唯一路径,使用复杂的混合色渐变进行绘制

我们这里只是简单介绍使用其中的几种
:

Graphics g 
=
 
this
.CreateGraphics();
Rectangle rect 

=
 
new
 Rectangle(
10

10

50

50
);
//
定义矩形,参数为起点横纵坐标以及其长和宽


//
单色填充


SolidBrush b1 
=
 
new
 SolidBrush(Color.Blue);
//
定义单色画刷          


g.FillRectangle(b1, rect);
//
填充这个矩形


//
字符串


g.DrawString(
"
字符串
"

new
 Font(
"
宋体
"

10
), b1, 
new
 PointF(
90

10
));


//
用图片填充


TextureBrush b2 
=
 
new
 TextureBrush(Image.FromFile(
@"
e:\picture\1.jpg
"
));
rect.Location 

=
 
new
 Point(
10

70
);
//
更改这个矩形的起点坐标


rect.Width 
=
 
200
;
//
更改这个矩形的宽来


rect.Height 
=
 
200
;
//
更改这个矩形的高


g.FillRectangle(b2, rect);


//
用渐变色填充


rect.Location 
=
 
new
 Point(
10

290
);
LinearGradientBrush b3 

=
 
new
  LinearGradientBrush(rect, Color.Yellow , Color.Black , LinearGradientMode.Horizontal);
g.FillRectangle(b3, rect);



 

运行效果图
:


点击查看原图


3.
坐标轴变换



winform中的坐标轴和我们平时接触的平面直角坐标轴不同
,winform中的坐标轴方向完全相反
:窗体的左上角为原点
(0,0),水平向左则
X增大
,垂直下向则
Y增大


点击查看原图

 

 

  运行效果图:

4.
最后我们来看下
Graphics

这个画板上我们还可以画什么






其实我们上面用到的都是在画一些简单的图形
,直线
,矩形
,扇形
,圆孤等
,我们还可以用它来绘制图片
,这可以用它的
DrawImage方法
.


这里我不详细讲解
,大家有兴趣可以自己去
MSDN了解下
.


我们后面会讲到的截图就会用到这个方法







点击查看原图

 


接下来
,
我们来实际操作下
,
通过旋转坐标轴的方向来画出不同角度的图案
,
或通过更改坐标原点的位置来平衡坐标轴的位置


Graphics g 
=
 
this
.CreateGraphics();


//
单色填充

//
SolidBrush b1 = new SolidBrush(Color.Blue);
//
定义单色画刷          


Pen p 
=
 
new
 Pen(Color.Blue,
1
);


//
转变坐标轴角度


for
 (
int
 i 
=
 
0
; i 
<
 
90
; i
++
)


{
    g.RotateTransform(i);

//
每旋转一度就画一条线


    g.DrawLine(p, 
0

0

100

0
);
    g.ResetTransform();

//
恢复坐标轴坐标


}




//
平移坐标轴


g.TranslateTransform(
100

100
);
g.DrawLine(p, 

0

0

100

0
);
g.ResetTransform();


//
先平移到指定坐标,然后进行度旋转


g.TranslateTransform(
100
,
200
);

for
 (
int
 i 
=
 
0
; i 
<
 
8
; i
++
)

{
g.RotateTransform(

45
);
g.DrawLine(p, 

0

0

100

0
);
}




g.Dispose();

我们先来做一个简单的----仿QQ截图,关于这个的例子其实网上已经有这方面的资料了,但是为了文章的完整性,还是觉得有必要讲解.
  我们先来看一下效果:
                                                                                                                  (图1)点击查看原图
 
                                                      点击查看原图
 
                                                                                                                     (图2)

  接下来看看这是如何做到的. 
  思路:聊天窗体上有一个截图按钮,点击按钮后,程序将整个屏幕画在一个新的全屏窗体上,然后显示这个窗体.因为是全屏的窗体,并且隐藏了菜单栏、工具栏等,所以在我们看来就好像是一个桌面的截图,然后在这个新窗体上画矩形,最后保存矩形中的内容并显示在原来的聊天窗体中.
  步骤:
  A.新建一个窗体.命名为Catch.然后设置这个窗体的FormBorderStyle为None,WindowState为Maximized.
  B.我们对代码进行编辑:

  C.创建了Catch窗体后,我们在截图按钮(位于聊天窗体上)上加入以下事件:

        
private
 
void
 bCatch_Click(
object
 sender, EventArgs e)
        

{

            
if
 (bCatch_HideCurrent.Checked)
            


{
                

this
.Hide();
//
隐藏当前窗体


                Thread.Sleep(
50
);
//
让线程睡眠一段时间,窗体消失需要一点时间


                Catch CatchForm 
=
 
new
 Catch();
                Bitmap CatchBmp 

=
 
new
 Bitmap(Screen.AllScreens[
0
].Bounds.Width, Screen.AllScreens[
0
].Bounds.Height);
//
新建一个和屏幕大小相同的图片         


                Graphics g 
=
 Graphics.FromImage(CatchBmp);
                g.CopyFromScreen(

new
 Point(
0

0
), 
new
 Point(
0

0
), 
new
 Size(Screen.AllScreens[
0
].Bounds.Width, Screen.AllScreens[
0
].Bounds.Height));
//
保存全屏图片


                CatchForm.BackgroundImage 
=
 CatchBmp;
//
将Catch窗体的背景设为全屏时的图片


                
if
 (CatchForm.ShowDialog() 
==
 DialogResult.OK)
                


{
//
如果Catch窗体结束,就将剪贴板中的图片放到信息发送框中


                    IDataObject iData 
=
 Clipboard.GetDataObject();
                    DataFormats.Format myFormat 

=
 DataFormats.GetFormat(DataFormats.Bitmap);
                    

if
 (iData.GetDataPresent(DataFormats.Bitmap))
                    


{
                        richtextbox1.Paste(myFormat);
                        Clipboard.Clear();

//
清除剪贴板中的对象


                    }


                    

this
.Show();
//
重新显示窗体


                }

            }

        }

  这样我们的截图功能便完成了.
  我想对于初学者来说如何消去第一次绘制的图片是个比较困难的问题.如果没有采取措施,你会发现只要你鼠标移动,就会画一个矩形,这样便会出现N多的矩形,而我们只是要最后的那一个.
  一般解决这种问题的方法有两种:
  1.就是在绘制第二个图形时,我们先用与底色相同的颜色将上次绘制的图形重新绘制一下.但这往往需要底色为纯色时使用.
  2.我们并不直接将图形画在画板上,我们用一个图片A来保存原画板上的图片.然后再新建一个与图片A相同的图片B,将我们要绘制的图形画在该图片B上,然后再将该图片B画在画板上.这样图片A并没有被改变.于是第二次画的时候我们还是同样新建一个与图片A相同的图片进行绘制.那么上一次的图形就不会被保留下来.问题也就解决了.

  下一次,向大家介绍如何做一个仿windows画板的程序

 

 

 

个人认为如果想做一个功能强大的绘图工具,那么单纯掌握GDI还远远不够,我的目前也只能做一个比较简单的绘图工具了.不足之处,欢迎大家讨论!

  先来看一下最终效果吧:

点击查看原图

  
  主要实现功能:画直线,矩形,橡皮,圆形,切换颜色,打开图片,保存图片,清除图片,手动调节画布大小;软件刚启动时,为一张空白画布,我们可以直接在画布上绘画,也可以通过菜单中的“打开”,导入一张图片,然后我们就可以在这张图片上进行绘制。
  平台:VS2005 WINFORM

  由于代码过多,在这里只简要介绍下制作步骤,提供大家工程下载.
  1.对整个界面进行布局.
  2.实现绘图工具的功能
  3.实现颜色拾取的功能,这里我们直接拿上次写的自定义控件来用.
  4.实现菜单功能
  5.实现手动调节画布大小的功能
  6.测试 

  实现绘图工具的功能

  为了让代码藕合度小点,稍许用了些设计模式,因为不是很会,所以代码还是有点乱乱的,嘿嘿!关于绘图工具的这些功能块全部写在了DrawTools这个类里.那么在主窗体中,只需要调用这个类来完成绘制就行了,而不需要过多的涉及到具体的绘图代码。绘图工具这个类提供的主要工具就是:铅笔、橡皮、直线、矩形、圆形、实心矩形、实心圆形。关于这些功能块的代码,并不难,只要大家对认真看过前几篇内容,那应该都看得懂。
  这里要注意以下几点:
  1.如何防止记录不必要的绘图过程中的痕迹?
  这个问题在第三篇

中有提到过,大家不妨先去看看那一篇。为了让代码看起来可读性高点,我设置了两个Image变量,finishingImg用来保存绘图过程中的痕迹,orginalImg用来保存已完成的绘图过程和初始时的背景图片。
  2.这个类如何与主窗体进行通信?
  当然如果直接将这些功能块写在主窗体中自然没有这个问题。但是那样代码会显得很混杂,如果只是工具代码出现问题就需要改整个项目。我在这里通过定义方法和属性,让主窗体通过给属性赋值将画板画布以及颜色什么的信息传给这个工具类,然后通过调用相应的工具方法来使用这些工具。
  3.关键属性
  要想让这些工具能正常使用,必须传递给他以下几样东西:目标画板(也就是picturebox),绘图颜色,原始画布。

  实现菜单功能

  点击查看原图

  这里就需要我们对文件的操作有一点了解,大家可以去查一下相关资料。
  难点主要就是“打开”这个菜单项的实现
  我们要实现将打开后的图片在修改后重新保存就必须让文件在打开后就能关闭,否则就会因为文件打开而无法覆盖原文件。就会导致编译时弹出“GDI  一般性错误”。所以根据网上其它朋友的做法就是先将打开的图片通过GDI+将图片画到另一个画布上,然后及时关闭打开的图片和用来绘制该图片的画板。详见http://www.wanxin.org/redirect.php?tid=3&goto=lastpost



 
private
 
void
 openPic_Click(
object
 sender, EventArgs e)
        

{
            OpenFileDialog ofd 

=
 
new
 OpenFileDialog();
//
实例化文件打开对话框


            ofd.Filter 
=
 
"
JPG|*.jpg|Bmp|*.bmp|所有文件|*.*
"
;
//
设置对话框打开文件的括展名


            
if
 (ofd.ShowDialog() 
==
 DialogResult.OK)
            


{
                Bitmap bmpformfile 

=
 
new
 Bitmap(ofd.FileName);
//
获取打开的文件


                panel2.AutoScrollPosition 
=
 
new
 Point(
0
,
0
);
//
将滚动条复位


                pbImg.Size 
=
 bmpformfile.Size;
//
调整绘图区大小为图片大小



                reSize.Location 

=
 
new
 Point(bmpformfile.Width, bmpformfile.Height);
//
reSize为我用来实现手动调节画布大小用的
                

//
因为我们初始时的空白画布大小有限,"打开"操作可能引起画板大小改变,所以要将画板重新传入工具类


                dt.DrawTools_Graphics 
=
 pbImg.CreateGraphics();

                Bitmap bmp 
=
 
new
 Bitmap(pbImg.Width, pbImg.Height);
                Graphics g 

=
 Graphics.FromImage(bmp);
                g.FillRectangle(

new
 SolidBrush(pbImg.BackColor), 
new
 Rectangle(
0

0
, pbImg.Width, pbImg.Height));
//
不使用这句话,那么这个bmp的背景就是透明的


                g.DrawImage(bmpformfile, 
0

0
,bmpformfile.Width,bmpformfile.Height);
//
将图片画到画板上


                g.Dispose();
//
释放画板所占资源
                

//
不直接使用pbImg.Image = Image.FormFile(ofd.FileName)是因为这样会让图片一直处于打开状态,也就无法保存修改后的图片


                bmpformfile.Dispose();
//
释放图片所占资源


                g 
=
 pbImg.CreateGraphics();
                g.DrawImage(bmp, 

0

0
);
                g.Dispose();
                dt.OrginalImg 

=
 bmp;
                bmp.Dispose();
                sFileName 

=
 ofd.FileName;
//
储存打开的图片文件的详细路径,用来稍后能覆盖这个文件


                ofd.Dispose();

            }


        }

  清除图像其实就是用白色填充整个画布
  其它的都比较简单,这就不具体讲了。

  实现手动调节画布大小
  

网上有人说使用API,但是个人觉得还是使用其它控件帮忙比较简单,至少我们还看得懂。
  思路:放置一个picturebox1(尺寸为5*5),将它固定在主画板的右下角,然后改变鼠标进入时的Cursor为箭头形状,设置鼠标按下移动时的事件,让该picturebox1 跟随鼠标移动。当鼠标松开时,将主画板的右下角坐标调整为picturebox1的坐标。
  下面来看下代码:
  其中的reSize就是我们用来帮忙的picturebox控件 

        
private
 
bool
 bReSize 
=
 
false
;
//
是否改变画布大小


        
private
 
void
 reSize_MouseDown(
object
 sender, MouseEventArgs e)
        

{
            bReSize 

=
 
true
;
//
当鼠标按下时,说明要开始调节大小


        }



        

private
 
void
 reSize_MouseMove(
object
 sender, MouseEventArgs e)
        

{
            

if
 (bReSize)
            


{
                reSize.Location 

=
 
new
 Point(reSize.Location.X 
+
 e.X, reSize.Location.Y 
+
 e.Y);

            }


        }



        

private
 
void
 reSize_MouseUp(
object
 sender, MouseEventArgs e)
        

{
            bReSize 

=
 
false
;
//
大小改变结束
            

//
调节大小可能造成画板大小超过屏幕区域,所以事先要设置autoScroll为true.
            

//
但是滚动条的出现反而增加了我们的难度,因为滚动条上下移动并不会自动帮我们调整图片的坐标。
            

//
这是因为GDI绘图的坐标系不只一个,好像有三个,没有仔细了解,一个是屏幕坐标,一个是客户区坐标,还个是文档坐标。
            

//
滚动条的上下移动改变的是文档的坐标,但是客户区坐标不变,而location属性就属于客户区坐标,所以我们直接计算会出现错误
            

//
这时我们就需要知道文档坐标与客户区坐标的偏移量,这就是AutoScrollPostion可以提供的



            pbImg.Size 

=
 
new
 Size(reSize.Location.X 
-
 (
this
.panel2.AutoScrollPosition.X), reSize.Location.Y 
-
 (
this
.panel2.AutoScrollPosition.Y));
            dt.DrawTools_Graphics 

=
 pbImg.CreateGraphics();
//
因为画板的大小被改变所以必须重新赋值

            
//
另外画布也被改变所以也要重新赋值


           Bitmap bmp 
=
 
new
 Bitmap(pbImg.Width, pbImg.Height);
            Graphics g 

=
 Graphics.FromImage(bmp);
            g.FillRectangle(

new
 SolidBrush(Color.White), 
0

0
, pbImg.Width, pbImg.Height);
            g.DrawImage(dt.OrginalImg, 

0

0
);
            g.Dispose();
            g 

=
 pbImg.CreateGraphics();
            g.DrawImage(bmp, 

0

0
);
            g.Dispose();
            dt.OrginalImg 

=
 bmp;

            bmp.Dispose();
        }

  效果如下图(仔细看白色区域的右下角):
  点击查看原图

  此时就可以通过拖动那个小方块来调节图片大小了