📄 vc2008_6_10.txt
字号:
● DRAFT_QUALITY Appearance of the font is less important than when PROOF_QUALITY is used. For GDI raster fonts, scaling is enabled. Bold, italic, underline, and strikeout fonts are synthesized if necessary.
● PROOF_QUALITY Character quality of the font is more important than exact matching of the logical-font attributes. For GDI raster fonts, scaling is disabled and the font closest in size is chosen. Bold, italic, underline, and strikeout fonts are synthesized if necessary.
nPitchAndFamily 字体间的间距
lpszFacename 指定字体名称,为了得到系统所拥有的字体可以利用EmunFontFamiliesEx。(可以参考文章在同一系统中显示GB字符和BIG5字符)
此外可以利用CFontDialog来得到用户选择的字体的LOGFONT数据。
最后我讲一下文本坐标的计算,利用CDC::GetTextExtent( const CString& str )可以得到字符串的在输出时所占用的宽度和高度,这样就可以在手工输出多行文字时使用正确的行距。另外如果需要更精确的对字体高度和宽度进行计算就需要使用CDC::GetTextMetrics( LPTEXTMETRIC lpMetrics ) 该函数将会填充TEXTMETRIC结构,该结构中的分量可以非常精确的描述字体的各种属性。
2.3 使用点、刷子、笔进行绘图
在Windows中画点的方法很简单,只需要调用COLORREF CDC::SetPixel( int x, int y, COLORREF crColor )就可以在指定点画上指定颜色,同时返回原来的颜色。COLORREF CDC::GetPixel( int x, int y)可以得到指定点的颜色。在Windows中应该少使用画点的函数,因为这样做的执行效率比较低。
刷子和画笔在Windows作图中是使用最多的GUI对象,本节在讲解刷子和画笔使用方法的同时也讲述一写基本作图函数。
在画点或画线时系统使用当前DC中的画笔,所以在创建画笔后必须将其选入DC才会在绘图时产生效果。画笔可以通过CPen对象来产生,通过调用CPen::CreatePen( int nPenStyle, int nWidth, COLORREF crColor )来创建。其中nPenStyle指名画笔的风格,可取如下值:
● PS_SOLID 实线 Creates a solid pen.
● PS_DASH 虚线,宽度必须为一 Creates a dashed pen. Valid only when the pen width is 1 or less, in device units.
● PS_DOT 点线,宽度必须为一 Creates a dotted pen. Valid only when the pen width is 1 or less, in device units.
● PS_DASHDOT 点划线,宽度必须为一 Creates a pen with alternating dashes and dots. Valid only when the pen width is 1 or less, in device units.
● PS_DASHDOTDOT 双点划线,宽度必须为一 Creates a pen with alternating dashes and double dots. Valid only when the pen width is 1 or less, in device units.
● PS_NULL 空线,使用时什么也不会产生 Creates a null pen.
● PS_ENDCAP_ROUND 结束处为圆形 End caps are round.
● PS_ENDCAP_SQUARE 结束处为方形 End caps are square.
nWidth和crColor为线的宽度和颜色。
刷子是在画封闭曲线时用来填充的颜色,例如当你画圆形或方形时系统会用当前的刷子对内部进行填充。刷子可利用CBrush对象产生。通过以下几种函数创建刷子:
● BOOL CreateSolidBrush( COLORREF crColor ); 创建一种固定颜色的刷子
● BOOL CreateHatchBrush( int nIndex, COLORREF crColor ); 创建指定颜色和网格的刷子,nIndex可取以下值:
● HS_BDIAGONAL Downward hatch (left to right) at 45 degrees
● HS_CROSS Horizontal and vertical crosshatch
● HS_DIAGCROSS Crosshatch at 45 degrees
● HS_FDIAGONAL Upward hatch (left to right) at 45 degrees
● HS_HORIZONTAL Horizontal hatch
● HS_VERTICAL Vertical hatch
● BOOL CreatePatternBrush( CBitmap* pBitmap ); 创建以8*8位图为模板的刷子
在选择了画笔和刷子后就可以利用Windows的作图函数进行作图了,基本的画线函数有以下几种
● CDC::MoveTo( int x, int y ); 改变当前点的位置
● CDC::LineTo( int x, int y ); 画一条由当前点到参数指定点的线
● CDC::BOOL Arc( LPCRECT lpRect, POINT ptStart, POINT ptEnd ); 画弧线
● CDC::BOOL Polyline( LPPOINT lpPoints, int nCount ); 将多条线依次序连接
基本的作图函数有以下几种:
● CDC::BOOL Rectangle( LPCRECT lpRect ); 矩形
● CDC::RoundRect( LPCRECT lpRect, POINT point ); 圆角矩形
● CDC::Draw3dRect( int x, int y, int cx, int cy, COLORREF clrTopLeft, COLORREF clrBottomRight ); 3D边框
● CDC::Chord( LPCRECT lpRect, POINT ptStart, POINT ptEnd ); 扇形
● CDC::Ellipse( LPCRECT lpRect ); 椭圆形
● CDC::Pie( LPCRECT lpRect, POINT ptStart, POINT ptEnd );
● CDC::Polygon( LPPOINT lpPoints, int nCount ); 多边形
对于矩形,圆形或类似的封闭曲线,系统会使用画笔绘制边缘,使用刷子填充内部。如果你不希望填充或是画出边缘,你可以选入空刷子(NULL_PEN)或是(NULL_BRUSH)空笔。
下面的代码创建一条两象素宽的实线并选入DC。并进行简单的作图:
{
...
CPen pen;
pen.CreatePen(PS_SOLID,2,RGB(128,128,128));
CPen* pOldPen=(CPen*)dc.SelectObject(&pen);
dc.SelectStockObject(NULL_BRUSH);//选入空刷子
dc.Rectangle(CRect(0,0,20,20));//画矩形
...
}
2.4 在窗口中绘制设备相关位图、图标、设备无关位图
在Windows中可以将预先准备好的图像复制到显示区域中,这种内存拷贝执行起来是非常快的。在Windows中提供了两种使用图形拷贝的方法:通过设备相关位图(DDB)和设备无关位图(DIB)。
DDB可以用MFC中的CBitmap来表示,而DDB一般是存储在资源文件中,在加载时只需要通过资源ID号就可以将图形装入。BOOL CBitmap::LoadBitmap( UINT nIDResource )可以装入指定DDB,但是在绘制时必须借助另一个和当前绘图DC兼容的内存DC来进行。通过CDC::BitBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop )绘制图形,同时指定光栅操作的类型。BitBlt可以将源DC中位图复制到目的DC中,其中前四个参数为目的区域的坐标,接下来是源DC指针,然后是源DC中的起始坐标,由于BitBlt为等比例复制,所以不需要再次指定长宽,(StretchBlt可以进行缩放)最后一个参数为光栅操作的类型,可取以下值:
● BLACKNESS 输出区域为黑色 Turns all output black.
● DSTINVERT 反色输出区域 Inverts the destination bitmap.
● MERGECOPY 在源和目的间使用AND操作 Combines the pattern and the source bitmap using the Boolean AND operator.
● MERGEPAINT 在反色后的目的和源间使用OR操作 Combines the inverted source bitmap with the destination bitmap using the Boolean OR operator.
● NOTSRCCOPY 将反色后的源拷贝到目的区 Copies the inverted source bitmap to the destination.
● PATINVERT 源和目的间进行XOR操作 Combines the destination bitmap with the pattern using the Boolean XOR operator.
● SRCAND 源和目的间进行AND操作 Combines pixels of the destination and source bitmaps using the Boolean AND operator.
● SRCCOPY 复制源到目的区 Copies the source bitmap to the destination bitmap.
● SRCINVERT 源和目的间进行XOR操作 Combines pixels of the destination and source bitmaps using the Boolean XOR operator.
● SRCPAINT 源和目的间进行OR操作 Combines pixels of the destination and source bitmaps using the Boolean OR operator.
● WHITENESS 输出区域为白色 Turns all output white.
下面用代码演示这种方法:
CYourView::OnDraw(CDC* pDC)
{
CDC memDC;//定义一个兼容DC
memDC.CreateCompatibleDC(pDC);//创建DC
CBitmap bmpDraw;
bmpDraw.LoadBitmap(ID_BMP);//装入DDB
CBitmap* pbmpOld=memDC.SelectObject(&bmpDraw);//保存原有DDB,并选入新DDB入DC
pDC->BitBlt(0,0,20,20,&memDC,0,0,SRCCOPY);//将源DC中(0,0,20,20)复制到目的DC(0,0,20,20)
pDC->BitBlt(20,20,40,40,&memDC,0,0,SRCAND);//将源DC中(0,0,20,20)和目的DC(20,20,40,40)中区域进行AND操作
memDC.SelectObject(pbmpOld);//选入原DDB
}
(图标并不是一个GDI对象,所以不需要选入DC)在MFC中没有一个专门的图标类,因为图标的操作比较简单,使用HICON CWinApp::LoadIcon( UINT nIDResource )或是HICON CWinApp::LoadStandardIcon( LPCTSTR lpszIconName ) 装入后就可以利用BOOL CDC::DrawIcon( int x, int y, HICON hIcon )绘制。由于在图标中可以指定透明区域,所以在某些需要使用非规则图形而且面积不大的时候使用图标会比较简单。下面给出简单的代码:
OnDraw(CDC* pDC)
{
HICON hIcon1=AfxGetApp()->LoadIcon(IDI_I1);
HICON hIcon2=AfxGetApp()->LoadIcon(IDI_I2);
pDC->DrawIcon(0,0,hIcon1);
pDC->DrawIcon(0,40,hIcon2);
DestroyIcon(hIcon1);
DestroyIcon(hIcon2);
}
同样在MFC也没有提供一个DIB的类,所以在使用DIB位图时我们需要自己读取位图文件中的头信息,并读入数据,并利用API函数StretchDIBits绘制。位图文件以BITMAPFILEHEADER结构开始,然后是BITMAPINFOHEADER结构和调色版信息和数据,其实位图格式是图形格式中最简单的一种,而且也是Windows可以理解的一种。我不详细讲解DIB位图的结构,提供一个CDib类供大家使用,这个类包含了基本的功能如:Load,Save,Draw。DownLoad CDib 4K
2.5 使用各种映射方式
所谓的映射方式简单点讲就是坐标的安排方式,系统默认的映射方式为MM_TEXT即X坐标向右增加,Y坐标向下增加,(0,0)在屏幕左上方,DC中的每一点就是屏幕上的一个象素。也许你会认为这种方式下是最好理解的,但是一个点和象素对应的关系在屏幕上看来是正常的,但到了打印机上就会很不正常。因为我们作图是以点为单位并且打印机的分辨率远远比显示器高(800DPI 800点每英寸)所以在打印机上图形看起来就会很小。这样就需要为打印另做一套代码而加大了工作量。如果每个点对应0.1毫米那么在屏幕上的图形就会和打印出来的图形一样大小。
通过int CDC::SetMapMode( int nMapMode )可以指定映射方式,可用的有以下几种:
● MM_HIENGLISH 每点对应0.001英寸 Each logical unit is converted to 0.001 inch. Positive x is to the right; positive y is up.
● MM_HIMETRIC 每点对应0.001毫米 Each logical unit is converted to 0.01 millimeter. Positive x is to the right; positive y is up.
● MM_LOENGLISH 每点对应0.01英寸 Each logical unit is converted to 0.01 inch. Positive x is to the right; positive y is up.
● MM_LOMETRIC 每点对应0.001毫米 Each logical unit is converted to 0.1 millimeter. Positive x is to the right; positive y is up.
● MM_TEXT 象素对应 Each logical unit is converted to 1 device pixel. Positive x is to the right; positive y is down.
以上几种映射默认的原点在屏幕左上方。除MM_TEXT外都为X坐标向右增加,Y坐标向上增加,和自然坐标是一致的。所以在作图是要注意什么时候应该使用负坐标。而且以上的映射都是X-Y等比例的,即相同的长度在X,Y轴上显示的长度都是相同的。观看不同映射效果图 DownLoad Sample
另外的一种映射方式为MM_ANISOTROPIC,这种方式可以规定不同的长宽比例。在设置这中映射方式后必须调用CSize CDC::SetWindowExt( SIZE size )和CSize CDC::SetViewportExt( SIZE size )来设定长宽比例。系统会根据两次设定的长宽的比值来确定长宽比例。下面给出一段代码比较映射前后的长宽比例:
OnDraw(CDC* pDC)
{
CRect rcC1(200,0,400,200);
pDC->FillSolidRect(rcC1,RGB(0,0,255));
pDC->SetMapMode(MM_ANISOTROPIC );
CSize sizeO;
sizeO=pDC->SetWindowExt(5,5);
TRACE("winExt %d %d\n",sizeO.cx,sizeO.cy);
sizeO=pDC->SetViewportExt(5,10);
TRACE("ViewExt %d %d\n",sizeO.cx,sizeO.cy);
CRect rcC(0,0,200,200);
pDC->FillSolidRect(rcC,RGB(0,128,0));
}
上面代码在映射后画出的图形将是一个长方形。观看效果图 DownLoad Sample
最后讲讲视原点(viewport origin),你可以通过调用CPoint CDC::SetViewportOrg( POINT point )重新设置原点的位置,这就相对于对坐标进行了位移。例如你将原点设置在(20,20)那么原来的(0,0)就变成了(-20,-20)。
2.6 多边形和剪贴区域
多边形也是一个GDI对象,同样遵守其他GDI对象的规则,只是通常都不将其选入DC中。在MFC中多边形有CRgn表示。多边形用来表示一个不同与矩形的区域,和矩形具有相似的操作。如:检测某点是否在内部,并操作等。此外还得到一个包含此多边形的最小矩形。下面介绍一下多边形类的成员函数:
● CreateRectRgn 由矩形创建一个多边形
● CreateEllipticRgn 由椭圆创建一个多边形
● CreatePolygonRgn 创建一个有多个点围成的多边形
● PtInRegion 某点是否在内部
● CombineRgn 两个多边形相并
● EqualRgn 两个多边形是否相等
在本节中讲演多边形的意义在于重新在窗口中作图时提高效率。因为引发窗口重绘的原因是某个区域失效,而失效的区域用多边形来表示。假设窗口大小为500*400当上方的另一个窗口从(0,0,10,10)移动到(20,20,30,30)这时(0,0,10,10)区域就失效了,而你只需要重绘这部分区域而不是所有区域,这样你程序的执行效率就会提高。
通过调用API函数int GetClipRgn( HDC hdc, HRGN hrgn)就可以得到失效区域,但是一般用不着那么精确而只需得到包含该区域的最小矩形就可以了,所以可以利用int CDC::GetClipBox( LPRECT lpRect )完成这一功能。
第三章 文档视结构
3.1 文档 视图 框架窗口间的关系和消息传送规律
在MFC中M$引入了文档-视结构的概念,文档相当于数据容器,视相当于查看数据的窗口或是和数据发生交互的窗口。(这一结构在MFC中的OLE,ODBC开发时又得到更多的拓展)因此一个完整的应用一般由四个类组成:CWinApp应用类,CFrameWnd窗口框架类,CDocument文档类,CView视类。(VC6中支持创建不带文档-视的应用)
在程序运行时CWinApp将创建一个CFrameWnd框架窗口实例,而框架窗口将创建文档模板,然后有文档模板创建文档实例和视实例,并将两者关联。一般来讲我们只需对文档和视进行操作,框架的各种行为已经被MFC安排好了而不需人为干预,这也是M$设计文档-视结构的本意,让我们将注意力放在完成任务上而从界面编写中解放出来。
在应用中一个视对应一个文档,但一个文档可以包含多个视。一个应用中只用一个框架窗口,对多文档界面来讲可能有多个MDI子窗口。每一个视都是一个子窗口,在单文档界面中父窗口即是框架窗口,在多文档界面中父窗口为MDI子窗口。一个多文档应用中可以包含多个文档模板,一个模板定义了一个文档和一个或多个视之间的对应关系。同一个文档可以属于多个模板,但一个模板中只允许定义一个文档。同样一个视也可以属于多个文档模板。(不知道我说清楚没有)
接下来看看如何在程序中得到各种对象的指针:
● 全局函数AfxGetApp可以得到CWinApp应用类指针
● AfxGetApp()->m_pMainWnd为框架窗口指针
● 在框架窗口中:CFrameWnd::GetActiveDocument得到当前活动文档指针
● 在框架窗口中:CFrameWnd::GetActiveView得到当前活动视指针
● 在视中:CView::GetDocument得到对应的文档指针
● 在文档中:CDocument::GetFirstViewPosition,CDocument::GetNextView用来遍历所有和文档关联的视。
● 在文档中:CDocument::GetDocTemplate得到文档模板指针
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -