所有CDC输出函数最终都会输出到物理平面(屏幕窗口、打印纸等)。这些物理平面的单位量化往往多种多样,比如像素、打印点、英寸、毫米等等。这样可能会造成很多混乱,所以CDC输出对所有物理平面进行统一抽象化为“逻辑平面”。所有CDC输出函数都是在逻辑平面输出。物理平面在CDC又称为设备平面。

所有CDC输出函数最终都是输出“点”。GDI使用坐标来抽象化表示这些“点”。逻辑平面和设备平面都使用直角坐标系作为输出依据。在逻辑平面上所有长度单位和面积单位都是纯粹的数学上的代数“1”,没有实际的物理单位(比如毫米、英寸、平方米等等)。

坐标是对CDC指定的设备进行“等分”用的。具体就是逻辑坐标是对逻辑平面进行逻辑等分,设备坐标是对设备平面进行设备等分。

逻辑坐标(Logical Coordinate)所在的平面本来该称为“逻辑平面”,但是最初系统开发者却用错单词,使用“窗口Window”。设备坐标(Device Coordinate)所在的平面本来该称为“设备平面”,但是最初系统开发者却用错单词,使用“视口Viewport”。这是CDC绘图的第1个难点。

画图在逻辑坐标中画,使用逻辑单位。显示图形在设备坐标中显示,使用设备坐标。

逻辑坐标上面的点叫做“逻辑点、数学点、几何点”

设备坐标上面的点叫做“设备点、物理点(比如显示点、屏幕点、打印点)”

点的位置用x和y两个坐标表示,x表示横坐标,y表示纵坐标。

GDI包括以下3种设备坐标:

(1)客户区坐标。框架窗口、对话框的客户区域(Client)。

(2)全窗口坐标。一般都会把“全”字省略。包括一个程序的整个窗口,包括标题条、菜单、滚动条、状态区和边框。使用GetWindowDC得到的全窗口设备环境。

(3)屏幕坐标。用函数ClientToScreen和ScreenToClient可以将客户区坐标转换成屏幕坐标,或反之。

客户区、全窗口、屏幕的左上角的点固定为(0,0)。这3种设备坐标都跟“显示”有直接关系。以后还有跟打印有关的设备坐标。

设备(物理)平面分为显示平面和打印平面。显示平面跟显示器等显示设备有关,打印平面跟打印机(包含虚拟打印机)等打印设备有关。

虽然经过抽象化使得CDC输出变得容易(逻辑平面单位单一),但是最后还是要输出要设备平面上。设备平面多种多样,对应的单位多种多样,很复杂。这样要求需要一种转换办法来处理逻辑平面到设备平面的差异。这种转换办法称为“映射”。映像指逻辑坐标与设备坐标之间的转换办法。比例映像模式本身也是设备环境的属性之一。

用SetMapMode函数设置新比例映像模式。

用GetMapMode函数获得当前比例映像模式。

MFC提供了8种比例映像模式,如下图

映像模式

1个逻辑单位

对应的设备单位

X轴正向方向 Y轴正向方向
MM_TEXT 1像素 右 下
MM_LOMETRIC 0.1mm 右 上
MM_HIMETRIC 0.01mm 右 上
MM_LOENGLISH 0.01in 右 上
MM_HIENGLISH 0.001in 右 上
MM_TWIPS 1/1440in 右 上
MM_ISOTROPIC 用户自定义x=y 用户自定义 用户自定义
MM_ANISOTROPIC 用户自定义x!=y 用户自定义 用户自定义

GDI包括以下2类3种比例映像模式:

①默认比例映像模式:

当不明确使用哪一种比例映像模式时候,GDI将会使用默认的映像比例映像模式——MM_TEXT。这个模式含义是:逻辑坐标和设备坐标是1:1的比例输出,也就是1个逻辑点正好就是1个设备点。这是最简单、易于理解的比例映像模式。如果输出平面是屏幕则1逻辑单位就是1像素点;如果是打印平面则1逻辑单位就是就是一个打印点。

在MM_TEXT模式下:

坐标轴向右方向是正方向

坐标轴向左方向是负方向

坐标轴向上方向是负方向

坐标轴向下方向是正方向

逻辑平面(0,0)的点永远与设备平面(0,0)的点是重合。这样最简单的绘图代码如下:

//main.h的内容

class CMyApp:public CWinApp

{

public:

  virtual BOOL InitInstance();

};

class CMainWindow:public CFrameWnd

{

public:

  CMainWindow();

protected:

  afx_msg void OnPaint();

  DECLARE_MESSAGE_MAP();

};

//main.cpp的内容

#include <afxwin.h>

#include "main.h"

CMyApp myApp;

BOOL CMyApp::InitInstance()

{

  Enable3dControls();

  m_pMainWnd=new CMainWindow;

  m_pMainWnd->ShowWindow(m_nCmdShow);

  m_pMainWnd->UpdateWindow();

  return TRUE;

}

BEGIN_MESSAGE_MAP(CMainWindow,CFrameWnd)

  ON_WM_PAINT()

END_MESSAGE_MAP()

CMainWindow::CMainWindow()

{

  Create(NULL,_T("MFC GDI"),WS_OVERLAPPEDWINDOW,CRect(200,200,350,350));

}

void CMainWindow::OnPaint()

{

  CPaintDC dc(this);

  dc.Ellipse(-100,-100,100,100);

}

代码“Ellipse(-100,-100,100,100);”的含义是绘制一个圆,但是窗口的客户区只显示右下角的1/4圆。这是坐标轴的负方向被屏蔽的原因。这时候可以用以下代码显示完整圆:

//main.cpp的内

//…….省略

CMainWindow::CMainWindow()

{

,));

}

void CMainWindow::OnPaint()

{

  CPaintDC dc(this);

  dc.SetViewportOrg(100,100);

  dc.Ellipse(-100,-100,100,100);

}

画出的圆如下:

以上代码有2处红字:

①上面Create函数的参数里面的改变是为了让窗口右边和下面有足够空间容纳圆。

②下面SetViewportOrg函数是改变设备平面“对齐点”的坐标。

所谓的“对齐点”是指逻辑平面映像到设备平面必须在2个平面里面各找一个固定点并对齐。只有这样“映射”才有实际意义。SetViewportOrg函数后面“Org”是“original point”的缩写,该单词意思是“原点”。“Viewport”本意是“视口”,这里应该是“Device”“设备”才适合。这是系统设计时候选单词的失误。绝大部分翻译都是如实翻译成“原点”,这样导致很多人错误理解。应该用类似“align”才对,比如SetDeviceAlignPoint之类。这是CDC绘图的第2个难点。

第2个也可以使用SetWindowOrg函数是改变逻辑平面“对齐点”的坐标。这个函数跟SetViewportOrg函数相似的问题。建议换成SetLogicalAlignPoint之类。

如果使用SetWindowOrg函数则代码如下:

dc.SetWindowOrg(-100,-100);

这样也可以达到同样效果。

②度量比例映像模式:

这类映像模式有5种,如下:

MM_LOMETRIC

MM_HIMETRIC

MM_LOENGLISH

MM_HIENGLISH

MM_TWIPS

他们的共同点是:

坐标轴向右方向是正方向

坐标轴向左方向是负方向

坐标轴向上方向是正方向

坐标轴向下方向是负方向

度量比例映像模式与默认比例映像模式的共同点:

逻辑平面原点(0,0)的点永远与设备平面原点(0,0)的点是重合。

不同点:

坐标轴在垂直方向是相反的。

这里以MM_LOENGLISH为例解释英制度量的影射模式。代码如下:

//main.cpp的内容

//…….省略

void CMainWindow::OnPaint()

{

  CPaintDC dc(this);

  dc.SetMapMode(MM_LOENGLISH);

  dc.Ellipse(0,0,100,-100);

}

画出的圆如下:

用系统自带的“画图”工具打开该图像,用放大镜放大6倍。选中铅笔工具,把铅笔分别移动到圆的最上方和最下方。这样会得到垂直坐标分别为32和158。这样圆的直径是127像素。在MM_LOENGLISH模式下,100逻辑单位就是1英寸,而系统有一个“正常尺寸(96dpi)”。这样显然不一样。GDI内部实际上使用的是126dpi。这实际上是系统设计bug。需要注意。

这里以MM_LOMETRIC为例解释公制度量的影射模式。代码如下:

//main.cpp的内容

//…….省略

CMainWindow::CMainWindow()

{

,));

}

void CMainWindow::OnPaint()

{

  CPaintDC dc(this);

  dc.SetMapMode(MM_LOMETRIC);

  dc.Ellipse(0,0,100,-100);

}

画出的圆如下:

用系统自带的“画图”工具打开该图像,用放大镜放大6倍。选中铅笔工具,把铅笔分别移动到圆的最上方和最下方。这样会得到垂直坐标分别为32和81。这样圆的直径是50像素。在MM_LOMETRIC模式下,100逻辑单位就是1cm=50像素。而1英寸=2.54cm=127像素。

MM_TWIPS也类似,代码如下:

//main.cpp的内容

//…….省略

CMainWindow::CMainWindow()

{

,));

}

void CMainWindow::OnPaint()

{

  CPaintDC dc(this);

  dc.SetMapMode(MM_TWIPS);

  dc.Ellipse(0,0,1440,-1440);

}

画出的圆如下:

用系统自带的“画图”工具打开该图像,用放大镜放大6倍。选中铅笔工具,把铅笔分别移动到圆的最上方和最下方。这样会得到垂直坐标分别为32和158。这样圆的直径是127像素。在MM_TWIPS模式下,1440逻辑单位就是1英寸,而1英寸=127像素。

可见,在GDI的CDC输出函数中,实际遵守127dpi而不是系统默认的96dpi。

③自定义比例映像模式

这类模式跟前面两类模式的差异是“自定义”可以改变逻辑范围和设备范围。

改变“范围”使用SetWindowExt函数和SetViewportExt函数。函数的后缀“Ext”是extent(范围、距离、长度)的缩写。

SetWindowExt是用逻辑坐标进行等分。SetViewportEx是用设备(物理)坐标进行设备(物理)等分。

当使用MM_ISOTROPIC或MM_ANISOTROPIC模式时候,必须2个函数都使用,不然可能会出现不可预知的错误或者任何CDC输出函数都会无效(没有任何像素输出)。

所谓“范围”跟设备(物理)平面有直接关系。比如一个屏幕分辨率是1024*768,那么SetViewportExt(1024*768) 意味着水平设备范围就是分成1024等分,垂直设备范围就是分成768等分。

“范围”跟逻辑平面有间接关系。当使用CDC输出函数时候定义多少逻辑单位可以铺满窗口,比如SetWindowExt(100,200)意味着水平100逻辑单位,垂直200逻辑单位可以铺满窗口。

这2个函数的Window和Viewport跟前面的“对齐点”有着一样的问题。

在《Windows程序设计》中文版P147里面有一个公式:

这个公式的原型其实是如下:

这样会有2个问题:

①窗口大小和客户区大小可能会不断变化而不稳定。这样就必须一直改变xWinExt和xViewExt以适应这样的变化。

②公式里面隐藏不可回避的乘法运算。把对齐点都设为0,这样最简化公式,如下:

公式里面所有变量实际上都是有符号整数。如此,乘法运算便是必须面对可能的积过大而溢出。

对于第1个问题的解决办法:对范围重新定义,不再是所谓的“等分”。从或可知关键之处在于“比例的值”。因此可以都除以最大公约数以最简化,这样结果不变。

对于第2个问题的解决办法:在现实中,对齐点坐标和目标点坐标往往是不可改变的,因此只能改变范围。建议范围用都除以最大公约数,这样也会得到一样的结果。

在《Windows程序设计》中文版P148最上面段落的最后那句“每个范围本身没有多大意义,但是逻辑单位转换为设备单位的换算因子是视口范围和窗口范围的比例”。这说明GDI设计者已经发现这个问题,并按照以上2点做出解决办法。

总结:逻辑范围和设备范围实际是逻辑坐标和设备坐标的比例放大或缩小的作用。

这类模式有2种:MM_ISOTROPIC和MM_ANISOTROPIC。其中MM_ANISOTROPIC是原始形态。MM_ISOTROPIC是变种,简化前面的功能。个人建议不使用MM_ISOTROPIC。

在使用MM_ISOTROPIC或MM_ANISOTROPIC模式之后许使用SetViewportExt函数和SetWindowExt函数才能得到正确结果,否则可能会有不可预支错误发生或者绘画函数没有任何效果。

SetViewportExt函数和SetWindowExt函数的原型如下:

virtual CSize SetViewportExt( int cx, int cy );    //设备单位

virtual CSize SetViewportExt( SIZE size );      //设备单位

virtual CSize SetWindowExt( int cx, int cy );     //逻辑单位

