当前位置: 首页 > 编程日记 > 正文

C# GDI+ 简单绘图 (三) 仿浏览器截屏效果

感谢大家的支持,这几天从早忙到晚,一个字累呀!!!现在挺困的,但是又不习惯这么早睡觉,哎~~还是利用这个时间继续来写第三篇吧.
  前两篇已经基本向大家介绍了绘图的基本知识.那么,我就用我们上两篇所学的,做几个例子.
  我们先来做一个简单的----仿QQ截图,关于这个的例子其实网上已经有这方面的资料了,但是为了文章的完整性,还是觉得有必要讲解.
  我们先来看一下效果:

20083191633.JPG
                                                                                                                  (图1)
                                                      20083191642.JPG
                                                                                                                     (图2)

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

None.gifusing System;
None.gif
using System.Collections.Generic;
None.gif
using System.ComponentModel;
None.gif
using System.Data;
None.gif
using System.Drawing;
None.gif
using System.Text;
None.gif
using System.Windows.Forms;
None.gif
None.gif
namespace Client
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    
public partial class Catch : Form
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
public Catch()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            InitializeComponent();
ExpandedSubBlockEnd.gif        }

InBlock.gif
ContractedSubBlock.gifExpandedSubBlockStart.gif        
用户变量#region 用户变量
InBlock.gif        
private Point DownPoint = Point.Empty;//记录鼠标按下坐标,用来确定绘图起点
InBlock.gif
        private bool CatchFinished = false;//用来表示是否截图完成
InBlock.gif
        private bool CatchStart = false;//表示截图开始
InBlock.gif
        private Bitmap originBmp;//用来保存原始图像
InBlock.gif
        private Rectangle CatchRect;//用来保存截图的矩形
ExpandedSubBlockEnd.gif
        #endregion

InBlock.gif
InBlock.gif        
//窗体初始化操作
InBlock.gif
        private void Catch_Load(object sender, EventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
InBlock.gif            
this.UpdateStyles();
InBlock.gif            
//以上两句是为了设置控件样式为双缓冲,这可以有效减少图片闪烁的问题,关于这个大家可以自己去搜索下
InBlock.gif
            originBmp = new Bitmap(this.BackgroundImage);//BackgroundImage为全屏图片,我们另用变量来保存全屏图片
ExpandedSubBlockEnd.gif
        }

