频道栏目
首页 > 资讯 > 其他综合 > 正文

MFC常识性知识汇总

17-07-13        来源:[db:作者]  
收藏   我要投稿

目录

1.BitMap与Dc间的关系.

2.由位图bitmap对象到位图文件保存.

3.OnOK()、OnCancel()、OnClose()、OnDestroy() 对话框概念.

4.OnDrow和OnPaint的区别

5.MFC文档视图

6.MFC框架间联立的方法

7.Document(文档)、View(视图)、Frame(框架)、App(应用)之间相互访问

1.BitMap与Dc间的关系.

view plaincopy

//获取窗口DC

HDChdc=GetDC(hWnd);

//获取窗口的客户区区域

RECTrect;

GetClientRect(hWnd,&rect);

//获取当前窗口的位图

HDChMemDC=CreateCompatibleDC(hdc);

HBITMAPhBitmapSrc;

hBitmapSrc=CreateCompatibleBitmap(hdc,rect.right,rect.bottom);

SelectObject(hMemDC,hBitmapSrc);

BitBlt(hMemDC,0,0,rect.right,rect.bottom,hdc,0,0,SRCCOPY);

SaveBitmap(hBitmapSrc,"C:\\123.bmp");//SaveBitmap()是自定义的一个函数,用来把传递进来的位图句柄保存成图片文件

//结果是C:\\123.bmp内容是hWnd的窗口内容

原来一直觉得,HBITMAP选进设备DC以后,是将图片数据拷贝到DC上,从这段代码当然这里自然而然猜想,HBITMAP选进DC后,对DC的操作会影响到HBIMAP。

于是添加了如下测试代码:

view plaincopy

//把窗口填充成白色

SelectObject(hdc,GetStockObject(WHITE_PEN));

SelectObject(hdc,GetStockObject(WHITE_BRUSH));

Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom);

HDChMemDC2=CreateCompatibleDC(hdc);

SelectObject(hMemDC2,hBitmapSrc);

BitBlt(hdc,0,0,rect.right,rect.bottom,hMemDC2,0,0,SRCCOPY); 先把原窗口拷贝成白色,然后再把选入了hBitmapSrc的内存DC拷贝到原窗口上,测试发现,窗口还是白色,无效。这是为什么呢。

百度下CreateCompatibleBitmap,发现百度百科里面介绍:

HDC hdc=GetDC(hwnd); HDC memdc=CreateCompatibleDC(hdc); RECT rc; BITMAP bmp; HBITMAP holdbmp,hbmp=LoadBitmap(hInstDVBRes,MAKEINTRESOURCE(IDB_CLOCK));//从资源加载位图 holdbmp=(HBITMAP)SelectObject(memdc,hbmp);//这里把hbmp的位图选择到兼容DC memdc,之后这个兼容DC就拥有和 //hbmp同样大小的绘图区域,注意超出位图返回的GDI输出都是无效的. GetObject(hbmp,sizeof(BITMAP),&bmp);//这里获取位图的大小信息,事实上也是兼容DC绘图输出的范围 SetRect(&rc,0,0,bmp.bmWidth,bmp.bmHeight); DrawText(memdc,"Center Line Text" -1,&rc,DT_VCENTER|DT_SINGLELINE|DT_CENTER);//在兼容DC中间位置输出字符串 //这样以来我们就相当于把hbmp这个位图加上了文字标注,我们可以把这个增加了文字标注的位图保存起来.一个简单的图像处理基本就OK了. SelectObject(memdc,holdbmp);//复原兼容DC数据. DeleteDC(memdc); ReleaseDC(hwnd, hdc);

这说明我的猜想是对的,如果HBITMAP选进了DC,对DC的操作的确会影响到HBITMAP。但代码测试不无效时为什么呢。 突然想到,可能使一个HBITMAP不能同时选进两个DC。做了如下测试 view plaincopy

//获取窗口DC

HDChdc=GetDC(hWnd);

//获取窗口的客户区区域

RECTrect;

GetClientRect(hWnd,&rect);

//获取当前窗口的位图

HDChMemDC=CreateCompatibleDC(hdc);

HBITMAPhBitmapSrc;

hBitmapSrc=CreateCompatibleBitmap(hdc,rect.right,rect.bottom);

HGDIOBJhOldBt=SelectObject(hMemDC,hBitmapSrc);//修改,保存老位图句柄

BitBlt(hMemDC,0,0,rect.right,rect.bottom,hdc,0,0,SRCCOPY);

//获取当前窗口位图的像素颜色数据

BYTE*pBitmapDataSrc=newBYTE[(rect.right-rect.left)*(rect.bottom-rect.top)*4];

GetBitmapBits(hBitmapSrc,(rect.right-rect.left)*(rect.bottom-rect.top)*4,pBitmapDataSrc);

//把窗口填充成白色

SelectObject(hdc,GetStockObject(WHITE_PEN));

SelectObject(hdc,GetStockObject(WHITE_BRUSH));

Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom);

TextOut(hMemDC,100,100,_T("123456abcd"),10);//新加内容,为了验证是否是之后拷贝上去的,多画了点东西

SelectObject(hMemDC,hOldBt);//新加内容

HDChMemDC2=CreateCompatibleDC(hdc);

SelectObject(hMemDC2,hBitmapSrc);

BitBlt(hdc,0,0,rect.right,rect.bottom,hMemDC2,0,0,SRCCOPY);

return;

验证无误。

原文转载自:http://blog.csdn.NET/qscjob/article/details/18995725

看到一段代码,内容是

view plaincopy

//获取窗口DC

HDChdc=GetDC(hWnd);

//获取窗口的客户区区域

RECTrect;

GetClientRect(hWnd,&rect);

//获取当前窗口的位图

HDChMemDC=CreateCompatibleDC(hdc);

HBITMAPhBitmapSrc;

hBitmapSrc=CreateCompatibleBitmap(hdc,rect.right,rect.bottom);

SelectObject(hMemDC,hBitmapSrc);

BitBlt(hMemDC,0,0,rect.right,rect.bottom,hdc,0,0,SRCCOPY);

SaveBitmap(hBitmapSrc,"C:\\123.bmp");//SaveBitmap()是自定义的一个函数,用来把传递进来的位图句柄保存成图片文件

//结果是C:\\123.bmp内容是hWnd的窗口内容

原来一直觉得,HBITMAP选进设备DC以后,是将图片数据拷贝到DC上,从这段代码当然这里自然而然猜想,HBITMAP选进DC后,对DC的操作会影响到HBIMAP。

于是添加了如下测试代码:

view plaincopy

//把窗口填充成白色

SelectObject(hdc,GetStockObject(WHITE_PEN));

SelectObject(hdc,GetStockObject(WHITE_BRUSH));

Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom);

HDChMemDC2=CreateCompatibleDC(hdc);

SelectObject(hMemDC2,hBitmapSrc);

BitBlt(hdc,0,0,rect.right,rect.bottom,hMemDC2,0,0,SRCCOPY); 先把原窗口拷贝成白色,然后再把选入了hBitmapSrc的内存DC拷贝到原窗口上,测试发现,窗口还是白色,无效。这是为什么呢。

百度下CreateCompatibleBitmap,发现百度百科里面介绍:

HDC hdc=GetDC(hwnd); HDC memdc=CreateCompatibleDC(hdc); RECT rc; BITMAP bmp; HBITMAP holdbmp,hbmp=LoadBitmap(hInstDVBRes,MAKEINTRESOURCE(IDB_CLOCK));//从资源加载位图 holdbmp=(HBITMAP)SelectObject(memdc,hbmp);//这里把hbmp的位图选择到兼容DC memdc,之后这个兼容DC就拥有和 //hbmp同样大小的绘图区域,注意超出位图返回的GDI输出都是无效的. GetObject(hbmp,sizeof(BITMAP),&bmp);//这里获取位图的大小信息,事实上也是兼容DC绘图输出的范围 SetRect(&rc,0,0,bmp.bmWidth,bmp.bmHeight); DrawText(memdc,"Center Line Text" -1,&rc,DT_VCENTER|DT_SINGLELINE|DT_CENTER);//在兼容DC中间位置输出字符串 //这样以来我们就相当于把hbmp这个位图加上了文字标注,我们可以把这个增加了文字标注的位图保存起来.一个简单的图像处理基本就OK了. SelectObject(memdc,holdbmp);//复原兼容DC数据. DeleteDC(memdc); ReleaseDC(hwnd, hdc);

这说明我的猜想是对的,如果HBITMAP选进了DC,对DC的操作的确会影响到HBITMAP。但代码测试不无效时为什么呢。 突然想到,可能使一个HBITMAP不能同时选进两个DC。做了如下测试 view plaincopy

//获取窗口DC

HDChdc=GetDC(hWnd);

//获取窗口的客户区区域

RECTrect;

GetClientRect(hWnd,&rect);

//获取当前窗口的位图

HDChMemDC=CreateCompatibleDC(hdc);

HBITMAPhBitmapSrc;

hBitmapSrc=CreateCompatibleBitmap(hdc,rect.right,rect.bottom);

HGDIOBJhOldBt=SelectObject(hMemDC,hBitmapSrc);//修改,保存老位图句柄

BitBlt(hMemDC,0,0,rect.right,rect.bottom,hdc,0,0,SRCCOPY);

//获取当前窗口位图的像素颜色数据

BYTE*pBitmapDataSrc=newBYTE[(rect.right-rect.left)*(rect.bottom-rect.top)*4];

GetBitmapBits(hBitmapSrc,(rect.right-rect.left)*(rect.bottom-rect.top)*4,pBitmapDataSrc);

//把窗口填充成白色

SelectObject(hdc,GetStockObject(WHITE_PEN));

SelectObject(hdc,GetStockObject(WHITE_BRUSH));

Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom);

TextOut(hMemDC,100,100,_T("123456abcd"),10);//新加内容,为了验证是否是之后拷贝上去的,多画了点东西

SelectObject(hMemDC,hOldBt);//新加内容

HDChMemDC2=CreateCompatibleDC(hdc);

SelectObject(hMemDC2,hBitmapSrc);

BitBlt(hdc,0,0,rect.right,rect.bottom,hMemDC2,0,0,SRCCOPY);

return;

验证无误。

原文转载自:http://blog.csdn.NET/qscjob/article/details/18995725

2.由位图bitmap对象到位图文件保存.

最近正在着手开发一个图片库,也就是实现对常见图片格式的度写操作。作为总结与积累,我会把这些图片格式以及加载的实现写在我的Blog上。

说到图片,位图(Bitmap)当然是最简单的,它Windows显示图片的基本格式,其文件扩展名为*.BMP。在Windows下,任何各式的图片文件(包括视频播放)都要转化为位图个时候才能显示出来,各种格式的图片文件也都是在位图格式的基础上采用不同的压缩算法生成的(Flash中使用了适量图,是按相同颜色区域存储的)。

一、下面我们来看看位图文件(*.BMP)的格式。

位图文件主要分为如下3个部分:

块名称对应Windows结构体定义大小(Byte)

文件信息头BITMAPFILEHEADER14

位图信息头BITMAPINFOHEADER40

RGB颜色阵列BYTE*由图像长宽尺寸决定

既然要保存为位图,那就应该正确按照位图未见格式向其写入数据.1.FILEHEADER,2.INFOHEADER,3.数据部分为BITMAP对象中的值,通过GetDIBits来获取.

说他多估计懵逼,文档末尾有我的demo.自己慢慢研究吧,涉及到其它项目的内容,自己查阅判断并合理利用.

1、文件信息头BITMAPFILEHEADER

结构体定义如下:

typedef struct tagBITMAPFILEHEADER {

UINT bfType;

DWORD bfSize;

UINT bfReserved1;

UINT bfReserved2;

DWORD bfOffBits;

} BITMAPFILEHEADER;

其中:

bfType说明文件的类型,该值必需是0x4D42,也就是字符'BM'。

bfSize说明该位图文件的大小,用字节为单位

bfReserved1保留,必须设置为0

bfReserved2保留,必须设置为0

bfOffBits说明从文件头开始到实际的图象数据之间的字节的偏移量。这个参数是非常有用的,因为位图信息头和调色板的长度会根据不同情况而变化,所以你可以用这个偏移值迅速的从文件中读取到位数据。

2、位图信息头BITMAPINFOHEADER

结构体定义如下:

typedef struct tagBITMAPINFOHEADER {

DWORD biSize;

LONG biWidth;

LONG biHeight;

WORD biPlanes;

WORD biBitCount;

DWORD biCompression;

DWORD biSizeImage;

LONG biXPelsPerMeter;

LONG biYPelsPerMeter;

DWORD biClrUsed;

DWORD biClrImportant;

} BITMAPINFOHEADER;

其中:

biSize说明BITMAPINFOHEADER结构所需要的字数。

biWidth说明图象的宽度,以象素为单位。

biHeight说明图象的高度,以象素为单位。注:这个值除了用于描述图像的高度之外,它还有另一个用处,就是指明该图像是倒向的位图,还是正向的位图。如果该值是一个正数,说明图像是倒向的,如果该值是一个负数,则说明图像是正向的。大多数的BMP文件都是倒向的位图,也就是时,高度值是一个正数。

biPlanes为目标设备说明位面数,其值将总是被设为1。

biBitCount说明比特数/象素,其值为1、4、8、16、24、或32。但是由于我们平时用到的图像绝大部分是24位和32位的,所以我们讨论这两类图像。

biCompression说明图象数据压缩的类型,同样我们只讨论没有压缩的类型:BI_RGB。

biSizeImage说明图象的大小,以字节为单位。当用BI_RGB格式时,可设置为0。

biXPelsPerMeter说明水平分辨率,用象素/米表示。

biYPelsPerMeter说明垂直分辨率,用象素/米表示。

biClrUsed说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)。

biClrImportant说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。

3、RGB颜色阵列

有关RGB三色空间我想大家都很熟悉,这里我想说的是在Windows下,RGB颜色阵列存储的格式其实BGR。也就是说,对于24位的RGB位图像素数据格式是:

蓝色B值绿色G值红色R值

对于32位的RGB位图像素数据格式是:

蓝色B值绿色G值红色R值透明通道A值

透明通道也称Alpha通道,该值是该像素点的透明属性,取值在0(全透明)到255(不透明)之间。对于24位的图像来说,因为没有Alpha通道,故整个图像都不透明。

二、搞清了文件格式,下一步我们要实现加载。

加载文件的目的是要得到图片属性,以及RGB数据,然后可以将其绘制在DC上(GDI),或是生成纹理对象(3D:OpenGL/Direct3D)。这两种用途在数据处理上有点区别,我们主要按前一种用法讲,在和3D有不同的地方,我们再提出来。

1、加载文件头

//Load the file header

BITMAPFILEHEADER header;

memset(&header, 0, sizeof(header));