virtual CSize SetWindowExt( SIZE size );       //逻辑单位

比如:

SetViewportExt(100,300);

SetWindowExt(200,400);

200/100=2/1=2     逻辑单位等于1设备单位,水平部分

400/300=4/3=1.3333逻辑单位等于1设备单位,垂直部分

反过来:

100/200=1/2=0.5   设备单位等于1逻辑单位,水平部分

300/400=3/4=0.75  设备单位等于1逻辑单位,垂直部分

比如,当要显示水平100逻辑单位时候,实际是水平50设备单位

先解释MM_ANISOTROPIC的用法。代码如下:

//main.cpp的内容

//…….省略

CMainWindow::CMainWindow()

{

,));

}

void CMainWindow::OnPaint()

{

  CPaintDC dc(this);

  dc.SetMapMode(MM_ANISOTROPIC);

  dc.SetWindowExt(1,1);

  dc.SetViewportExt(2,1);

  dc.Ellipse(0,0,100,100);

}

画出的圆如下:

从上面看到水平有放大,垂直没有。

当使用MM_ISOTROPIC模式时候,cx和cy“实际”上是相等的。所谓的“实际上”是指即使输入参数不相等,但是函数内部也会自动修改输入参数使其相等。这个模式不会像MM_ANISOTROPIC那样会变形。

代码如下:

//main.cpp的内容

//…….省略

CMainWindow::CMainWindow()

{

  Create(NULL,_T("MFC GDI"),WS_OVERLAPPEDWINDOW,CRect(200,200,400,400));

}

void CMainWindow::OnPaint()

{

  CPaintDC dc(this);

  dc.SetMapMode(MM_ISOTROPIC);

,);

,);

,);

}

画出的圆如下:

上图的圆直径是150像素,可见SetWindowExt函数保留比较大的参数,SetViewportExt保留比较小的参数。

MM_ISOTROPIC模式的存在显得比较尴尬的,因为MM_ANISOTROPIC也可以做到。

注意:GDI不会真的按照100、200、300、400来进行等分,而是比例才是起着实际作用。用中间比例来也一样的效果,具体如代码如下:

SetViewportExt(1,3);

SetWindowExt(2,4);

而且这样有一个好处:如果参数太大,可能会乘法(CDC输出函数的参数很可能会跟上面的参数相乘)溢出,一般都是除去最大公约数。

如果SetWindowExt函数和SetViewportExt函数的参数都是1,那么输出的就是原始输出,结果如下:

这时1逻辑单位就是1设备单位(像素)。

注意事项:

①SetWindowExt函数和SetViewportExt函数只有在映像模式为MM_ISOTROPIC或MM_ANISOTROPIC时候才有实际作用。

②不管当前是哪个模式,最近的SetWindowOrg函数、SetViewportOrg函数函数总是有效的。

③6个已定义的映像模式仅对逻辑平面的坐标轴改变方向而不对设备平面坐标轴改变方向。

④在xp、2003系统上面1厘米50像素。但是在Vista、7、8系统上面1厘米26.7像素。这是系统设计的bug,无解。一般认为在相同模式下,逻辑单位一样长便认为物理单位一样长。

⑤SetWindowExt函数和SetViewportExt函数对设备平面坐标轴方向的改变仅仅适用于CDC的输出函数。其他方面还是按照向右、向下为正。这个在屏幕坐标更常见。

⑥在SetWindowExt函数和SetViewportExt函数之后,SetWindowOrg函数和SetViewportOrg函数的方向性是依据最近的SetWindowExt函数和SetViewportExt函数。

⑦使用窗口滚动条不会改变设备坐标,会改变逻辑坐标的对齐点。

最后、最隐蔽的bug:

以下代码为了让结果图案看的更直观,加入刷子,请看:

//main.cpp的内容

//…….省略

CMainWindow::CMainWindow()

{

  Create(NULL,_T("MFC GDI"),WS_OVERLAPPEDWINDOW,CRect(200,200,450,450));

}

void CMainWindow::OnPaint()

{

  CPaintDC dc(this);

  CBrush brush1(HS_DIAGCROSS,RGB(0,255,0));

  dc.SelectObject(&brush1);

  dc.Ellipse(-100,-100,100,100);

  dc.SetViewportOrg(100,100);

  CBrush brush2(NULL,RGB(255,0,0));

  dc.SelectObject(&brush2);

  dc.SetBkMode(TRANSPARENT);

  dc.Ellipse(-100,-100,100,100);

}

