MFC学习笔记

窗口对象和CWnd对象,Attach和Detach函数

  CWnd对象实际上并没有把整个Windows对象都包装在其中,它只是有一个窗口句柄。(在MSDN上查看CWnd类的成员变量,确实只有一个HWND hWnd,成员函数有Attach()和Dettach()、Create()等)。这个窗口句柄如果指向一个实际存在的窗口对象,那么这个CWnd对象就是有效的;否则是空的。

  如果你还不明白,请回忆一下,当我们使用MFC创建一个窗口时,是分两步进行的:

  1. new一个CWnd对象,但其中的HWND还是非法的,因为对应的窗口对象还没有被创建出来;
  2. 调用CWnd的成员函数Create创建真正的窗口对象,同时,把先前创建的MFC的CWnd对象的HWND成员指向该窗口,这样才算创建完毕一个窗口。

  而如果你是用SDK方式,那么只要创建一个WNDCLASS结构,然后调用Create或者CreateEx就创建了一 个窗口。

  让一个有效窗口句柄和一个CWnd对象关联起来用Attach:就是让一个CWnd对象的HWND成员等于这个窗口句柄;Attach,通俗地说,就是切断一个CWnd对象和一个有效窗口的脐带。因为CWnd是C++的对象,C++的对象有一个生存期的概念,脱离了该对象的作用域,这个对象就要被销毁,但是窗口对象没有这个特点,当销毁 CWnd对象的时候,我们不一定希望WNDCLASS一起被销毁,那么在此之前,我们就先要把这个“脐带”剪断,以免“城门失火,殃及池鱼”。

不要在子线程中操作MFC控件  

  不要在线程函数体内操作MFC控件,因为每个线程都有自己的线程模块状态映射表,在一个线程中操作另一个线程中创建的MFC对象,会带来意想不到的问题。更不要在线程函数里,直接调用UpdataData()函数更新用户界面,这会导致程序直接crash。而应该通过发送消息给主线程的方式,在主线程的消息响应函数里操作控件

类之间的相互访问

  MFC中一个类要访问另外一个类的的对象的成员变量值,就需要获得原来那个类对象的指针,其实有好几种方法都可以实现。比如维护一个单例模式、设置静态变量等等。这里举个例子,实现多个类之间的相互访问。

(1)创建一个MFC对话框应用程序,命名为Visit工程,对话框本身有一个主界面(CVisitDlg对话框),我们再添加一个新界面CXXXDlg。

(2)在主界面CVisitDlg类的头文件中,添加一个static CVisitDlg *s_pDialog;指针。由于这个指针式静态的,需要在类外初始化,那么在CVisitDlg .cpp文件中,写上一行初始化代码,直接初始化为空,如下:

CVisitDlg *CVisitDlg::s_pDialog = NULL; //注意要写在类外,不要写在类实现函数里面

(3)然后需要在CVisitDlg主对话框生成的时候,给这个指针赋值为主对话框指针,在CVisitDlg 类的构造函数或者OnInitDialog()函数里面写上如下一句代码:

s_pDialog = this;

  现在只要在其他的类里面获得这个静态指针,就可以访问这个类里面的所有数据了。

(4)获得静态指针,如果在CXXXDlg类中访问CVisitDlg类的数据,如下代码即可实现:

CVisitDlg *pDia = CVisitDlg::s_pDialog

注意: 

  初始化是赋一个初始值,而定义是分配内存。

  静态成员变量在类中仅仅是声明,没有定义,所以要在类的外面定义,实际上是给静态成员变量分配内存

  对于类来说,new一个类对象不仅会分配内存,同时会调用构造函数进行初始化,所以类对象的定义和初始化总是关联在一起。

解决MFC启动时隐藏的闪烁问题

(1)VC++ 6.0开发的单文档程序

  在BOOL CXXXXApp::InitInstance()方法中添加如下代码:

m_nCmdShow = SW_HIDE;if (!ProcessShellCommand(cmdInfo))    return FALSE;

  即在"if (!ProcessShellCommand(cmdInfo))”在这一句的上方加一句代码"m_nCmdShow = SW_HIDE;"即可

(2)VC2010开发的单文档程序

  • 在InitInstance函数中ParseCommandLine(cmdInfo)语句之后添加语句m_bLoadWindowPlacement=FALSE
  • 在InitInstance函数中m_pMainWnd->ShowWindow(SW_SHOW)语句中的SW_SHOW改为SW_HIDE
  • 在mainframe中还要实现虚函数ActivateFrame,把参数m_nCmdShow置为SW_HIDE;

设置透明窗体

//透明窗体SetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE,GetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE)^0X80000);HINSTANCE hInst = LoadLibrary("User32.DLL");if(hInst){    typedef BOOL (WINAPI *MYFUNC)(HWND,COLORREF,BYTE,DWORD);    MYFUNC fun = NULL;    //取得SetLayeredWindowAttributes函数指针    fun=(MYFUNC)GetProcAddress(hInst,"SetLayeredWindowAttributes");    if(fun)        fun(this->GetSafeHwnd(),0,128,2);    FreeLibrary(hInst);}

GetDeviceCaps获取与修改显示器属性

int pixelxperinch = dc.GetDeviceCaps(LOGPIXELSX);//每英寸水平逻辑像素数int pixelyperinch = dc.GetDeviceCaps(LOGPIXELSY);//每英寸垂直逻辑像素数int pixelx = dc.GetDeviceCaps(HORZRES);//水平像素总数int pixely = dc.GetDeviceCaps(VERTRES);//垂直像素总数int hmm = dc.GetDeviceCaps(HORZSIZE);//水平毫米数int vmm = dc.GetDeviceCaps(VERTSIZE);//垂直毫米

  以上三者的关系通常满足:HORZSIZE = 25.4 * HORZRES / LOGPIXELSX

  获取和设置显示器属性的代码如下所示:

//创建显示设备上下文HDC hdc = CreateDC(_T("display"), NULL, NULL, NULL);//********** 获取显示器属性 **********//颜色深度int nBitsPerPixel=GetDeviceCaps(hdc, BITSPIXEL);//水平分辨率int nWidth = GetDeviceCaps(hdc, HORZRES);//垂直分辨率int nHeight = GetDeviceCaps(hdc, VERTRES);     //刷新率int nDisplayFrequency = GetDeviceCaps(hdc, VREFRESH); //********** 设置显示器属性 **********DEVMODE DevMode;//颜色深度DevMode.dmBitsPerPel = 16;//分辨率DevMode.dmPelsWidth = 800;DevMode.dmPelsHeight = 600;//刷新率DevMode.dmDisplayFrequency = 60;//设置显示属性LONG nResult = ChangeDisplaySettings(&DevMode, 0);if (nResult == DISP_CHANGE_SUCCESSFUL) {    //用新的设置参数更新注册表    ChangeDisplaySettings(&DevMode, CDS_UPDATEREGISTRY);    AfxMessageBox(_T("设置显示属性成功。"));}else {    //恢复默认设置    ChangeDisplaySettings(NULL, 0);    AfxMessageBox(_T("设置显示属性失败。"));} //设置显示器为省电模式::SendMessage(m_hWnd, WM_SYSCOMMAND, SC_MONITORPOWER, 1);//打开显示器::SendMessage(m_hWnd, WM_SYSCOMMAND, SC_MONITORPOWER, -1);//关闭显示器::SendMessage(m_hWnd, WM_SYSCOMMAND, SC_MONITORPOWER, 2);

GetSystemMetrics

1 int i; 2 i = GetSystemMetrics(SM_CLEANBOOT);//启动方式 0 3 i = GetSystemMetrics(SM_CMONITORS);//显示器数目 1 4 i = GetSystemMetrics(SM_CMOUSEBUTTONS);//鼠标上按键的数目 3 5 i = GetSystemMetrics(SM_CXBORDER);//窗口边框的宽度,3D外观下与SM_CXEDGE值相同 1 6 i = GetSystemMetrics(SM_CXCURSOR);//光标的宽度 32 7 i = GetSystemMetrics(SM_CXDLGFRAME);//与SM_CXFIXEDFRAME相同 3 8 i = GetSystemMetrics(SM_CXDOUBLECLK);//鼠标在某个矩形内连击两次被认为是双击,该矩形的宽度 4 9 i = GetSystemMetrics(SM_CXDRAG);//鼠标在某个矩形内单击移动被认为是拖曳,该矩形的宽度 410 i = GetSystemMetrics(SM_CXEDGE);//SM_CXBORDER在3D外观下的值 211 i = GetSystemMetrics(SM_CXFIXEDFRAME);//具有title bar和固定border的窗体的border的宽度 312 i = GetSystemMetrics(SM_CXFOCUSBORDER);//DrawFocusRect画出的矩形的左右边框的 113 i = GetSystemMetrics(SM_CXFRAME);//与SM_CXSIZEFRAME相同 414 i = GetSystemMetrics(SM_CXFULLSCREEN);//客户区(工作区)的宽度 128015 i = GetSystemMetrics(SM_CXHSCROLL);//水平滚动条上箭头位图的宽度 1716 i = GetSystemMetrics(SM_CXHTHUMB);//水平滚动条滑块的宽度 1717 i = GetSystemMetrics(SM_CXICON);//图标的默认宽度,LoadIcon只能载入该宽度和SM_CYICON指定高度的icon 3218 i = GetSystemMetrics(SM_CXICONSPACING);//icon view中各icon的所占矩形的宽度,大于等于SM_CXICON 7519 i = GetSystemMetrics(SM_CXMAXIMIZED);//最大化顶级窗口的宽度 128820 i = GetSystemMetrics(SM_CXMAXTRACK);//一个具有title bar和sizable的窗口所能达到的最大宽度 129221 i = GetSystemMetrics(SM_CXMENUCHECK);//菜单上位图的宽度 1322 i = GetSystemMetrics(SM_CXMENUSIZE);//菜单条按钮的宽度,例如多文档中ChildFrame(最大化时)右上角的关闭按钮 1923 i = GetSystemMetrics(SM_CXMIN);//窗体的最小宽度 12324 i = GetSystemMetrics(SM_CXMINIMIZED);//最小化窗体的宽度 16025 i = GetSystemMetrics(SM_CXMINSPACING);//各个最小化窗体所占的矩形的宽度,大于等于SM_MINIMIZED 16026 i = GetSystemMetrics(SM_CXMINTRACK);//窗口能拖拽的最小宽度 12327 i = GetSystemMetrics(SM_CXSCREEN);//屏幕宽度 128028 i = GetSystemMetrics(SM_CXSIZE);//title bar上按钮的宽度 2529 i = GetSystemMetrics(SM_CXSIZEFRAME);//sizable窗体的横向border的宽度 430 i = GetSystemMetrics(SM_CXSMICON);//推荐的小图标(如title bar上的和icon view中的)的宽度 1631 i = GetSystemMetrics(SM_CXSMSIZE);//caption中图标的宽度 1732 i = GetSystemMetrics(SM_CXVIRTUALSCREEN);//virutal screen的宽度 128033 i = GetSystemMetrics(SM_CXVSCROLL);//垂直滚动条的宽度 1734 i = GetSystemMetrics(SM_CYBORDER);// 135 i = GetSystemMetrics(SM_CYCAPTION);// 2636 i = GetSystemMetrics(SM_CYCURSOR);// 3237 i = GetSystemMetrics(SM_CYDLGFRAME);// 338 i = GetSystemMetrics(SM_CYDOUBLECLK);// 439 i = GetSystemMetrics(SM_CYDRAG);// 440 i = GetSystemMetrics(SM_CYEDGE);// 241 i = GetSystemMetrics(SM_CYFIXEDFRAME);// 342 i = GetSystemMetrics(SM_CYFOCUSBORDER);// 143 i = GetSystemMetrics(SM_CYFRAME);// 444 i = GetSystemMetrics(SM_CYFULLSCREEN);// 74445 i = GetSystemMetrics(SM_CYHSCROLL);// 1746 i = GetSystemMetrics(SM_CYICON);// 3247 i = GetSystemMetrics(SM_CYICONSPACING);// 7548 i = GetSystemMetrics(SM_CYMAXIMIZED);// 77849 i = GetSystemMetrics(SM_CYMAXTRACK);// 81250 i = GetSystemMetrics(SM_CYMENU);// 2051 i = GetSystemMetrics(SM_CYMENUCHECK);// 1352 i = GetSystemMetrics(SM_CYMIN);// 34=4+4+2653 i = GetSystemMetrics(SM_CYMINIMIZED);// 3154 i = GetSystemMetrics(SM_CYMINSPACING);// 3155 i = GetSystemMetrics(SM_CYMINTRACK);// 3456 i = GetSystemMetrics(SM_CYSCREEN);// 80057 i = GetSystemMetrics(SM_CYSIZE);// 2558 i = GetSystemMetrics(SM_CYSIZEFRAME);// 459 i = GetSystemMetrics(SM_CYSMCAPTION);// 1860 i = GetSystemMetrics(SM_CYSMICON);// 1661 i = GetSystemMetrics(SM_CYSMSIZE);// 1762 i = GetSystemMetrics(SM_CYVIRTUALSCREEN);// 80063 i = GetSystemMetrics(SM_CYVSCROLL);// 1764 i = GetSystemMetrics(SM_CYVTHUMB);// 17