inf.read((char*)&header, sizeof(header));

if(header.bfType != 0x4D42)

return false;

这个很简单,没有什么好说的。

2、加载位图信息头

//Load the image information header

BITMAPINFOHEADER infoheader;

memset(&infoheader, 0, sizeof(infoheader));

inf.read((char*)&infoheader, sizeof(infoheader));

m_iImageWidth = infoheader.biWidth;

m_iImageHeight = infoheader.biHeight;

m_iBitsPerPixel = infoheader.biBitCount;

这里我们得到了3各重要的图形属性:宽,高,以及每个像素颜色所占用的位数。

3、行对齐

由于Windows在进行行扫描的时候最小的单位为4个字节,所以当

图片宽X每个像素的字节数!= 4的整数倍

时要在每行的后面补上缺少的字节,以0填充(一般来说当图像宽度为2的幂时不需要对齐)。位图文件里的数据在写入的时候已经进行了行对齐,也就是说加载的时候不需要再做行对齐。但是这样一来图片数据的长度就不是:宽X高X每个像素的字节数了,我们需要通过下面的方法计算正确的数据长度:

//Calculate the image data size

int iLineByteCnt = (((m_iImageWidth*m_iBitsPerPixel) + 31) >> 5) << 2;

m_iImageDataSize = iLineByteCnt * m_iImageHeight;

4、加载图片数据

对于24位和32位的位图文件,位图数据的偏移量为sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER),也就是说现在我们可以直接读取图像数据了。

if(m_pImageData) delete []m_pImageData;

m_pImageData = new unsigned char[m_iImageDataSize];

inf.read((char*)m_pImageData, m_iImageDataSize);

如果你足够细心,就会发现内存m_pImageData里的数据的确是BGR格式,可以用个纯蓝色或者是纯红色的图片测试一下。

5、绘制

好了,数据和属性我们都有了,现在就可以拿来随便用了,就和吃馒头一样,爱粘白糖粘白糖,爱粘红糖粘红糖。下面是我的GDI绘制代码,仅作参考。

void CImage::DrawImage(HDC hdc, int iLeft, int iTop, int iWidth, int iHeight)

{

if(!hdc || m_pImageData == NULL)

return;

BITMAPINFO bmi;

memset(&bmi, 0, sizeof(bmi));

bmi.bmiHeader.biSize = sizeof(BITMAPINFO);

bmi.bmiHeader.biWidth = m_iImageWidth;

bmi.bmiHeader.biHeight = m_iImageHeight;

bmi.bmiHeader.biPlanes = 1;

bmi.bmiHeader.biBitCount = m_iBitsPerPixel;

bmi.bmiHeader.biCompression = BI_RGB;

bmi.bmiHeader.biSizeImage = m_iImageDataSize;

StretchDIBits(hdc, iLeft, iTop, iWidth, iHeight,

0, 0, m_iImageWidth, m_iImageHeight,

m_pImageData, &bmi, DIB_RGB_COLORS, SRCCOPY);

}

6、3D(OpenGL)的不同之处

如果你是想用刚才我们得到的数据生成纹理对象,那么你还要请出下面的问题。

首先,用来生成纹理的数据不需要对齐,也就是说不能在每行的后面加上对齐的字节。当然在OpenGL里要求纹理图片的尺寸为2的幂,所以这个问题实际上不存在;

其次,我们得到的图形数据格式是BGR(BGRA),所以在生成纹理的时候,需指定格式为GL_BGR_EXT(GL_BGRA_EXT);否则需要做BGR->RGB(BGRA->RGBA)的转化。

自己的demo例子,待审阅查看.

/*

typedef struct tagBITMAP{

LONG bmType; //位图类型,必须为0 LONG bmWidth; //位图宽度 LONG bmHeight; //位图高度 LONG bmWidthBytes; //每一行像素所在的byte数 WORD bmPlanes; //颜色平面数 WORD bmBitsPixel; //像素的位数 LPVOID bmBits; //位图内存指针 }BITMAP; 以下,仅仅是创造了兼容性位图bmp,也就是说bmBits并不存在实质性数据,所以需要通过GetDIBits来获取位图数据.

*/

/*

延伸理解:BITMAP位图与DC间的关系:http://blog.csdn.net/qq_24571549/article/details/72821906

BITMAP对象是真正的图像数据,但就下面代码来说,

因为BITMAP是从无到有的构建,需要兼容dc和BITMAP<比如位图大小>等相关信息来填充BITMAPINFO 值,所以,你懂得.

总结:利用BITMAP和兼容dc来完善BITMAPINFO 和pdata<位图数据>信息.

*/

void CClientDlg::OnOK()

{

CDC* pDeskDC = GetDesktopWindow()->GetDC();//获取桌面画布对象

CRect rc;

GetDesktopWindow()->GetClientRect(rc);//获取屏幕的客户区域

int width = 300;//获取屏幕的宽度

int height = 300;//获取屏幕的高度

CDC memDC; //定义一个内存画布

memDC.CreateCompatibleDC(pDeskDC);//创建一个兼容的画布

CBitmap bmp;

bmp.CreateCompatibleBitmap(pDeskDC,width,height);//创建兼容位图

memDC.SelectObject(&bmp);//选中位图对象

BITMAP bitmap;

bmp.GetBitmap(&bitmap);

float panelsize = 0;//记录调色板大小

////以下代码是获取调色板的长度,调色板现在的用处很少,因为256色的位图已经不多了。

if (bitmap.bmBitsPixel < 16)//判断是否为真彩色位图

{

int Data = bitmap.bmBitsPixel*sizeof(RGBQUAD);

panelsize = pow(2.0f, Data);

}

/*

typedef struct tagBITMAPINFO

{

BITMAPINFOHEADER bmiHeader;

RGBQUAD bmiColors[1];

} BITMAPINFO;

*/

//这个实际上就是填充bmiHeader信息.

BITMAPINFO *pBInfo = (BITMAPINFO*)LocalAlloc(LPTR,sizeof(BITMAPINFO)+panelsize);

pBInfo->bmiHeader.biBitCount = bitmap.bmBitsPixel;

pBInfo->bmiHeader.biClrImportant = 0;

pBInfo->bmiHeader.biCompression = 0;

pBInfo->bmiHeader.biHeight = height;

pBInfo->bmiHeader.biPlanes = bitmap.bmPlanes;

pBInfo->bmiHeader.biSize = sizeof(BITMAPINFO);

pBInfo->bmiHeader.biSizeImage = bitmap.bmWidthBytes*bitmap.bmHeight;

pBInfo->bmiHeader.biWidth = width;

pBInfo->bmiHeader.biXPelsPerMeter = 0;

pBInfo->bmiHeader.biYPelsPerMeter = 0;

m_X = m_RecvX;

m_Y = m_RecvY;

memDC.BitBlt(0,0,width,height,pDeskDC,m_X,m_Y,SRCCOPY);

char* pData = new char[bitmap.bmWidthBytes* bitmap.bmHeight];

// 获取该调色板下新的像素值

/*

GetDIBits是最重要的函数,真正获得位图数据的工作就由它完成,它第一个参数为HDC,第二个参数为位图句柄,第三个

参数为扫描行的开始行,一般为0,第四个为结束行,一般就是高度,第四个参数最重要,它表示接收数据的起始地址,这个

地址一般是在调色板之后。第五个参数指的是接收BITMAPINFO结构的地址,这个结构上面没有写,它其实就是BITMAPIN

FO结构加上调色板信息。最后一个参数是格式。一般是DIB_RGB_COLORS

*/

::GetDIBits(memDC.m_hDC,bmp,0,bitmap.bmHeight,pData,pBInfo,DIB_RGB_COLORS);

int BufSize = panelsize+ sizeof(BITMAPINFO)+bitmap.bmWidthBytes* bitmap.bmHeight;

memcpy(pSendBuf,pBInfo,sizeof(BITMAPINFO));

char* pHead = pSendBuf;

pSendBuf += sizeof(BITMAPINFO);

memcpy(pSendBuf,pData,bitmap.bmWidthBytes* bitmap.bmHeight);

///////////////////////////////////////////////////////////////////////

BITMAPFILEHEADER bmheader;

memset(&bmheader, 0, sizeof(bmheader));

bmheader.bfType = 0x4d42; //图像格式。必须为'BM'格式。

bmheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + panelsize; //从文件开头到数据的偏移量

bmheader.bfSize = ((width *bitmap.bmBitsPixel +31) /8) *height + bmheader.bfOffBits;//文件大小 这个结构包含图像大小信息和 bmheader.bfOffBits偏移量信息总和

////////////////////////////////////////////////////////////////////////

static int ncount = 0;

if (0 == ncount)

{

CFile MyBitMap("b.bmp", CFile::modeCreate | CFile::modeWrite);

MyBitMap.Write(&bmheader, sizeof(BITMAPFILEHEADER));

MyBitMap.Write(&(pBInfo->bmiHeader), sizeof(BITMAPINFOHEADER));

MyBitMap.Write(pData, bitmap.bmWidthBytes* bitmap.bmHeight);

MyBitMap.Close();

ncount++;

}

//////////////////////////////////////////////////////////////////////////

pSendBuf = pHead;

addr1.sin_family = AF_INET;

addr1.sin_port = htons(5002);

addr1.sin_addr.S_un.S_addr = inet_addr(m_ServerIP);

//定义数据报的格式

/*序号2位||结束标记2位||位图数据||位图数据大小4位||屏幕X坐标2位||屏幕Y坐标2位||数据报大小4位*/

//定义每次发送位图数据的大小

bmpsize = GraphSize;

//计算每个位图发送的次数

count = BufSize / GraphSize;

mod = BufSize % GraphSize;

if ( mod != 0)

count+=1;

m_FrameIndex = 0;

int ret = SendData(m_FrameIndex,mod,bmpsize,count,pSendBuf,addr1);

pSendBuf = pHead;

delete []pData;

LocalFree(pBInfo);

pDeskDC->DeleteDC();

bmp.DeleteObject();

memDC.DeleteDC();

}

3.OnOK()、OnCancel()、OnClose()、OnDestroy() 对话框概念.

重要的话写在前言:

1.是否真正区分类函数重写概念.<比如点击对话框上"确定","取消",类函数重写导致ID事件绑定,自动调用你重写的函数>

2.是否真正区分消息响应机制间关系.

3.理解消息路由间前后的关系.

对话框默认用的两个按钮的ID分别是IDOK和IDCANCEL,这两个都是在winuser.h 中预定义的系统标准控件ID。

对于标准ID,你不重载时MFC会自动调用父类的相应处理函数。

比如IDOK映射到CDialog::OnOK()函数,IDCANCEL映射到CDialog::OnCancel()。

在这两个函数的源码如下:

void CDialog::OnOK()

{

if (!UpdateData(TRUE))

{

TRACE(traceAppMsg, 0, "UpdateData failed during dialog termination.\n");

// the UpdateData routine will set focus to correct item

return;

}

EndDialog(IDOK);

}

void CDialog::OnCancel()

{

EndDialog(IDCANCEL);

}

可以看出点击这两个按钮,都会调用EndDialog()来关闭对话框,只是返回值不同。

EndDialog()函数调用了DestroyWindow()函数,DestroyWindow()函数又发送了WM_DESTROY消息,该消息的处理函数是OnDestroy(),对话框的生存期最后一个函数是PostNcDestroy()函数。

点那个叉叉呢,首先向对话框发送WM_CLOSE消息,由OnClose()函数处理,它调用DestroyWindow(),其后是和上面一样的路由。

可以看出点叉叉的时候绕过了OnOK()和OnCancel()。

小结一下:

1. 点“确定”、“取消”时的关闭路由为

OnOK()或OnCancel() ---> EndDialog() ---> DestroyWindow() ---> OnDestroy() ---> PostNcDestroy()

2. 点“关闭”标题栏按钮的关闭路由为

OnClose()---> DestroyWindow() ---> OnDestroy() ---> PostNcDestroy()

回答楼主的问题:

请注意,上面提到的这些函数统统都是可以重载的,在重载时加入了你自己的代码后,应该调用父类CDialog同名的函数才能正确路由下去,否则就关不了对话框了。

举个例子,重载了关闭的小叉叉

void CAboutDlg::OnClose()

{

// TODO: 在此添加消息处理程序代码和/或调用默认值

DoSomthing(0; // 执行自己的判断等等

// CDialog::OnClose(); // 把向导生成的父类调用给注释了,这时就关不了对话框了。

}

补充回答,

点叉叉会发送WM_CLOSE消息,如果需要重载的话,应该在对话框的属性窗口中,选择WM_CLOSE消息来添加消息处理函数。

VS的IDE会自动添加如下三段:

1. xxx.h文件,类声明中加入OnClose()函数声明

afx_msg void OnClose();

2. xxx.cpp文件,加入消息映射宏

ON_WM_CLOSE() // 对于Windows标准消息,都是这种简短的格式。

3. xxx.cpp文件,加入函数体

void CMyDlg::OnClose()

{

CDialog::OnClose();

}

上述3处如果都正常的话,叉叉就映射到OnClose()了。

原文转载自:http://zhidao.baidu.com/question/62651044.html

4.nDrow和OnPaint的区别

OnPaint是WM_PAINT消息的消息处理函数,在OnPaint中调用OnDraw,一般来说,用户自己的绘图代码应放在OnDraw中。

OnPaint()是CWnd的类成员,负责响应WM_PAINT消息。OnDraw()是CVIEW的成员函数,没有响应消息的功能.当视图变得无效时(包括大小的改变,移动,被遮盖等等),Windows发送WM_PAINT消息。该视图的OnPaint 处理函数通过创建CPaintDC类的DC对象来响应该消息并调用视图的OnDraw成员函数.OnPaint最后也要调用OnDraw,因此一般在OnDraw函数中进行绘制。

The WM_PAINT message is sent when the UpdateWindow or RedrawWindow member function is called.

在OnPaint中,将调用BeginPaint,用来获得客户区的显示设备环境,并以此调用GDI函数执行绘图操作。在绘图操作完成后,将调用EndPaint以释放显示设备环境。而OnDraw在BeginPaint与EndPaint间被调用。

1) 在mfc结构里OnPaint是CWnd的成员函数. OnDraw是CView的成员函数.

2) OnPaint()调用OnDraw(),OnPrint也会调用OnDraw(),所以OnDraw()是显示和打印的共同操作。

OnPaint是WM_PAINT消息引发的重绘消息处理函数,在OnPaint中会调用OnDraw来进行绘图。OnPaint中首先构造一个CPaintDC类得实例,然后一这个实例为参数来调用虚函数OnPrepareDC来进行一些绘制前的一些处理,比设置映射模式,最后调用OnDraw。而OnDraw和OnPrepareDC不是消息处理函数。所以在不是因为重绘消息所引发的OnPaint导致OnDraw被调用时,比如在OnLButtonDown等消息处理函数中绘图时,要先自己调用OnPrepareDC。

至于CPaintDC和CClientDC根本是两回事情 CPaintDC是一个设备环境类,在OnPaint中作为参数传递给OnPrepareDC来作设备环境的设置。真正和CClientDC具有可比性的是CWindowDC,他们一个是描述客户区域,一个是描述整个屏幕。

如果是对CVIEW或从CVIEW类派生的窗口绘图时应该用OnDraw。

OnDraw()和OnPaint()有什么区别呢?

首先:我们先要明确CView类派生自CWnd类。而OnPaint()是CWnd的类成员,同时负责响应WM_PAINT消息。OnDraw()是CVIEW的成员函数,并且没有响应消息的功能。这就是为什么你用VC成的程序代码时,在视图类只有OnDraw没有OnPaint的原因。而在基于对话框的程序中,只有OnPaint。

其次:我们在第《每天跟我学MFC》3的开始部分已经说到了。要想在屏幕上绘图或显示图形,首先需要建立设备环境DC。其实DC是一个数据结构,它包含输出设备(不单指你17寸的纯屏显示器,还包括打印机之类的输出设备)的绘图属性的描述。MFC提供了CPaintDC类和CWindwoDC类来实时的响应,而CPaintDC支持重画。当视图变得无效时(包括大小的改变,移动,被遮盖等等),Windows 将 WM_PAINT 消息发送给它。该视图的OnPaint 处理函数通过创建 CPaintDC 类的DC对象来响应该消息并调用视图的 OnDraw 成员函数。通常我们不必编写重写的 OnPaint 处理成员函数。

///CView默认的标准的重画函数

void CView::OnPaint() //见VIEWCORE.CPP

{

CPaintDC dc(this);

OnPrepareDC(&dc);

OnDraw(&dc); //调用了OnDraw

}

///CView默认的标准的OnPrint函数

void CView::OnPrint(CDC* pDC, CPrintInfo*)

{

ASSERT_VALID(pDC);

OnDraw(pDC); // Call Draw

}

既然OnPaint最后也要调用OnDraw,因此我们一般会在OnDraw函数中进行绘制。下面是一个典型的程序。

///视图中的绘图代码首先检索指向文档的指针,然后通过DC进行绘图调用。

void CMyView::OnDraw( CDC* pDC )

{

CMyDoc* pDoc = GetDocument();

CString s = pDoc->GetData();

GetClientRect( &rect ); // Returns a CString CRect rect;

pDC->SetTextAlign( TA_BASELINE | TA_CENTER );

pDC->TextOut( rect.right / 2, rect.bottom / 2, s, s.GetLength() );

}

最后:现在大家明白这哥俩之间的关系了吧。因此我们一般用OnPaint维护窗口的客户区(例如我们的窗口客户区加一个背景图片),用OnDraw维护视图的客户区(例如我们通过鼠标在视图中画图)。当然你也可以不按照上面规律来,只要达到目的并且没有问题,怎么干都成。补充:我们还可以利用Invalidate(),ValidateRgn(),ValidateRect()函数强制的重画窗口,具体的请参考MSDN吧。

OnDraw中可以绘制用户区域。OnPaint中只是当窗口无效时重绘不会保留CClientDC绘制的内容。

这两个函数有区别也有联系:

1、区别:OnDraw是一个纯虚函数,定义为virtual void OnDraw( CDC* pDC ) = 0; 而OnPaint是一个消息响应函数,它响应了WM_PANIT消息,也是是窗口重绘消息。

2、联系:我们一般在视类中作图的时候,往往不直接响应WM_PANIT消息,而是重载OnDraw纯虚函数,这是因为在CVIEW类中的WM_PANIT消息响应函数中调用了OnDraw函数,如果在CMYVIEW类中响应了WM_PAINT消息,不显式地调用OnDraw函数的话,是不会在窗口重绘的时候调用OnDraw函数的。

应用程序中几乎所有的绘图都在视图的 OnDraw 成员函数中发生,必须在视图类中重写该成员函数。(鼠标绘图是个特例,这在通过视图解释用户输入中讨论。)

OnDraw 重写:

通过调用您提供的文档成员函数获取数据。

通过调用框架传递给 OnDraw 的设备上下文对象的成员函数来显示数据。

当文档的数据以某种方式更改后,必须重绘视图以反映该更改。默认的 OnUpdate 实现使视图的整个工作区无效。当视图变得无效时,Windows 将 WM_PAINT 消息发送给它。该视图的 OnPaint 处理函数通过创建 CPaintDC 类的设备上下文对象来响应该消息并调用视图的 OnDraw 成员函数。

当没有添加WM_PAINT消息处理时,窗口重绘时,由OnDraw来进行消息响应...当添加WM_PAINT消息处理时,窗口重绘时,WM_PAINT消息被投递,由OnPaint来进行消息响应.这时就不能隐式调用OnDraw了.必须显式调用( CDC *pDC=GetDC(); OnDraw(pDC); )..

隐式调用:当由OnPaint来进行消息响应时,系统自动调用CView::OnDraw(&pDC).

想象一下,窗口显示的内容和打印的内容是差不多的,所以,一般情况下,统一由OnDraw来画。窗口前景需要刷新时,系统会会调用到OnPaint,而OnPaint一般情况下是对DC作一些初始化操作后,调用OnDraw()。

OnEraseBkGnd(),是窗口背景需要刷新时由系统调用的。明显的一个例子是设置窗口的背景颜色(你可以把这放在OnPaint中去做,但是会使产生闪烁的现象)。

至于怎么界定背景和前景,那要具体问题具体分析了,一般情况下,你还是很容易区别的吧。

的确,OnPaint()用来响应WM_PAINT消息,视类的OnPaint()内部根据是打印还是屏幕绘制分别以不同的参数调用OnDraw()虚函数。所以在OnDraw()里你可以区别对待打印和屏幕绘制。

其实,MFC在进行打印前后还做了很多工作,调用了很多虚函数,比如OnPreparePrint()等。

对于OnDraw()

This method is called by the framework to render an image of the document. The framework calls this method to perform screen display, printing, and print preview, and it passes a different device context in each case. There is no default implementation

5.MFC文档视图

写在前言:怎么说呢,其实搞了好几天了,有点懵逼,似懂非懂.最终还是没有找到好的demo进行全面的分析.仅写,权当过程中总结.

总结:

CDocTemplate:给我的概念就是"类"概念.<这是个抽象类>

其派生类CSingleDocTemplate,CMultiDocTemplate.分别就是单文档和多文档类了.实际上应该关注的是他们的构造函数.所需皆为:nIDResource,pDocClass,pFrameClass,pViewClass.

想形成什么样的结果,就在构造函数中这块进行相应替换绑定.<但必须注意的是,要完善相关类和资源的绑定,比如类视图和类窗口资源>

/*可能存在多类进行联立设计实现,重点在于思想和设计*/

一:单文档的使用与理解

1直接在doc类中进行添加共有成员变量进行记录和控制.然后再view中就能直接通过getdocment返回指针进行访问doc中的数据了.

当窗口需要重绘时,view中的ondrow函数内部就会访问pdoc的指针,进行数据的获取了.看下创建工程源码即可.这样就实现了数据与现实分离开来了.

逻辑可行即可,比如示例:<以下文字逻辑阐述都是建立在利用vs创建的默认的单文档应用程序基础上>

1.在doc类中添加共有成员变量.

2.在程序开发设计中,处理鼠标按键消息,在其中进行通过getdocment获取文档指针,然后就能访问doc添加的共有成员变量进行信息存储了.

3.在view中,就能直接通过getdocment进行指针获取,然后访问数据获得成员变量进行信息重现(或者重绘了)

<顺带插一句,还有可能进行封装设计,封装一个数据类,将对象封在doc类成员变量中,对外提供方法,这种设计也很巧妙>

进行文档插入解释:p184面的运行机制.有关多文档编程具备的基础知识.p191

二:文档的读写

Getdocument函数负责文档和视图交互,但文档的数据要存盘或者读盘就要与磁盘进行数据传递,MFC提供一种读写文件简单的方法----“序列化”或者串行滑,实际上就是接口功能.

/*接下来就是介绍多文档的相关操作了.数据看到209面,进行实例化解说了*/

关于问题: http://blog.sina.com.cn/s/blog_63a881060102w8q2.html

<转载别人话,虽然能懂基本各个意思,但关联起来处理还不是很理解>:

程序初始化时保存窗口指针,或按层次关系直接获取。

关于获得MFC窗口其它类指针的方法

关于获得MFC窗口其它类指针的方法(CSDN)

访问应用程序的其它类

获得CWinApp:

-在CMainFrame,CChildFrame,CDocument,CView中直接调用AfxGetApp()或用theApp

-在其它类中只能用AfxGetApp()

获得CMainFrame:

-在CMinApp中用AfxGetMainWnd()或者m_pMainWnd

-在CChildFrame中可用GetParentFrame()

-在其它类中用AfxGetMainWnd()

获得CChildFrame:

-在CView中用GetParentFrame()

-在CMainFrame中用MDIGetActive()或GetActiveFrame()

-在其它类中用AfxGetMainWnd()->MDIGetActive()或AfxGetMainWnd()->GetActiveFrame()

获得CDocument:

-在CView中用GetDocument()

-在CChildFrame中用GetActiveView()->GetDocument()

-在CMainFrame中用

-ifSDI:GetActiveView()->GetDocument()

-ifMDI:MDIGetActive()->GetActiveView()->GetDocument()

-在其它类中

-ifSDI:AfxGetMainWnd()->GetActiveView()->GetDocument()

-if

MDI:AfxGetMainWnd()->MDIGetActive()->GetActiveView()->GetDocument()

获得CView:

-在CDocument中POSITIONpos=GetFirstViewPosition();GetNextView(pos)

-在CChildFrame中GetActiveView()

-在CMainFrame中

-ifSDI:GetActiveView()

-ifMDI:MDIGetActive()->GetActiveView()

-在其它类中

-ifSDI:AfxGetMainWnd()->GetActiveView()

-ifMDI:AfxGetMainWnd()->MDIGetActive()->GetActiveView()

不过要注意在doc中要取得view的指针C*View要注意类C*View声明的问题,

因为默认情况下,mfc在*View.h中已经包含了*Doc.h,如果在*Doc.h中包含

*View.h,就会引起嵌套包含问题,这样要在*Doc.h中加入classC*View;

而在*Doc.cpp中加入#include"*View.h"

转接相关图片联系:

实现这个工程的资源链接:http://download.csdn.NET/detail/qq_24571549/9778806

6.MFC框架间联立的方法

一:句柄关联性;

①接获取句柄或者窗口对象:Findwindow和FromHandle

②GetWindow加上FromHandle(hWnd);联立获取、

③获取主窗口句柄:

CWnd *wnd = AfxGetMainWnd();

HWND hwnd =wnd->GetSafeHwnd();

④获取当前最上层窗口的句柄:

HWNDmainHwnd = ::GetForegroundWindow();//获取当前topmost的窗口句柄

⑤附加一些其它函数:

FindWindow //这个估计会拖慢程序的运行速率

FindWindowEx

WindowFromPoint//获得当前鼠标光标位置的窗口HWND

GetActiveWindow 取当前活动窗口句柄

AfxGetMainWnd 取主窗口句柄//获得主框架窗口指针(任何时候都可以用,只要是MFC程序中)

GetForegroundWindow取前台窗口句柄

FindWindow寻找参数指定的窗口

EnumWindow枚举窗口

(二)MFC【MFC AppWizard(exe)框架】中各个框架间指针关联性:

一、一般的框架(this间的使用):<注意获取到的指针进行强制类型转换>

理解 m_pMainWnd, AfxGetApp(),AfxGetMainWnd() 的意义<最终归结首者>

1)获得Doc指针GetDocument();一个视只能有一个文档。

2)获得MainFrame指针CWinApp 中的 m_pMainWnd变量就是MainFrame的指针,也可以: AfxGetMainWnd

3) 获得MainFrame指针

CMainFrame*pMain=(CmaimFrame *)AfxGetApp()->m_pMainWnd;

4) 获得View(已建立)指针

CMainFrame*pMain=(CmaimFrame *)AfxGetApp()->m_pMainWnd;

CyouView*pView=(CyouView *)pMain->GetActiveView();

5) 获得当前文档指针

CDocument *pCurrentDoc =(CFrameWnd *)m_pMainWnd->GetActiveDocument();

6) 获得状态栏与工具栏指针

CStatusBar *pStatusBar=(CStatusBar*)AfxGetMainWnd()-

>GetDescendantWindow(AFX_IDW_STATUS_BAR);

CToolBar *pToolBar=(CtoolBar *)AfxGetMainWnd()->GetDescendantWindow

(AFX_IDW_TOOLBAR);

7) 如果框架中加入工具栏和状态栏变量还可以这样

(CMainFrame*)GetParent()->m_wndToolBar;

(CMainFrame*)GetParent()->m_wndStatusBar;

8) 在Mainframe获得菜单指针

CMenu*pMenu=m_pMainWnd->GetMenu();

9)在任何类中获得应用程序类

AfxGetInstanceHandle得到句柄,AfxGetApp 得到指针

(三)MFC中句柄、指针、ID之间的转换使用

win32直接操作的是句柄HANDLE,每个句柄就对应windows窗口,而vc对HANDLE进行类封装,间接操作的都是HANDLE,现在句柄只是类的一个成员变量。

从句柄到指针

CWnd*pWnd=CWnd::FromHandle(hWnd); //a temporary CWnd object is

created //andattached.

pWnd->Attach(hWnd);//Attaches a Windows window to a CWnd object

从指针到句柄

HWNDhWnd=pWnd->GetSafeHandle(); //使用GetSafeHwnd函数取得程序所在窗口类的句柄

hWnd=pWnd->m_hWnd;

SDK编程中窗口ID,句柄,指针三者相互转换函数

ID--HANDLE--HWND三者之间的互相转换

id->句柄-----------hWnd =::GetDlgItem(hParentWnd,id);

id->指针-----------CWnd::GetDlgItem();

句柄->id-----------id = GetWindowLong(hWnd,GWL_ID);

句柄->指针--------CWnd*pWnd=CWnd::FromHandle(hWnd);

指针->ID----------id = GetWindowLong(pWnd->GetSafeHwnd,GWL_ID);

GetDlgCtrlID();

指针->句柄--------hWnd=cWnd.GetSafeHandle()or mywnd->m_hWnd;

HICON->ID--------HICONhIcon = AfxGetApp()->LoadIcon(nIconID);

HICON hIcon =LoadIcon(AfxGetApp()-

>m_hInstance,MAKEINTRESOURCE(nIconID));

7.Document(文档)、View(视图)、Frame(框架)、App(应用)之间相互访问