InBlock.gif
InBlock.gif        
//鼠标右键点击结束截图
InBlock.gif
        private void Catch_MouseClick(object sender, MouseEventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
if (e.Button == MouseButtons.Right)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
this.DialogResult = DialogResult.OK;
InBlock.gif                
this.Close();
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
//鼠标左键按下时动作
InBlock.gif
        private void Catch_MouseDown(object sender, MouseEventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
if (e.Button == MouseButtons.Left)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
if (!CatchStart)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{//如果捕捉没有开始
InBlock.gif
                    CatchStart = true;
InBlock.gif                    DownPoint 
= new Point(e.X, e.Y);//保存鼠标按下坐标
ExpandedSubBlockEnd.gif
                }

ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
private void Catch_MouseMove(object sender, MouseEventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
if (CatchStart)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{//如果捕捉开始
InBlock.gif
                Bitmap destBmp = (Bitmap)originBmp.Clone();//新建一个图片对象,并让它与原始图片相同
InBlock.gif
                Point newPoint = new Point(DownPoint.X, DownPoint.Y);//获取鼠标的坐标
InBlock.gif
                Graphics g = Graphics.FromImage(destBmp);//在刚才新建的图片上新建一个画板
InBlock.gif
                Pen p = new Pen(Color.Blue,1);
InBlock.gif                
int width = Math.Abs(e.X - DownPoint.X), height = Math.Abs(e.Y - DownPoint.Y);//获取矩形的长和宽
InBlock.gif
                if (e.X < DownPoint.X)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    newPoint.X 
= e.X;
ExpandedSubBlockEnd.gif                }

InBlock.gif                
if (e.Y < DownPoint.Y)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    newPoint.Y 
= e.Y;
ExpandedSubBlockEnd.gif                }

InBlock.gif                CatchRect 
= new Rectangle(newPoint,new Size(width,height));//保存矩形
InBlock.gif
                g.DrawRectangle(p,CatchRect);//将矩形画在这个画板上
InBlock.gif
                g.Dispose();//释放目前的这个画板
InBlock.gif
                p.Dispose();
InBlock.gif                Graphics g1 
= this.CreateGraphics();//重新新建一个Graphics类
InBlock.gif                
//如果之前那个画板不释放,而直接g=this.CreateGraphics()这样的话无法释放掉第一次创建的g,因为只是把地址转到新的g了.如同string一样
InBlock.gif
                g1 = this.CreateGraphics();//在整个全屏窗体上新建画板
InBlock.gif
                g1.DrawImage(destBmp,new Point(0,0));//将刚才所画的图片画到这个窗体上
InBlock.gif                
//这个也可以属于二次缓冲技术,如果直接将矩形画在窗体上,会造成图片抖动并且会有无数个矩形.
InBlock.gif
                g1.Dispose();
InBlock.gif                destBmp.Dispose();
//要及时释放,不然内存将会被大量消耗
InBlock.gif
                
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
private void Catch_MouseUp(object sender, MouseEventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
if (e.Button == MouseButtons.Left)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
if (CatchStart)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    CatchStart 
= false;
InBlock.gif                    CatchFinished 
= true;
InBlock.gif                  
ExpandedSubBlockEnd.gif                }

ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
//鼠标双击事件,如果鼠标位于矩形内,则将矩形内的图片保存到剪贴板中
InBlock.gif
        private void Catch_MouseDoubleClick(object sender, MouseEventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
if (e.Button == MouseButtons.Left&&CatchFinished)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
if (CatchRect.Contains(new Point(e.X, e.Y)))
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    Bitmap CatchedBmp 
= new Bitmap(CatchRect.Width, CatchRect.Height);//新建一个于矩形等大的空白图片
InBlock.gif
                    Graphics g = Graphics.FromImage(CatchedBmp);
InBlock.gif                    g.DrawImage(originBmp, 
new Rectangle(00, CatchRect.Width, CatchRect.Height), CatchRect, GraphicsUnit.Pixel);
InBlock.gif                    
//把orginBmp中的指定部分按照指定大小画在画板上
InBlock.gif
                    Clipboard.SetImage(CatchedBmp);//将图片保存到剪贴板
InBlock.gif
                    g.Dispose();
InBlock.gif                    CatchFinished 
= false;
InBlock.gif                    
this.BackgroundImage = originBmp;
InBlock.gif                    CatchedBmp.Dispose();
InBlock.gif                    
this.DialogResult = DialogResult.OK;
InBlock.gif                    
this.Close();
ExpandedSubBlockEnd.gif                }

ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

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

None.gif        private void bCatch_Click(object sender, EventArgs e)
ExpandedBlockStart.gifContractedBlock.gif        
dot.gif{
InBlock.gif
InBlock.gif            
if (bCatch_HideCurrent.Checked)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
this.Hide();//隐藏当前窗体
InBlock.gif
                Thread.Sleep(50);//让线程睡眠一段时间,窗体消失需要一点时间
InBlock.gif
                Catch CatchForm = new Catch();
InBlock.gif                Bitmap CatchBmp 
= new Bitmap(Screen.AllScreens[0].Bounds.Width, Screen.AllScreens[0].Bounds.Height);//新建一个和屏幕大小相同的图片         
InBlock.gif
                Graphics g = Graphics.FromImage(CatchBmp);
InBlock.gif                g.CopyFromScreen(
new Point(00), new Point(00), new Size(Screen.AllScreens[0].Bounds.Width, Screen.AllScreens[0].Bounds.Height));//保存全屏图片
InBlock.gif
                CatchForm.BackgroundImage = CatchBmp;//将Catch窗体的背景设为全屏时的图片
InBlock.gif
                if (CatchForm.ShowDialog() == DialogResult.OK)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{//如果Catch窗体结束,就将剪贴板中的图片放到信息发送框中
InBlock.gif
                    IDataObject iData = Clipboard.GetDataObject();
InBlock.gif                    DataFormats.Format myFormat 
= DataFormats.GetFormat(DataFormats.Bitmap);
InBlock.gif                    
if (iData.GetDataPresent(DataFormats.Bitmap))
ExpandedSubBlockStart.gifContractedSubBlock.gif                    
dot.gif{
InBlock.gif                        richtextbox1.Paste(myFormat);
InBlock.gif                        Clipboard.Clear();
//清除剪贴板中的对象
ExpandedSubBlockEnd.gif
                    }

InBlock.gif                    
this.Show();//重新显示窗体
ExpandedSubBlockEnd.gif
                }

ExpandedSubBlockEnd.gif            }

InBlock.gif
ExpandedBlockEnd.gif        }

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

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

原作者:stg609

转载于:https://www.cnblogs.com/zhjzwl/archive/2009/02/27/1399498.html

相关文章:

POJ 1236 Network of Schools(tarjan)

Network of SchoolsDescription A number of schools are connected to a computer network. Agreements have been developed among those schools: each school maintains a list of schools to which it distributes software (the “receiving schools”). Note that if B …

如何设置网页自动刷新(JSP,JS,HTML)

http://blog.163.com/ylx282006126/blog/static/59772717201111685917664/ 转载于:https://www.cnblogs.com/liuzhuqing/p/7480284.html

1084 Broken Keyboard

两个注意的点 1.本题被归到散列专题下&#xff0c;但是由于是逐字符地映射到整形&#xff0c;可以直接把布尔型哈希数组的大小设置为ASCII的数量128&#xff0c;然后直接将字符作为数组下标(如果是字符串&#xff0c;才需要自己写一个哈希函数&#xff0c;将字符串映射到整形&…

Android提示框与通知的使用

1.通知 Android 3.0以前使用的方法 1 NotificationManager nm (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 2 Notification notification new Notification(R.drawable.dss, 3 "通知到了", Syste…

nginx安全日志分析脚本的编写

https://blog.csdn.net/nextdoor6/article/details/51914966

[转]笑死人的考试填空

高考完后又是中考&#xff0c;考题千奇百怪&#xff0c;答卷也五花八门。真佩服现在的学生啊&#xff0c;思维跳脱&#xff0c;天马行空&#xff0c;和我们那时候的循规蹈矩&#xff0c;差别太大了&#xff0c;呵呵。看一组语文试卷中的填空题&#xff1a;1.__________&#xf…

1033 旧键盘打字

1. 非常奇怪&#xff0c;明明都说了用下划线替代空格&#xff0c;但是用scanf读入的时候就会有1个测试点没通过&#xff0c;换成cin.getline就通过了 2.3种情况下对应的哈希表赋值为true。1是上来就赋值&#xff0c;2是对于大写字母把对应小写字母也赋值&#xff0c;这里注意直…

OLE 操作Excel 详解(转)

使用Excel模板进行报表的开发. 今年搞的Excel比较多&#xff0c;总结了一下&#xff0c;相信常用的操作包含的差不多了。 可以首先定义一个无内容的Excel报表模板文件. 通过Tcode SMW0 上传至SAP数据库中备用.(注: Web对象应该选择’WebRFC 应用程序的二进制数据’) 开发程序…

只需3分钟,就能轻松创建 一个SpreadJS的React项目

概述SpreadJS 纯前端表格控件 V11.2(SP2) 已经全面支持了 React 的拓展。接下来我们看下如何利用3分钟快速创建一个 SpreadJS 的 React 项目。1.新建React 项目&#xff08;耗时 1 Min&#xff09;直接运行&#xff1a;npx create-react-app react-spread-sheets还不清楚什么是…

1039 到底买不买

很典型的散列题&#xff0c;对于shop和eva有的珠子(即字符)&#xff0c;各开一个128长度的整形散列表计数&#xff0c;将字符作为下标读入。 然后从0~127进行遍历&#xff0c;看每个下标下两个散列表的数量&#xff0c;如果有shop<eva说明不买&#xff0c;但是遍历仍然要继…

什么是Linq

最近一直利用业余的时间来研究Linq&#xff0c;估计这样的文章在对于园子里很多牛人来说就有点小儿科了&#xff0c;前段时间写了一个Linq To Sql体验的小例子&#xff0c;感觉很简洁程序上操作体验不错&#xff0c;我写这些的文章目的是自我学习笔记的备用和查看&#xff0c;当…

OSS正式支持IPv6公测

背景 6月20日阿里云宣布全面支持IPv6&#xff0c; 随后阿里云开放对象存储OSS也逐步开始向用户公测。 公测步骤 正常使用IPv6服务&#xff0c;除了OSS端支持还需要客户端支持&#xff0c;我们做一些检查证明客户端具备访问 IPv6的能力&#xff0c;再使用OSS SDK或工具通过IPv6 …

C++中定义类的对象:用new和不用new的区别

Point p1; Point *p2new Point(); p1 由系统创建并释放&#xff0c;不用担心会出现内存泄露&#xff0c;但是生命期只有在本区域的大括号内&#xff0c;出了大括号就没用了。 P2 是指针&#xff0c;要自己释放&#xff0c;用不好很危险&#xff0c;用好了功能强大&#xff0c;…

1043 输出PATest

开一个长度为6的整型数组分别记录6个字符的数量&#xff0c;输出的时候条件是数组中至少存在一个不为零的元素 while(PATest[0]||PATest[1]||PATest[2]||PATest[3]||PATest[4]||PATest[5]){//当6个还有一个不为0 AC代码 #include<cstdio> #include<cmath> #inc…

[转载 js] YUI解决mouseout事件冒泡的办法

原文出处&#xff1a;http://design.alibaba-inc.com/?qnode/727&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&am…

adodb.RecordSet的属性和方法

为了更精确地跟踪数据&#xff0c;要用RecordSet组件创建包括数据的游标&#xff0c;游标就是储存在内存中的数据&#xff1a; rs Server.CreateObject("ADODB.RecordSet") rs.Open(sqlStr,conn,1,A) 注&#xff1a;A1表示读取数据&#xff1b;A3表示新增、改动或删…

给女友讲讲设计模式——适配器模式(JAVA实例)5

前言 有这样一个人&#xff0c;看到别人一个个开餐馆赚了好多钱&#xff0c;于是自己也很想在餐饮业这方面大展拳脚&#xff0c;他从别人那里学到了他们的理念&#xff0c;还学习到了他们真正开店的经验。不但如此&#xff0c;他还引进了除了吃饭意外其他的服务&#xff0c;例如…

1041 Be Unique

1.依旧是散列题&#xff0c;开一个整形的哈希数组bets[10010]来计数&#xff0c;最后数目为1的&#xff0c;也就是unique 2.注意&#xff0c;可能为1的不止一个&#xff0c;要输出第一个输入的unique&#xff0c;那么需要记录下读入的顺序&#xff0c;可以开辟一个数组inputs[…

SWT图像处理入门

SWT图像处理入门 Standard Widget Toolkit ( SWT &#xff0c;标准窗口小部件工具箱)&#xff0c;是在 Eclipse 平台上使用的窗口小部件工具箱&#xff0c;它能向开发者提供和本机平台一致的用户界面和比较稳定的性能&#xff0c;也提供了强大的图像处理功能。本文首先介绍 SWT…

MyEclipse中的快捷键

MyEclipse 快捷键1(CTRL)-------------------------------------Ctrl1 快速修复CtrlD: 删除当前行 CtrlQ 定位到最后编辑的地方 CtrlL 定位在某行 CtrlO 快速显示 OutLine CtrlT 快速显示当前类的继承结构 CtrlW 关闭当前Editer CtrlK 快速定位到下一个 CtrlE 快速显示当…

CocoaPods原理(一)

CocoaPods介绍 CocoaPods 是开发 OS X 和 iOS 应用程序的一个第三方库的依赖管理工具。利用 CocoaPods&#xff0c;可以定义自己的依赖关系 (称作 pods)&#xff0c;并且随着时间的变化&#xff0c;以及在整个开发环境中对第三方库的版本管理非常方便。 CocoaPods 背后的理念主…

1055 The World‘s Richest

开始的做法是&#xff0c;对于每一个case&#xff0c;都在整个person数组内进行遍历&#xff0c;把所有年龄符合要求的都放进一个临时数组&#xff0c;然后对临时数组进行排序&#xff0c;再根据要求的数目输出。但是这么做会有一个测试用例因为超时而通不过。 穷则思变。于是…

IOS时间传递机制简记

事件传递顺序&#xff1a;自定义View -- > UIview --> RootViewController --> UIWindow -->UIApplication -->Appdelegate -->nil 注&#xff1a; //分发事件&#xff0c;将当前的触摸事件分发给当前对象的下一个响应者 //如果当前对象处理了当前事件&am…

HDOJ2270(How Many Friends Will Be Together With You)

#include <iostream>using namespacestd;const int MAX 1000005;intfr[MAX];intmain(){ int n, i, tt, sum; while(cin>>n) { for(i 1; i < n; i) fr[i] i;//初始化&#xff0c;一开始每个都是独立的&#xff0c;并无朋友 …

在linux环境下重启oracle数据库,解决密码过期的问题

&#xff08;1&#xff09; 以oracle身份登录数据库&#xff0c;命令&#xff1a;su – oracle &#xff08;2&#xff09; 进入Sqlplus控制台&#xff0c;命令&#xff1a;sqlplus /nolog &#xff08;3&#xff09; 以系统管理员登录&#xff0c;命令&#xff1a;connect /as…

1075 PAT Judge

1. 这一题一开始&#xff0c;为了同一个人的数据更新得方便&#xff0c;我把id从字符串转化成整数&#xff0c;作为数组下标&#xff0c;但是注意了&#xff0c;每个学生还是要有字符串的id属性(根据下标得到)&#xff0c;因为后面一旦排序&#xff0c;数组下标就毫无意义了。 …

MySQL 错误代码和消息

本章列出了当你用任何主机语言调用MySQL时可能出现的错误。首先列出了服务器错误消息。其次列出了客户端程序消息。 B.1. 服务器错误代码和消息 服务器错误信息来自下述源文件&#xff1a; 错误消息信息列在share/errmsg.txt文件中。“%d”和“%s”分别代表编号和字符…

一步步学习汇编(8)之指令

要理解ret,retf&#xff0c;call指令&#xff0c;必须要先理清以下汇编基础知识&#xff1a; 一&#xff0e; [bx]和内存单元<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />[bx]是什么呢&#xff1f; 和[0]有些类似…

【转】不分主副卡!全网通5.0时代到来

全网通在今天已经不是噱头了&#xff0c;它经历了有5年时间&#xff0c;从过去的全网通1.0到现在的全网通5.0&#xff0c;可以说有这长足的发展。今年&#xff0c;小米率先了支持全网通5.0的小米MIX 2S和红米Note5&#xff0c;可以双卡双待4G&#xff0c;一边打电话一边打游戏不…

1048 Find Coins(散列解法)

1. 开始测试点3答案错误&#xff0c;看参考书发现是&#xff0c;读题时根据硬币最大面值500设置的数组大小&#xff0c;但是后来会用总面值减去硬币面值&#xff0c;这个大小在[1,999)&#xff0c;因此散列表的大小应该设为1010。 2. 学会了一个小技巧。main函数可以不止一处r…