MFC坐标转换

  GetWindowRect是取得窗口在屏幕坐标系下的RECT坐标(包括客户区和非客户区),这样可以得到窗口的大小和相对屏幕左上角(0,0)的位置。

  GetClientRect取得窗口客户区(不包括非客户区)在客户区坐标系下的RECT坐标,可以得到窗口的大小,而不能得到相对屏幕的位置,因为这个矩阵是在客户区坐标系下(相对于窗口客户区的左上角)的。  

  ClientToScreen把客户区坐标系下的RECT坐标转换为屏幕坐标系下的RECT坐标。

  ScreenToClient把屏幕坐标系下的RECT坐标转换为客户区坐标系下的RECT坐标。

  我们对同一个窗口先GetWindowRect取得一个RECT,再用ScreenToClient转换到客户坐标系。然后 GetClientRect取得一个RECT,再用ClientToScreen转换到屏幕坐标系。显然,GetWindowRect取得的矩阵不小于 GetClientRect取得的矩阵。因为前者包含了非客户区,而后者包括了客户区。   

  • 对GetWindowRect取得的矩阵ScreenToClient后,矩阵的大小没有变小,(-3,-29)是窗口的左上角的坐标,相对窗口客户区左上角;   
  • 对GetClientRect取得的矩阵ClientToScreen后,矩阵也没有变大,新得到的矩阵是窗口客户区在屏幕坐标系上的RECT;

对话框全屏

//删除WS_CAPTION和WS_BORDER风格ModifyStyle(WS_CAPTION, 0);ModifyStyle(WS_BORDER, 0);//获得屏幕长度和高度int cx = GetSystemMetrics(SM_CXSCREEN);int cy = GetSystemMetrics(SM_CYSCREEN);//设置对话框位置和大小SetWindowPos(NULL, 0, 0 , cx, cy, SWP_NOZORDER)

键盘消息

(1)组合按键