绘出的图案如下:

以上代码第一部分获得设备环境句柄。

第二部分画出左上角绿色的1/4圆。

第三部分画出红色圆。

从上图可以看出MFC的GDI是有1个bug的。那就是在第2部分使用SetViewportOrg函数设置新原点时候,新的映射会使得新圆覆盖掉旧圆。但显然旧圆还在。这是GDI设计者认为:任何CDC输出函数都是互相独立的——每次输出都是在“新”逻辑平面进行输出。这样在逻辑平面输出后都对新输出的像素根据CDC属性运算到已经存在的设备平面上。

SetMapMode函数本身不会影响已经在设备平面输出的点的分布。比如在上面代码最后加入SetMapMode(MM_HIENGLISH);语句,重新编译并运行,结果是一样的。

跟SetMapMode函数一样,SetWindowOrg函数、SetViewportOrg函数、SetWindowExt函数和SetViewportExt函数不会影响已经在设备平面输出的点的分布。比如在上面代码最后加入dc.SetViewportExt(300,300);语句,重新编译并运行档,结果是一样的。

GetDeviceCaps函数是一个很有用的函数。里面参数经常被用来查找跟CDC相关的环境信息。现在给出一个例子:

//main.cpp的内容

//…….省略

CMainWindow::CMainWindow()

{

,));

}

void CMainWindow::OnPaint()

{
  CPaintDC dc(this);

  int cHORZRES = dc.GetDeviceCaps(HORZRES);
  int cVERTRES = dc.GetDeviceCaps(VERTRES);
  int cHORZSIZE = dc.GetDeviceCaps(HORZSIZE);
  int cVERTSIZE = dc.GetDeviceCaps(VERTSIZE);
  int cLOGPIXELSX = dc.GetDeviceCaps(LOGPIXELSX);
  int cLOGPIXELSY = dc.GetDeviceCaps(LOGPIXELSY);
  int cNUMCOLORS = dc.GetDeviceCaps(NUMCOLORS);
  int cBITSPIXEL = dc.GetDeviceCaps(BITSPIXEL);
  int cPLANES = dc.GetDeviceCaps(PLANES);
  int cRASTERCAPS = dc.GetDeviceCaps(RASTERCAPS);
  int cTECHNOLOGY = dc.GetDeviceCaps(TECHNOLOGY);

  CString sHORZRES;
  CString sVERTRES;
  CString sHORZSIZE;
  CString sVERTSIZE;
  CString sLOGPIXELSX;
  CString sLOGPIXELSY;
  CString sNUMCOLORS;
  CString sBITSPIXEL;
  CString sPLANES;
  CString sRASTERCAPS;
  CString sTECHNOLOGY;

  sHORZRES.Format("%d",cHORZRES);
  sVERTRES.Format("%d",cVERTRES);
  sHORZSIZE.Format("%d",cHORZSIZE);
  sVERTSIZE.Format("%d",cVERTSIZE);
  sLOGPIXELSX.Format("%d",cLOGPIXELSX);
  sLOGPIXELSY.Format("%d",cLOGPIXELSY);
  sNUMCOLORS.Format("%u",cNUMCOLORS);
  sBITSPIXEL.Format("%d",cBITSPIXEL);
  sPLANES.Format("%d",cPLANES);
  sRASTERCAPS.Format("%d",cRASTERCAPS);
  sTECHNOLOGY.Format("%d",cTECHNOLOGY);

  dc.TextOut(10,20,CString("HORZRES"));
  dc.TextOut(10,40,CString("VERTRES"));
  dc.TextOut(10,60,CString("HORZSIZE"));
  dc.TextOut(10,80,CString("VERTSIZE"));
  dc.TextOut(10,100,CString("LOGPIXELSX"));
  dc.TextOut(10,120,CString("LOGPIXELSY"));
  dc.TextOut(10,140,CString("NUMCOLORS"));
  dc.TextOut(10,160,CString("BITSPIXEL"));
  dc.TextOut(10,180,CString("PLANES"));
  dc.TextOut(10,200,CString("RASTERCAPS"));
  dc.TextOut(10,220,CString("TECHNOLOGY"));

  dc.TextOut(120,20,sHORZRES);
  dc.TextOut(120,40,sVERTRES);
  dc.TextOut(120,60,sHORZSIZE);
  dc.TextOut(120,80,sVERTSIZE);
  dc.TextOut(120,100,sLOGPIXELSX);
  dc.TextOut(120,120,sLOGPIXELSY);
  dc.TextOut(120,140,sNUMCOLORS);
  dc.TextOut(120,160,sBITSPIXEL);
  dc.TextOut(120,180,sPLANES);
  dc.TextOut(120,200,sRASTERCAPS);
  dc.TextOut(120,220,sTECHNOLOGY);
}