Document(文档)、View(视图)、Frame(框架)、App(应用)之间相互访问的

纸上得来终觉浅,为了熟悉获取方法,我建了个SDI。

首先说明这四个类的执行顺序是App->Doc->Main->View

另外添加CDialog类获得各个指针的方法。

多文档的获取有点小区别,有时间也总结一下。

view plaincopy

在CODE上查看代码片
派生到我的代码片

//App

voidCSDIApp::OnApp()

{

//App

//Doc

CDocument*pDoc=((CFrameWndEx*)m_pMainWnd)->GetActiveDocument();

//Main成员变量m_pMainWnd

CFrameWndEx*pMain=(CFrameWndEx*)AfxGetMainWnd();

//View

CView*pView=((CFrameWndEx*)m_pMainWnd)->GetActiveView();

}

//Doc

CSDIDoc::CSDIDoc()

{

//App

CWinAppEx*pApp=(CWinAppEx*)AfxGetApp();

//Doc

//Main

//Doc的创建先于Main

//View

//Doc的创建先于View

}

voidCSDIDoc::OnDoc()

{

//App

//同构造函数

//Doc

//Main

CFrameWndEx*pMain=(CFrameWndEx*)AfxGetMainWnd();

//View

CView*pView=(CView*)pMain->GetActiveView();//POSITIONpos=GetFirstViewPosition();pView=GetNextView(pos);}

//Main

CMainFrame::CMainFrame()

{

theApp.m_nAppLook=theApp.GetInt(_T("ApplicationLook"),ID_VIEW_APPLOOK_VS_2005);

//App

CWinAppEx*pApp=(CWinAppEx*)AfxGetApp();

//Doc

//构造函数里无法得到当前激活的Doc

//Main

//View

//构造函数里无法得到View指针,因为Main先于View创建。

}

voidCMainFrame::OnMain()

{

//App

//同构造函数

//Doc

CDocument*pDoc=(CDocument*)GetActiveDocument();

//Main

//View

CView*pView=(CView*)GetActiveView();

}

//View

CSDIView::CSDIView()

{

//App

CWinAppEx*pApp=(CWinAppEx*)AfxGetApp();

//Doc

/*无法在View的构造函数里得到Doc指针

GetDocument();实际上是返回m_pDocument

m_pDocument在OnCreate();里创建*/

//CDocument*pDoc=GetDocument();

//Main

//构造函数里无法得到MainFrame指针

//CFrameWndEx*pMain=(CFrameWndEx*)pApp->m_pMainWnd;

//View

}

voidCSDIView::OnView()

{

//App

//同构造函数

//Doc

CDocument*pDoc=GetDocument();

//Main

CFrameWndEx*pMain=(CFrameWndEx*)AfxGetMainWnd();

//View

}

//Dlg

CDlg::CDlg(CWnd*pParent/*=NULL*/)

:CDialog(CDlg::IDD,pParent)

{

//App

CWinAppEx*pApp=(CWinAppEx*)AfxGetApp();

//Doc

CDocument*pDoc=((CFrameWndEx*)AfxGetMainWnd())->GetActiveDocument();

//Main

CFrameWndEx*pMain=(CFrameWndEx*)AfxGetMainWnd();

//View

CView*pView=((CFrameWndEx*)AfxGetMainWnd())->GetActiveView();

}

参考:

MFC中Doc,View,MainFrmae,App各指针的互相获取

1) 在View中获得Doc指针

2) 在App中获得MainFrame指针

3) 在View中获得MainFrame指针

4) 获得View(已建立)指针

5) 获得当前文档指针

6) 获得状态栏与工具栏指针

7) 获得状态栏与工具栏变量

8) 在Mainframe获得菜单指针

9) 在任何类中获得应用程序类

10) 从文档类取得视图类的指针(1)

11) 在App中获得文档模板指针

12) 从文档模板获得文档类指针

13) 在文档类中获得文档模板指针

14) 从文档类取得视图类的指针(2)

15) 从一个视图类取得另一视图类的指针

VC中编程对于刚刚开始学习的同学,最大的障碍和问题就是消息机制和指针获取与操作。其实这些内容基本上是每本VC学习工具书上必讲的内容,而且通过MSDN很多问题都能解决。下面文字主要是个人在编程中指针使用的一些体会,说的不当的地方请指正。一般我们使用的框架是VC提供的Wizard生成的MFC App Wizard(exe)框架,无论是多文档还是单文档,都存在指针获取和操作问题。下面这节内容主要是一般 的框架,然后再讲多线程中的指针使用。使用到的类需要包含响应的头文件。首先一般获得本类(视,文档,对话框都支持)实例指针this,用this的目的,主要可以通过类中的函数向其他类或者函数中发指针,以便于在非本类中操作和使用本类中的功能。

锚点
锚点

1) 在View中获得Doc指针

CYouSDIDoc *pDoc=GetDocument();一个视只能有一个文档。

锚点
锚点

2) 在App中获得MainFrame指针

CWinApp 中的 m_pMainWnd变量就是MainFrame的指针

也可以: CMainFrame *pMain =(CMainFrame *)AfxGetMainWnd();

锚点
锚点

3) 在View中获得MainFrame指针

CMainFrame *pMain=(CmaimFrame *)AfxGetApp()->m_pMainWnd;

锚点
锚点

4) 获得View(已建立)指针

CMainFrame *pMain=(CmaimFrame *)AfxGetApp()->m_pMainWnd;

CyouView *pView=(CyouView *)pMain->GetActiveView();

锚点
锚点

5) 获得当前文档指针

CDocument * pCurrentDoc =(CFrameWnd *)m_pMainWnd->GetActiveDocument();

锚点
锚点

6) 获得状态栏与工具栏指针

CStatusBar * pStatusBar=(CStatusBar *)AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_STATUS_BAR);

CToolBar * pToolBar=(CtoolBar *)AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_TOOLBAR);

锚点
锚点

7) 如果框架中加入工具栏和状态栏变量还可以这样

(CMainFrame *)GetParent()->m_wndToolBar;

(CMainFrame *)GetParent()->m_wndStatusBar;

锚点
锚点

8) 在Mainframe获得菜单指针

CMenu *pMenu=m_pMainWnd->GetMenu();

锚点
锚点

9) 在任何类中获得应用程序类

用MFC全局函数AfxGetApp()获得。

锚点
锚点

10) 从文档类取得视图类的指针

从文档获得视图类指针目的一般为了控制同一文档的多个视图的定位问题,我的体会

特别是文字处理CEditView当产生多个视图类时,这个功能是非常需要的。

CDocument类提供了两个函数用于视图类的定位:

GetFirstViewPosition()和GetNextView()

virtual POSITION GetFirstViewPosition() const;

virtual CView* GetNextView(POSITION& rPosition) const;

注意:GetNextView()括号中的参数用的是引用方式,因此执行后值可能改变。

GetFirstViewPosition()用于返回第一个视图位置(返回的并非视图类指针,而是一

个POSITION类型值),GetNextView()有两个功能:返回下一个视图类的指针以及用

引用调用的方式来改变传入的POSITION类型参数的值。很明显,在Test程序中,只有

一个视图类,因此只需将这两个函数调用一次即可得到CTestView的指针如下(需定

义一个POSITION结构变量来辅助操作):

CTestView* pTestView;

POSITION pos=GetFirstViewPosition();

pTestView=GetNextView(pos);

这样,便可到了CTestView类的指针pTestView.执行完几句后,变量pos=NULL,因为没