BOOL CMyDlg::PreTranslateMessage(MSG* pMsg)   {    if(pMsg->message == WM_KEYDOWN)    {        // 组合键响应keydown消息        if( pMsg->wParam == VK_SPACE && (GetKeyState(VK_SHIFT) & 0x8000))        {            // 空格 + Shift        }    }}

(2)屏蔽ESC键

BOOL CColorDlgDlg::PreTranslateMessage(MSG* pMsg){    //屏蔽ESC关闭窗体/    if(pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_ESCAPE ) return TRUE;    //屏蔽回车关闭窗体,但会导致回车在窗体上失效.    //if(pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_RETURN && pMsg->wParam) return TRUE;    else        return CDialog::PreTranslateMessage(pMsg);}

(3)屏蔽Alt+F4

BOOL CTestApp::PreTranslateMessage(MSG* pMsg){    // TODO: 在此添加专用代码和/或调用基类    if(pMsg->message == WM_SYSKEYDOWN        && pMsg->wParam == VK_F4)        return TRUE;    return CWinAppEx::PreTranslateMessage(pMsg);}

添加ToolTip提示框

为窗口或其中的控件添加提示框,可以使用MFC的类CToolTipCtrl,使用方法如下:

(1)在窗口的类定义中添加变量说明:

class CTooltipTestDlg : public Cdialog{public:    CToolTipCtrl m_tt;}

(2)在对话框的OnInitDialog()函数中添加如下代码:

EnableToolTips(TRUE);m_tt.Create(this);m_tt.Activate(TRUE);CWnd* pW=GetDlgItem(IDC_CHECK1);//得到控件的指针m_tt.AddTool(pW,L"Check1lakjsfasfdasfd");//为此控件添加tip

(3)重载父窗口的 BOOL PreTranslateMessage(MSG* pMsg) ,在函数中调用 m_tt.RelayEvent(pMsg):

BOOL CTooltipTestDlg::PreTranslateMessage(MSG* pMsg){    // TODO: Add your specialized code here and/or call the base class    if (NULL != m_tt.GetSafeHwnd())    {        m_tt.RelayEvent(pMsg);    }    return CDialog::PreTranslateMessage(pMsg);}

  这样就完成了为控件添加Tip。

  如果想修改已添加的tip的内容,可以使用UpdateTipText函数,如下

CWnd* pW=GetDlgItem(IDC_CHECK1);//得到已添加tip控件m_tt.UpdateTipText(L"asdflasdf",pW);//更新tip的内容

  其他控制函数具体可查MSDN的CToolTipCtrl类。

  对于静态文本框,要把Notify的属性设为TRUE;而如果静态文本控件是动态创建的,必须给窗口风格添加SS_NOTIFY,如

m_StaticText.Create(_T("my static"), WS_CHILD|WS_VISIBLE|WS_BORDER|SS_NOTIFY,CRect(10,10,150,50),this);

鼠标经过控件时呈手形

//响应WM_SETCURSOR消息BOOL CAboutDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message){     // TODO: 在此添加消息处理程序代码和/或调用默认值     //m_hCursor=AfxGetApp()->LoadCursor(IDC_HAND);     //文本框IDC_STATIC_HOMEPAGE_LINK的Notify设为true     if(pWnd==GetDlgItem(IDC_STATIC_HOMEPAGE_LINK))     {              SetCursor(LoadCursor(NULL,IDC_HAND));              return true;//必须加上     }     return CDialogEx::OnSetCursor(pWnd, nHitTest, message);}

Bmp图片显示

HBITMAP  hBitmap = (HBITMAP)LoadImage(    NULL,    _T(“xxx.bmp”),    Image_Bitmap,    0,0,    Lr_CreateDibSection|Lr_DefaultSize|Lr_LoadFromFile);PWnd->ModifyStytle(0,SS_BITMAP);PWnd->SetBitmap(hBitmap);

对话框bmp背景图片显示

在对话框的OnEraseBkgnd(CDC *pDC) 函数中添加如下代码:

HBITMAP  hBitmap = (HBITMAP)Load Image(    NULL,    _T(“xxx.bmp”),    Image_Bitmap,    0,0,    Lr_CreateDibSection|Lr_DefaultSize|Lr_LoadFromFile);CBitmap bitmap;bitmap.Attach(hBitmap); //关联位图对象BITMAP bmp;Bitmap.GetBitmap(&bmp); //获取位图信息CDC compatibleDC;compatibleDC.CreateCompatibleDC(pDC); //内存compatibleDC. SelectObject(&bitmap); //选取位图对象Crect rect;GetClientRect(&rect);pDC->StretchBlt(    rect.left, rect.top, rect.right, rect.bottom,    &compatibleDC,    0, 0, bmp.bmWidth, bmp.bmHeight,    SRCCOPY);Return true;// 将OnEraseBkgnd(CDC *pDC) 函数中最后的return语句改为本句

解决StretchBlt()压缩图片失真

  StretchBlt函数缩放图片后图片失真严重,所以要用SetStretchBltMode函数来设置 StretchBlt(或StretchDIBits)函数的伸缩模式。具体用法是调用StretchBlt前调用:

SetStretchBltMode(pDC->m_hDC,STRETCH_HALFTONE);

  

GDI+加载透明png图片

(1)开始加入:

#include <comdef.h>//初始化一下com口#include "GdiPlus.h"using namespace Gdiplus;#pragma comment(lib,"gdiplus.lib")

(2)添加成员变量

ULONG_PTR m_gdiplusToken;

(3)初始化时,启动GDI+

GdiplusStartupInput gdiplusStartupInput;GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);

(4)退出时,结束GDI+

GdiplusShutdown(m_gdiplusToken);

(5)在OnPaint或OnDraw函数中,显示图片

CClientDC *pDC = new CClientDC(GetDlgItem(IDC_STATIC_PIC));CRect rect;GetDlgItem(IDC_STATIC_PIC)->GetWindowRect(&rect);Image image(_T("1.png")); // Construct an image//或 Image::FromFile(strPath);Graphics graphics(pDC->m_hDC); // Create a GDI+ graphics objectgraphics.DrawImage(&image, 0, 0, image.GetWidth(), image.GetHeight());delete pDC;//或ReleaseDC

窗口重绘

  RedrawWindow函数引发重新绘制,使用flags参数控制。

  InvalidateRect直接无效某个区域,指定是否刷新背景。

  如果您的绘制变化较多,有时有闪烁,可以用LockWindowUpdate、UnlockWindowUpdate或者CWnd::SetRedraw控制,最后结束了,允许绘制,再无效待绘制区域Invalidate。

可视动画控件ActiveMovie

  可视动画控件ActiveMovie是Microsoft公司开发的ActiveX控件,从开始的1.0版、1.2版到现在的2.0版,功能上已经有了很大的改进。由于该控件内嵌了Microsoft MPEG音频解码器和Microsoft MPEG视频解码器,所以能够很好地支持音频文件和视频文件,用其播放的VCD效果就很好。另外,播放时若用鼠标右键单击画面,可以直接对画面的播放、暂停、停止等进行控制,读者还可以自行在"属性"栏中对影片播放进行控制设置,用起来非常方便。当前在Microsoft公司推出的Visual C++6.0中已经包含了ActiveMovie控件的2.0版。

在Visual C++6.0中,一般情况都是在基于对话框的应用程序中使用ActiveMovie控件,可在菜单中依次选择"project- >Add To Project- >Components And Controls",在出现的"Components And Controls Gallery"对话框中打开"Registered Active Controls"文件夹,选中"ActiveMovie Control Object"选项,按"Insert"按钮后关闭该对话框,ActiveMovie控件便出现在程序编辑器的控件面板中。

可是我按本法操作,却找不到此控件。

解决办法:

打开"开始",运行 regsvr32 msdxm.ocx,有时候会发生注册失败的情况,这时必须关掉VS6及相关的音频播放软件,才能注册成功。

MFC operator new: 没有重载函数接受 3 个参数

  解决办法:在new前添加全局作用域::

程序打包

  在没有安装 VC 的计算机上运行VC程序时要从 VC 中把 mscomm32.ocx、 msvcrt.dll、 mfc42.dll 拷到 Windows 目录下的 System 子目录中(win2000 为 System32),对于还没有注册的控件,例如自己开发出来的控件,可以借助regsvr32.exe注册,具体做法是:开始菜单->运行->regsvr32.exe 控件文件名.ocx(或者是控件文件名.dll),注意控件文件名处需要用绝对路径,例如regsvr32.exe C:/progrm files/myActiveXControl.ocx;你也可以卸载已注册的控件(也即反注册),命令格式如下:regsvr32.exe/u 控件文件名.ocx(或者是控件文件名.dll)

  相关链接:http://blog.csdn.net/testcs_dn/article/details/26976555

(0)

相关推荐