结果如下图:

最真实的dpi的获得办法:

//main.cpp的内容

//…….省略

CMainWindow::CMainWindow()
{
  Create(NULL,_T("MFC GDI"),WS_OVERLAPPEDWINDOW,CRect(200,200,300,300));
}

void CMainWindow::OnPaint()
{
  CPaintDC dc(this);

  CRect rect;
  GetClientRect(&rect);

  int xLogPixPerInch = dc.GetDeviceCaps(LOGPIXELSX);
  int yLogPixPerInch = dc.GetDeviceCaps(LOGPIXELSY);

  CString strxLogPixPerInch;
  CString stryLogPixPerInch;

  strxLogPixPerInch.Format("%d",xLogPixPerInch);
  stryLogPixPerInch.Format("%d",yLogPixPerInch);

  dc.TextOut(40,20,strxLogPixPerInch);
  dc.TextOut(40,40,stryLogPixPerInch);

}

如果操作系统在桌面属性的“设置”→“高级”→“常规”→“显示”→“dpi设置”把dpi设置成默认的96时候获得的结果如下图:

如果是设置为120那么结果如下图:

里面的字都变得比较大一些。

GetClientRect函数获得的客户区情况,代码如下:

//main.cpp的内容

//…….省略

,));
}

void CMainWindow::OnPaint()
{
  CPaintDC dc(this);

  CRect rect;
  GetClientRect(&rect);

  CString oldRight;
  CString oldBottom;

  oldRight.Format("%d",rect.right);
  oldBottom.Format("%d",rect.bottom);

  dc.TextOut(40,20,oldRight);
  dc.TextOut(40,40,oldBottom);

  dc.SetMapMode(MM_LOMETRIC);

  GetClientRect(&rect);

  oldRight.Format("%d",rect.right);
  oldBottom.Format("%d",rect.bottom);

  dc.TextOut(40,-130,oldRight);
  dc.TextOut(40,-160,oldBottom);
}

结果如下图:

可见,映射模式并不会改变GetClientRect函数获得的客户区大小与设备坐标方向(oldRight和oldBottom都是正数可见设备坐标方向没被改变),也不会改变字体大小,但依然可以改变逻辑方向性。即使用鼠标拖动边框改变窗口大小也是上下一致。

根据经验,最好别在SetViewportExt函数参数里面使用负数,改变坐标轴方向最好是在SetWindowExt函数里面比较保险。这2个函数的x比例的正负号决定逻辑坐标轴的右方向的正负号。这2个函数的y比例的正负号决定逻辑坐标轴的下右方向的正负号。

①可以认为CDC的所有成员函数都以逻辑坐标为参数。

②可以认为CWnd的所有成员函数都以设备坐标为参数。

