private void Catch_MouseClick(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Right) {
this.DialogResult = DialogResult.OK; this.Close(); } }
//鼠标左键按下时动作
private void Catch_MouseDown(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
if (!CatchStart) {//如果捕捉没有开始 CatchStart = true;
DownPoint = new Point(e.X, e.Y);//保存鼠标按下坐标 } } }
private void Catch_MouseMove(object sender, MouseEventArgs e) {
if (CatchStart) {//如果捕捉开始
Bitmap destBmp = (Bitmap)originBmp.Clone();//新建一个图片对象,并让它与原始图片相同
Point newPoint = new Point(DownPoint.X, DownPoint.Y);//获取鼠标的坐标 Graphics g = Graphics.FromImage(destBmp);//在刚才新建的图片上新建一个画板
Pen p = new Pen(Color.Blue,1);
int width = Math.Abs(e.X - DownPoint.X), height = Math.Abs(e.Y - DownPoint.Y);//获取矩形的长和宽
if (e.X < DownPoint.X) {
newPoint.X = e.X; }
if (e.Y < DownPoint.Y) {
newPoint.Y = e.Y; }
CatchRect = new Rectangle(newPoint,new Size(width,height));//保存矩形 g.DrawRectangle(p,CatchRect);//将矩形画在这个画板上 g.Dispose();//释放目前的这个画板 p.Dispose();
Graphics g1 = this.CreateGraphics();//重新新建一个Graphics类
//如果之前那个画板不释放,而直接g=this.CreateGraphics()这样的话无法释放掉第一次创建的g,因为只是把地址转到新的g了.如同string一样 g1 = this.CreateGraphics();//在整个全屏窗体上新建画板
g1.DrawImage(destBmp,new Point(0,0));//将刚才所画的图片画到这个窗体上
//这个也可以属于二次缓冲技术,如果直接将矩形画在窗体上,会造成图片抖动并且会有无数个矩形. g1.Dispose();
destBmp.Dispose();//要及时释放,不然内存将会被大量消耗 } }
private void Catch_MouseUp(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
if (CatchStart) {
CatchStart = false; CatchFinished = true; } } }
//鼠标双击事件,如果鼠标位于矩形内,则将矩形内的图片保存到剪贴板中 private void Catch_MouseDoubleClick(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left&&CatchFinished) {
if (CatchRect.Contains(new Point(e.X, e.Y))) {
Bitmap CatchedBmp = new Bitmap(CatchRect.Width, CatchRect.Height);//新建一个于矩形等大的空白图片
Graphics g = Graphics.FromImage(CatchedBmp);
g.DrawImage(originBmp, new Rectangle(0, 0, CatchRect.Width, CatchRect.Height), CatchRect, GraphicsUnit.Pixel);
//把orginBmp中的指定部分按照指定大小画在画板上 Clipboard.SetImage(CatchedBmp);//将图片保存到剪贴板 g.Dispose();
CatchFinished = false;
this.BackgroundImage = originBmp; CatchedBmp.Dispose();
this.DialogResult = DialogResult.OK; this.Close(); }
} } } }
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
相同的图片进行绘制.那么上一次的图形就不会被保留下来.问题也就解决了. g.Dispose();
4.最后我们来看下Graphics这个画板上我们还可以画什么
其实我们上面用到的都是在画一些简单的图形,直线,矩形,扇形,圆孤等,我们还可以用它来绘制图片,这可以用它的DrawImage方法.这里我不详细讲解,大家有兴趣可以自己去MSDN了解下.我们后面会讲到的截图就会用到这个方法. 个人认为如果想做一个功能强大的绘图工具,那么单纯掌握GDI还远远不够,我的目前也只能做一个比较简单的绘图工具了.不足之处,欢迎大家讨论! 先来看一下最终效果吧:
主要实现功能:画直线,矩形,橡皮,圆形,切换颜色,打开图片,保存图片,清除图片,手动调节画布大小;软件刚启动时,为一张空白画布,我们可以直接在画布上绘画,也可以通过菜单中的“打开”,导入一张图片,然后我们就可以在这张图片上进行绘制。 平台:VS2005 WINFORM
由于代码过多,在这里只简要介绍下制作步骤,提供大家工程下载. 1.对整个界面进行布局. 2.实现绘图工具的功能
3.实现颜色拾取的功能,这里我们直接拿上次写的自定义控件来用. 4.实现菜单功能
5.实现手动调节画布大小的功能 6.测试
实现绘图工具的功能
为了让代码藕合度小点,稍许用了些设计模式,因为不是很会,所以代码还是有点乱乱的,嘿嘿!关于绘图工具的这些功能块全部写在了DrawTools这个类里.那么在主窗体中,只需要调用这个类来完成绘制就行了,而不需要过多的涉及到具体的绘图代码。绘图工具这个类提供的主要工具就是:铅笔、橡皮、直线、矩形、圆形、实心矩形、实心圆形。关于这些功能块的代码,并不难,只要大家对认真看过前几篇内容,那应该都看得懂。 这里要注意以下几点:
1.如何防止记录不必要的绘图过程中的痕迹?
这个问题在第三篇中有提到过,大家不妨先去看看那一篇。为了让代码看起来可读性高点,我设置了两个Image变量,finishingImg用来保存绘图过程中的痕迹,orginalImg用来保存已完成的绘图过程和初始时的背景图片。 2.这个类如何与主窗体进行通信?
当然如果直接将这些功能块写在主窗体中自然没有这个问题。但是那样代码会显得很混杂,如果只是工具代码出现问题就需要改整个项目。我在这里通过定义方法和属性,让主窗体通过给属性赋值将画板画布以及颜色什么的信息传给这个工具类,然后通过调用相应的工具方法来使用这些工具。 3.关键属性
要想让这些工具能正常使用,必须传递给他以下几样东西:目标画板(也就是picturebox),绘图颜色,原始画布。 实现菜单功能
这里就需要我们对文件的操作有一点了解,大家可以去查一下相关资料。 难点主要就是“打开”这个菜单项的实现
我们要实现将打开后的图片在修改后重新保存就必须让文件在打开后就能关闭,否则就会因