有下一个视图类,自然也没有下一个视图类的POSITION.但是这几条语句太简单,不

具有太强的通用性和安全特征;当象前面说的那样,当要在多个视图为中返回某个指

定类的指针时,我们需要遍历所有视图类,直到找到指定类为止。判断一个类指针指

向的是否某个类的实例时,可用IsKindOf()成员函数时行检查,如:

pView->IsKindOf(RUNTIME_CLASS(CTestView));

即可检查pView所指是否是CTestView类。

有了以上基础,我们已经可以从文档类取得任何类的指针。为了方便,我们将其作

为一个文档类的成员函数,它有一个参数,表示要获得哪个类的指针。实现如下:

view plaincopy

在CODE上查看代码片
派生到我的代码片

CView*CTestDoc::GetView(CRuntimeClass*pClass)

{

CView*pView;

POSITIONpos=GetFirstViewPosition();

while(pos!=NULL){

pView=GetNextView(pos);

if(!pView->IsKindOf(pClass))

break;

}

if(!pView->IsKindOf(pClass)){

AfxMessageBox("ConntLocatetheView./r/nhttp://www.VCKBASE.com");

returnNULL;

}

returnpView;

}

其中用了两次视图类的成员函数IsKindOf()来判断,是因为退出while循环有三种可能:

1.pos为NULL,即已经不存在下一个视图类供操作;

2.pView已符合要求。

1和2同是满足。这是因为GetNextView()的功能是将当前视图指针改变成一个视图

的位置同时返回当前视图指针,因此pos是pView的下一个视图类的POSITION,完全

有可能既是pos==NULL又是pView符合需要。当所需的视图是最后一个视图是最后一

个视图类时就如引。因此需采用两次判断。

使用该函数应遵循如下格式(以取得CTestView指针为例):

CTestView* pTestView=(CTestView*)GetView(RUNTIME_CLASS(CTestView));

RUNTIME_CLASS是一个宏,可以简单地理解它的作用:将类的名字转化为

CRuntimeClass为指针。至于强制类型转换也是为了安全特性考虑的,因为从同一个

基类之间的指针类型是互相兼容的。这种强制类型转换也许并不必要,但能避免一

些可能出现的麻烦。

3.从一个视图类取得另一视图类的指针 综合1和2,很容易得出视图类之间互相获得

指针的方法:就是用文档类作中转,先用1的方法得到文档类的指针,再用2的方法,

以文档类的视图定位函数取得另一个视图类。同样,可以实现成一个函数:

(假设要从CTestAView中取得指向其它视图类的指针)

view plaincopy

在CODE上查看代码片
派生到我的代码片

CView*CTestAView::GetView(CRuntimeClass*pClass)

{

CTestDoc*pDoc=(CTestDoc*)GetDocument();

CView*pView;

POSITIONpos=pDoc->GetFirstViewPosition();

while(pos!=NULL){

pView=pDoc->GetNextView(pos);

if(!pView->IsKindOf(pClass))

break;

}

if(!pView->IsKindOf(pClass)){

AfxMessageBox("ConntLocatetheView.");

returnNULL;

}

returnpView;

}

这个函数和2中的GetView()相比,一是多了第一句以取得文档类指针,二是在

GetFirstViewPosition()和GetNextView()前加上了文档类指针,以表示它们是文档

类成员函数。有了此函数;当要从CTestAView中取得CTestBView的指针时,只需如

下:CTestBView* pTestbView=(CTestView*)GetView(RUNTIME_CLASS(CTestBView));

11)对于单文档中也可以加入多个文档模板

但是一般的开发就使用MDI方式开发

多文档模板,其方法与上述视图的获取方法很接近,这里稍做解释,如果不清楚,

请查阅MSDN,(以下四个内容(11、12、13、14)来源:

可以用CWinApp::GetFirstDocTemplatePostion获得应用程序注册的第一个文档模板

的位置;利用该值来调用CWinApp::GetNextDocTemplate函数,获得第一个

CDocTemplate对象指针。 POSITION GetFirstDocTemplate( ) const;

CDocTemplate *GetNextDocTemplate( POSITION & pos ) const;

第二个函数返回由pos 标识的文档模板。POSITION是MFC定义的一个用于迭代或对象

指针检索的值。通过这两个函数,应用程序可以遍历整个文档模板列表。如果被检索

的文档模板是模板列表中的最后一个,则pos参数被置为NULL。

12)一个文档模板可以有多个文档,每个文档模板都保留并维护了一个所有对应文档的指针列表。

用CDocTemplate::GetFirstDocPosition函数获得与文档模板相关的文档集合中第一

个文档的位置,并用POSITION值作为CDocTemplate::GetNextDoc的参数来重复遍历与

模板相关的文档列表。函数原形为:

viaual POSITION GetFirstDocPosition( ) const = 0;

visual CDocument *GetNextDoc(POSITION & rPos) const = 0;

如果列表为空,则rPos被置为NULL.

13)在文档中可以调用CDocument::GetDocTemplate获得指向该文档模板的指针。

函数原形如下: CDocTemplate * GetDocTemplate ( ) const;

如果该文档不属于文档模板管理,则返回值为NULL。

14)一个文档可以有多个视。每一个文档都保留并维护一个所有相关视的列表。

CDocument::AddView将一个视连接到文档上,将该视加入到文档相联系的视的列表

中,并将视的文档指针指向该文档。当有File/New、File/Open、Windows/New或

Window/Split的命令而将一个新创建的视的对象连接到文档上时, MFC会自动调用

该函数,框架通过文档/视的结构将文档和视联系起来。当然,程序员也可以根据自

己的需要调用该函数。

Virtual POSITION GetFirstViewPosition( ) const;

Virtual CView * GetNextView( POSITION &rPosition) cosnt;

应用程序可以调用CDocument::GetFirstViewPosition返回与调用文档相联系的视的

列表中的第一个视的位置,并调用CDocument::GetNextView返回指定位置的视,并将

rPositon的值置为列表中下一个视的POSITION值。如果找到的视为列表中的最后一个

视,则将rPosition置为NULL.

15)从一个视图类取得另一视图类的指针

这个应用在多视的应用程序中很多见,一般如果自己在主程序或者主框架中做好变

量记号,也可以获得,还有比较通用的就是用文档类作中转,以文档类的视图

访问对象访问位置访问实现

应用程序App任何位置①AfxGetApp();

②在要使用应用程序App的文件中加入:

extern CAApp theApp,然后直接使用全局的theApp变量。

主框架窗口任何位置①AfxGetMainWnd();

②AfxGetApp()->m_pMainWnd;

视图框架类中GetActiveView();//当前的活动视图

文档类中GetFirstViewPosition();//可以获取全部视图

GetNextView();

文档文档类中GetDocument();

文当模版类中GetFirstDocPosition(); //该文档模版对应全部文档

GetNextDoc();

框架类中GetActiveDocument(); //当前活动文当

子框架类(MDI中)主框架类中①MDIGetActive();

②GetActiveFrame();

视图类中GetParentFrame();

文档模版文档类中GetDocTemplate();

应用程序App中GetFirstDocTemplatePosition();

GetNextDocTemplate();

说明:1)以上给出的都是方法,实际访问中可能还要进行以下简单的处理,如类型转换,循环遍历等;

2)可能没有列举完所有可能位置的互访问,但可以通过他们的组合得到。

相关TAG标签
上一篇:江门14岁少年获5项国家实用专利 灵感多来自生活
下一篇:1万元启动资金,公众号如何一个月内从0做到5万精准粉丝?
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站