MFC映射的更多相关文章

  1. MFC打印

    映射模式是MFC甚至SDK界面编程第1个难点.打印则是第2个难点.这2个都是历史遗留的设计缺陷.这些缺陷还不至于到bug程度,但却很难用,不易理解. MFC提供2个类来实现打印(预览),具体有CPri ...

  2. MFC【5】MFC集合类

    MFC集合类现在来看已经很落后了. 5.1数组 5.1.1MFC数组类 CArray类,它实际是一个模板类,利用它可以创建人和数据类型的类型安全数组.在头文件Afxtempl.h中定义了CArray. ...

  3. MFC消息映射机制以及画线功能实现

    ---此仅供用于学习交流,切勿用于商业用途,转载请注明http://www.cnblogs.com/mxbs/p/6213404.html. 利用VS2010创建一个单文档标准MFC工程,工程名为Dr ...

  4. MFC编程入门之五(MFC消息映射机制概述)

    在MFC软件开发中,界面操作或者线程之间通信都会经常用到消息,通过对消息的处理实现相应的操作.比较典型的过程是,用户操作窗口,然后有消息产生,送给窗口的消息处理函数处理,对用户的操作做出响应. 一.什 ...

  5. VS2010/MFC编程入门之五(MFC消息映射机制概述)

    VS2010/MFC编程入门之五(MFC消息映射机制概述)-软件开发-鸡啄米 http://www.jizhuomi.com/software/147.html 上一讲鸡啄米为大家简单分析了MFC应用 ...

  6. (转) MFC的入口点与消息循环,消息映射

    博文分析的很不错,尤其是替换默认窗口过程的这块,本人觉得,所有的这些都可以参阅侯杰的<深入浅出MFC >. 来自:http://blog.csdn.net/sryan/article/de ...

  7. MFC消息映射机制

    1.MFC应用框架主要类之间的关系 MFC自动生成的框架中重要的类有:C-App.CMainFrame.C-Doc和C-View. 其他的类如CClassView.CFileView等都是在框架窗口( ...

  8. MFC消息映射与命令传递

    题外话:刚开始学视窗程序设计的时候,我就打印了一本Windows消息详解,里面列举了各种已定义消息的意义和作用,共10多页,在编程的时候翻翻,有时觉得很受用.我发觉很多编程的朋友,虽然每天都面对消息, ...

  9. 剖析MFC六大关键技术(五六)--消息映射与命令传递

    说到消息,在MFC中,“最熟悉的神秘”可算是消息映射,那是我们刚开始接触MFC时就要面对的东西.有过SDK编程经验的朋友转到MFC编程的时候,一下子觉得什么都变了样.特别是窗口消息及对消息的处理跟以前 ...

随机推荐

  1. 优化之Joiner组件

    Joiner组件在运行时需要额外的内存空间处理中间结果,因此会影响性能 可通过查看Joiner performance计数器来决定Joiner组件是否需要优化 通过如下方式优化Joiner组件 将Ma ...

  2. ASP.Net Core项目在Mac上使用Entity Framework Core 2.0进行迁移可能会遇到的一个问题.

    在ASP.Net Core 2.0的项目里, 我使用Entity Framework Core 2.0 作为ORM. 有人习惯把数据库的连接字符串写在appSettings.json里面, 有的习惯写 ...

  3. 【Redis篇】初始Redis与Redis安装

    一.前述 Redis是当前比较热门的NOSQL系统之一,它是一个key-value存储系统.和Memcache类似,但很大程度补偿了Memcache的不足,它支持存储的value类型相对更多,包括st ...

  4. Hbase篇--HBase中一对多和多对多的表设计

    一.前述 今天分享一篇关于HBase的一对多和多对多的案例的分析. 二.具体案例 案例一.多对多    人员-角色   人员有多个角色  角色优先级   角色有多个人员   人员 删除添加角色   角 ...

  5. 【JVM虚拟机】(3)---垃圾回收器

    垃圾回收器 收集算法是内存回收的方法论,垃圾收集器就是内存回收的具体实现.收集器主要分三类:串行收集器.并行收集器以及并发收集器. 一.基础概念 1.并发和并行 a:并行(Parallel):指多条垃 ...

  6. 【干货】.NET WebApi HttpMessageHandler管道

    消息拦截器是一个类,接收 HTTP request并返回 HTTP response,Message handler 继承自抽象类 HttpMessageHandler,那么学习消息过滤器之前你应该了 ...

  7. PHP分页倒序时,需要注意的问题

    PHP分页倒序请求,如果有新数据加入,下一页会出现重复数据 解决方案: 第一次查询时,给前端返回一个查询时间戳,下一次请求时,把时间戳带过来,只查询比这个时间戳小的数据

  8. C++中int与string的转化

    C++中int与string的转化 int本身也要用一串字符表示,前后没有双引号,告诉编译器把它当作一个数解释.缺省情况下,是当成10进制(dec)来解释,如果想用8进制,16进制,怎么办?加上前缀, ...

  9. Java基础17:Java IO流总结

    更多内容请关注微信公众号[Java技术江湖] 这是一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux ...

  10. 《深入java虚拟机》读书笔记之垃圾收集器与内存分配策略

    前言 该读书笔记用于记录在学习<深入理解Java虚拟机--JVM高级特性与最佳实践>一书中的一些重要知识点,对其中的部分内容进行归纳,或者是对其中不明白的地方做一些注释.主要是方便之后进行 ...