490 likes | 673 Views
VC++ 程序设计. 1 设备环境. Windows 使用一个称为设备环境 (DeviceContext) 的对象的方法 — 但该对象的创建没有一个 C++ 类,因为 Windows 早于并存在于 C++ 之外。 设备环境对象用一些默认值创建,可以用它们创建默认的设备环境。 应用 Windows 绘制一直线,所需的是一个设备环境对象的指针和直线坐标,设备环境对象的指针叫做设备环境句柄 (HDC) 。 ::MoveTo(HDC hdc, int x, int y); ::LineTo(HDC hdc, int x, int y);.
E N D
1设备环境 • Windows使用一个称为设备环境(DeviceContext)的对象的方法—但该对象的创建没有一个C++类,因为Windows早于并存在于C++之外。 • 设备环境对象用一些默认值创建,可以用它们创建默认的设备环境。 • 应用Windows绘制一直线,所需的是一个设备环境对象的指针和直线坐标,设备环境对象的指针叫做设备环境句柄(HDC)。 • ::MoveTo(HDC hdc, int x, int y); • ::LineTo(HDC hdc, int x, int y);
要绘制一个完整的形状,::MoveTo()函数只被调用一次。然后,::LineTo()函数被调用要绘制一个完整的形状,::MoveTo()函数只被调用一次。然后,::LineTo()函数被调用 • 设备无关 • 设备环境是设备无关的,它允许你编写软件而不必指定一个设备。这些软件必须知道的是:它是编写一个设备环境的,而不管该设备环境是屏幕还是Epson打印机。之后,设备环境可以被分配给一个打印机、屏幕、位图或一个文件。
2、在MFC环境中创建一个设备环境 • MFC用CDC类封装了一个设备环境对象,CDC的成员变量m_hdc指向它所拥有的设备环境,用户可以创建四种类型的设备环境 • 屏幕 • 打印机 • 内存 • 位图
2.1 屏 幕 • 创建一个屏幕设备环境,可以用: • CDC*pDC=GetDC(); • 这里的GetDC()是CWnd类的成员函数,它返回一个允许用户绘制它的窗口客户区的设备环境; • 如果还希望绘制非客户区,则使用: • CDC*pDC=GetWindowDC(); • 如果需要绘制整个屏幕 • CDC*pDC=CDC::FromHandle(::GetDC(NULL));
必须保证在用完设备环境后释放它,否则系统将发生冲突并导致异常终止。虽然设备环境是被创建的,但内存资源还是有限的必须保证在用完设备环境后释放它,否则系统将发生冲突并导致异常终止。虽然设备环境是被创建的,但内存资源还是有限的 • ReleaseDC(pDC); • 用户最有可能用通用设备环境来做绘图工作。每次释放一个通用设备环境时,为它们设置的值都被丢失。但是,可以用下面两个CDC成员函数保存和恢复一个设备环境的设置值: • SaveDC()保存设备环境的当前状态。 • RestoreDC()将设备环境恢复到用SaveDC()保存的状态。
2.2 打印机 • 要为打印机创建一个设备环境,必须先创建一个CDC类对象,然后使用它的CreateDC() • CDC dc; • dc.CreateDC(LPCTSTR lpszDriverName, LPCTSTR lpszDeviceName, LPCTSTR lpszOutput, Const void * lpInitData)
使用打印机设备环境之后,必须用CDC类的DeleteDC()成员函数销毁它。但是,若在堆栈中创建CDC类,则程序返回时,设备环境自动被删除。使用打印机设备环境之后,必须用CDC类的DeleteDC()成员函数销毁它。但是,若在堆栈中创建CDC类,则程序返回时,设备环境自动被删除。 • CMyClass::Drawing(.) { CDC dc; dc.CreateDC(.); }
2.3 内 存 • 要创建一个可以直接绘制位图的设备环境,必须先用CDC的CreateCompatibleDC()成员 • 函数创建一个CDC类对象。 CDC dc; dc.CreateCompatibleDC(HDC hdc); • CreateCompatibleDC()用设备环境的设置值初始化它所创建的设备环境对象,因此,取名兼容DC。应该从显示被创建的位图的设备(如屏幕、打印机)传递一个设备环境。
要真实地绘制一个位图,还必须创建一个空位图对象,并使该设备环境与该位图相关联。要真实地绘制一个位图,还必须创建一个空位图对象,并使该设备环境与该位图相关联。 • CDC dc; //create CD Cobject CBitmap bitmap; //create CBitmap object dc.CreateCompatibleDC(pDC);// bitmap.CreateCompatibleBitmap(pDC,ICON_WIDTH,ICON_HEIGHT); dc.SelectObject(&bitmap); //to this bitmap • 绘制位图以后,必须用下列语句删除设备环境: • dc.DeleteDC();
3绘图例程 • CDC类还在它的成员函数中封装了全部Windows图形API,这些成员函数包括: • 画点的函数。 • 画线的函数。 • 画形状的函数。 • 填充和翻转形状的函数。 • 滚动屏幕的函数。 • 绘制文本的函数。 • 绘制位图和图标的函数
3.1画点 • 像素点的绘制不过是改变单个像素点的颜色。 • SetPixel()试图用指定的颜色画一个像素,返回绘制时使用的实际颜色 • SetPixelV()与上面的基本相同,但不用返回绘制时使用的实际颜色,因而速度更快
3.2画线 • 画线即改变屏幕上一系列像素点的颜色。 • MoveTo()开始画线、弧和多边形时,把光标移动到一个初始位置 • LineTo()画一条从初始位置到另一个点的直线 • Arc()画一段弧 • ArcTo()画一段弧,并更新初始位置 • AngleArc()画一条线,然后画一段弧,并更新初始位置 • PolyDraw()画一系列线段和Bezier样条 • PolyLine()画一系列线段 • PolyPolyLine()画多个系列线条
3.3画形状 • 这里的形状是一系列封闭线条。 • Rectangle()画一个矩形 • Pie()画一个饼状楔形 • RoundRect()画一个圆角矩形 • Draw3dRect()画一个三维矩形 • Polygon()画一个多边形 • DrawEdge()画一个矩形的边缘 • PolyPolygon()创建一个或多个多边形DrawFrameControl()画一个框架控件 • Ellipse()画一个椭圆
3.4形状填充和翻转 • 填充和翻转改变一个形状内的所有像素点颜色。 • FillRect()填充一个矩形 • FillSolidRect()用用一单色填充一个矩形 • InvertRect()反转一个矩形的颜色 • ExtFloodFill()用用当前画刷填充一个区域,提供比FloodFill()成员函数更多的灵活性 • FrameRect()画一个矩形的边框
3.5滚动 • 通过滚动,可移动屏幕周围像素颜色。 • ScrollDC()左、右、上、下移动屏幕图像。
3.6绘制文本 • 我们可能会认为文本是打印的,不是画的;但在一个图形用户界面中,甚至一个文本字符都是由像素构成的一幅图。 • TextOut()在在一个指定的位置,输出一个字符串 • ExtTextOut()在在一个矩形区域里输出一个字符串 • TabbedTextOut()在基于用该函数传输的一个表,在指定位置输出一个字符串,并将字符串中的任何制表符转换为空格 • DrawText()在在指定的矩形域里绘制文本,但比TextOut()有更多的选项,如把文本居中和显示多行文本
3.7绘制位图和图标 • 一个位图或图标只是大量像素的颜色阵列。通常有一个标题,用来指示在一行中有多少像素点,以便一个画图例程知道什么时候开始下一行。通常,位图绘制例程只是把像素阵列拷贝到视频内存中。图标具有透明色这一附加的能力,换句话说,当一个图标被绘制在屏幕上时,它的每一点的颜色都可被屏幕上原有的颜色代替。 • DrawIcon()在在指定的位置画一个图标 • BitBlt()在从指定的设备环境中拷贝一个位图,通常是从磁盘中装入或在内存中创建, • StretchBlt()在与BitBlt()基本相同,但它试图伸展或压缩一个位图以适应目标 • PatBlt()在创建一个位模式
4 绘图属性 • 因为所有的绘图函数只有一个设备环境,因此,每个函数可能只使用存储在设备环境中20%的属性。 • 一个设备环境既可以自身包含一种特定的绘图属性,也可以指向另一个包含某种特定属性的对象。例如,画一条线的属性(如宽度或颜色)可存储在一个单独的Pen对象中,而由设备环境指向该对象。 • 创建这些附加的图形对象,与创建一个窗口对象的方法一样,先创建MFC类对象,然后调用那个类的成员函数Create()。附加的图形对象一旦被创建,需要告诉设备环境运用SelectObject()使用该新对象,SelectObject()只是把设备环境指向新的图形对象。
CPen pen; pen.CreatePen(.); CPen*pOldPen=dc.SelectObject(&pen); • 用新对象绘图后,必须从内存中删除该对象。因为设备环境仍指向该对象,所以,必须先把它指向另一个对象—通常是原先的对象。 • dc.SelectObject(pOldPen); • pen.DeleteObject()
7 控制什么时候在哪里绘图 • 通常,当处理一个WM_PAINT或WM_DRAWITEM窗口消息时,应当在窗口中绘制。当绘制窗口客户区的时候,由系统发送WM_PAINT窗口消息;当需绘制控件的某部分时,WM_DRAWITEM消息被发送到用户绘制控件的物主窗口。 • 也可以在其他时间画,但必须注意,下一次处理WM_PAINT消息时,无论画什么都将被绘图过程所覆盖。要想再在窗口绘图,只能作为鼠标命令的结果移动对象或线条,例如,在视图中,用鼠标绘制一个套索框框选取一组目标。 • 注意可以用CWnd::LockWindowUpdate()停止绘图过程绘制窗口。然而,这时该窗口就不能被移动了。
7.1处理WM_PAINT • 处理WM_PAINT消息的标准方法是: Void CMyWnd::OnPaint() { //creates a Device Contextdraw to dc CPaintDC dc(this); ::: } • 这里看到的CPaintDC类是CDC类的派生类。它不仅创建一个设备环境,而且在OnPaint()返回和CPaintDC析构自己时销毁该设备环境
7.2只绘制被无效化的区域 • 为了防止过度的屏幕闪烁并加快刷新速度,从CPaintDC获得的设备环境不总是允许绘制一个窗口的整个客户区,而只允许重画窗口中发生变化的部分(如一个文本文档的最后一行)。CPaintDC通知设备环境剪切掉在无效区域以外的任何绘图,这可以通过把被无效化的区域添加到设备环境的Region对象中实现;如果再调用,则Region对象定义一个剪切区域,在该区域以外不能进行绘制。 • InvalidateRect(rect); • Invalidate(); • UpdateWindow();
OnDraw() • 当对一个视图(用CView类或它的派生类创建的窗口)进行绘制时,应当在CView的OnDraw()函数中处理WM_PAINT消息,而不是在它的OnPaint()中。通过一个打印机设备环境调用OnDraw(),CView可以把视图打印到打印机上。当用户使用File菜单中的标准Print命令时,就是这样实现的。 • 但是,许多应用程序不执行OnDraw(),因为它们不直接使用CView;一些使用CFormView类创建的视图由通用控件组成,而通用控件不用OnDraw()画它们自己,但还是可以打印这种类型的视图,不过此时可进行屏幕捕捉而不能绘图
7.3在其他时间绘图 • 你可能在鼠标命令之后绘图,例如,当用鼠标来绘制一条直线时。 • 还可以使用另外两个CDC类的派生类,它们可以使工作轻松。CClientDC()类像CPaintDC()一样创建一个设备环境,但它不试图剪裁任何东西。当过程开始时,CClientDC类创建设备环境;而当过程结束时,释放它。 • CMyClass::Foo() { CClientDC dc(this); ::: }
注意在使用设备环境带来方便的同时,也可能会带来隐患。其中之一就是必须记住在使用之后必须释放它们,否则将承受应用程序慢慢使用完内存的痛苦。不用CDC*pDC=GetDC(),而养成使用CClientDC的习惯,可能会得到更好的服务,因为不必考虑释放设备环境。注意在使用设备环境带来方便的同时,也可能会带来隐患。其中之一就是必须记住在使用之后必须释放它们,否则将承受应用程序慢慢使用完内存的痛苦。不用CDC*pDC=GetDC(),而养成使用CClientDC的习惯,可能会得到更好的服务,因为不必考虑释放设备环境。 • CWindowDC()类与CClientDC()的目的相同,但是,它创建的设备环境允许对整个窗口进行绘制,其中包括非客户区。
例1 绘制图形 • 1.创建一个设备环境 • 1)如果处理一个WM_PAINT消息或其他类似的消息,则可以提供一个设备环境,如果没有提供,则必须自己创建一个;如果要绘制一个屏幕,可以用下面的代码创建一个设备环境,这里的pWnd是CWnd类的实例的指针。该类的实例应该拥有需要绘制的窗口。 • CDC*pDC=pWnd->GetDC(); • 2)如果创建一个自己的设备环境,用完后必须销毁它;否则,会发生另一种内存泄漏,称为资源泄漏。销毁一个设备环境,用: • pWnd->ReleaseDC(pDC);
2.创建一个画笔 1)根据画线所需的特征创建CPen类的一个实例。 CPen pen( PS_SOLID, //solid line also //PS_DASH,PS_DOT,PS_DASHDOT, //PS_DASHDOTDOT //PS_NULL 2, //width in pixels RGB(128,128,128)); //color
2)让设备环境指向该新画笔对象,但还要保存一个旧画笔的指针,以便以后能恢复它。2)让设备环境指向该新画笔对象,但还要保存一个旧画笔的指针,以便以后能恢复它。 • Cpen*pPen=pDC->SelectObject(&pen); • 另一个在设备环境中预定义的特征是填充色(用来绘制封闭图形内部的颜色),它与一个绘制封闭图形的函数一起使用;默认的颜色是白色,但通过告知设备环境使用一个新的画刷对象,可以改变填充颜色。
3.创建一个画刷 1)用需要的颜色创建CBrush类的一个实例。 CBrush brush(RGB(128,128,128)); 2)让设备环境指向该新画刷对象,但还要保存旧对象的指针,以便以后能恢复它。 CBrush*pBrush= pDC->SelectObject(&brush);
4.用CDC类成员函数绘制图形 • 1)用该设备环境画一条直线,用: pDC->MoveTo(5,5); pDC->LineTo(25,25); • 2)绘制一个矩形,用: pDC->Rectangle(CRect(5,55,50,85)); • 3)绘制弧,用: pDC->Arc(CRect(5,115,50,145), CPoint(5,115), CPoint(50,115));
4)绘制圆角矩形,用: pDC->RoundRect(CRect(5,185,50,215), CPoint(15,15)); //distance from corner to draw arc 5)绘制椭圆或圆,用: pDC->Ellipse(CRect(250,5,305,25)); 6)绘制饼图,用: pDC->Pie(CRect(250,55,305,85) CPoint(250,55), //starting point CPoint(305,55)); //end point
例2 绘制文本 • 如果不存在设备环境,则用前面例子中介绍的技术创建一个。一种常见的方法如下: • CDC*pDC=pWnd->GetDC(); • 1.使用TextOut() • 1)绘制一个文本串,用: • CString str("Thisisdrawntext"); pDC->TextOut(x,y,str,str.getLength());
x和y变量定义文本位置的左上角,如果要x和y指示别的位置,如文本的中心位置,可以用CDC::SetTextAlign()改变x和y的含义。x和y变量定义文本位置的左上角,如果要x和y指示别的位置,如文本的中心位置,可以用CDC::SetTextAlign()改变x和y的含义。 • 2)使x和y代表文本中央位置,在调用TextOut()之前调用下面的函数: • pDC->SetTextAlign(TA_CENTER);) • 也可以用下面的函数,改变y对齐方式: • pDC->SetTextAlign(TA_BOTTOM);) • 3)用不同的标准字形绘制文本,在绘制前使用下面的函数调用(参见MFC文档有关他库存字体): • pDC->SelectStockObject(ANSI_VAR_FONT);
4)创建绘制文本的字体,可以用: • CFontfont; • font.CreateFont(-22,0,0,0,FW_NORMAL, 0, 0, 0, 0,0,0,0,0,"Courier"); • CFont*pFont=(CFont*)pDC->SelectObject(&font); • CreateFont()不是真正地创建一种字体,相反它扫描系统当前安装的字体,寻找一种与用户调用参数中指定的标准最匹配的字体,并使用这种字体。
5)改变窗口的默认字体,可以用: • pwnd->SetFont(pFont);//thefont • 默认字体被自动地选进从那个窗口创建的任一设备环境中。 • 6)改变文本的颜色,用: • pDC->SetTextColor(RGB(100,100,100)); • 7)改变文本的背景色,用: • pDC->SetBkColor(RGB(200,200,200)); • 除非背景模式设为不透明,否则背景色被忽略。不透明意味着在绘制文本前,先绘制背景矩形;透明模式意味着文本被绘制在当前背景之上。
8)打开不透明背景模式,用: • pDC->SetBkMode(OPAQUE); • 9)打开透明背景模式,用: • pDC->SetBkMode(TRANSPARENT);
2.使用DrawText() 用DrawText()绘制文本,用: pDC->DrawText(Str, rect,//a bounding rectangle DT_CENTER);//analignment centered 其他文本绘制函数包括: ExtTextOut(),该函数裁剪给定矩形外的绘制文本。 TabbedTextOut(),使用用户提供给该函数的跳格键位置表,扩大插入文本中的跳格距离。 DrawState(),用来绘制无效文本,该文本看起来被蚀刻一样。
例3图标绘制 • 用三种不同的方法装入一个图标。 • 第一种方法,使用一个称为LoadIcon()的应用程序类的成员函数,它从应用程序的资源中装入一个图标; • 第二种方法,用WindowAPI函数LoadImage()直接从一个磁盘文件中装入一个图标; • 第三种方法,用WindowsAPI函数ExtractIcon()从另一个应用程序的可执行文件中抽出一个图标。
1.从应用程序的资源中装入一个图标 • 装入一个在应用程序资源中定义的图标,用: HICON hIcon; hIcon=AfxGetApp()->LoadIcon(IDR_MAINFRAME);
2.直接从一个.ico磁盘文件中装入一个图标 从一个.ico文件装入一个图标,使用下面的方法。本例从Wzd.ico装入一个图标。 hicon=(HICON)LoadImage( NULL, “Wzd.ico”, IMAGE_ICON, IMAGE_CURSOR //or IMAGE_ICON 0,0, //width and height LR_LOADFROMFILE); //loadflags
3.从一个DLL或.exe文件中装入一个图标 • 从另一个应用程序的可执行文件中抽取一个图标,可以用下面的方法。本例中抽取在Wzd.exe中发现的第二个图标。 HINSTANCE hinst=AfxGetInstanceHandle(); hicon=ExtractIcon(hinst, "Debug\\wzd.exe", 1);
4.绘制一个图标 • 用下面的方法可以把一个图标绘制到任何窗口。这里的(0,0)是图标的左上角坐标。 • pDC->DrawIcon(0,0,hicon); • 5.销毁一个图标 • 必须手工销毁任何一个装入的或者用LoadImage()装入或ExtractIcon()抽取的图标,以避免资源内存泄漏。 • DestroyIcon(hicon);
例4装入一个位图和绘制一个位图 • 用CBitmap类装入一个在应用程序资源中定义的位图; • 用WindowsAPI函数LoadImage()从一个.bmp文件中装入一个位图添加位图到应用程序的资源中 • 有两种把位图装入到工程中的方法。第一种,用DeveloperStudio的位图编辑器创建一个位图;第二种,用DeveloperStudio的insert/ InsertResource菜单命令下的Import命令装入一个位图。注意分配给位图的ID。
2.从应用程序的资源中装入位图 • 要把位图装入应用程序中进行绘制,用下面的方法,可以用自己的位图ID替代IDB_WZD。 • CBitmap bitmap; • bitmap.LoadBitmap(IDB_WZD);
3.从一个.bmp文件中装入位图 在运行时用LoadImage()装入一个位图。 CBitmap bitmap; HBITMAP hbitmap=(HBITMAP)::LoadImage( NULL, "Wzd2.bmp", //name or identifier of image IMAGE_BITMAP, //type of image- 0,0, //desired width and height LR_LOADFROMFILE); //load from file bitmap.Attach(hbitmap);
4.绘制一个位图 绘制一个位图可以用下面的方法,注意用BitBlt()需要两个设备环境,而不是一个。 CDC dcComp; dcComp.CreateCompatibleDC(pDC); dcComp.SelectObject(&bitmap); //get size of bitmap for BitBlt() BITMAP bmInfo; bitmap.GetObject(sizeof(bmInfo),&bmInfo); //use BitBlt() to draw bitmap pDC->BitBlt(0,0,bmInfo.bmWidth, bmInfo.bmHeight,&dcComp,0,0,SRCCOPY);