MFC学习笔记

时间:2022-11-15 09:47:24

获取窗口句柄

  • FindWindow                根据窗口名获取
  • GetSafehWnd                取你程序所在窗口类的句柄
  • GetActiveWindow          取当前活动窗口句柄
  • AfxGetMainWnd            取主窗口句柄
  • GetForegroundWindow   取前台窗口句柄

窗口对象和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(),,,);
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 = ; //分辨率
DevMode.dmPelsWidth = ;
DevMode.dmPelsHeight = ; //刷新率
DevMode.dmDisplayFrequency = ; //设置显示属性
LONG nResult = ChangeDisplaySettings(&DevMode, );
if (nResult == DISP_CHANGE_SUCCESSFUL)
{
//用新的设置参数更新注册表
ChangeDisplaySettings(&DevMode, CDS_UPDATEREGISTRY);
AfxMessageBox(_T("设置显示属性成功。"));
}
else
{
//恢复默认设置
ChangeDisplaySettings(NULL, );
AfxMessageBox(_T("设置显示属性失败。"));
} //设置显示器为省电模式
::SendMessage(m_hWnd, WM_SYSCOMMAND, SC_MONITORPOWER, ); //打开显示器
::SendMessage(m_hWnd, WM_SYSCOMMAND, SC_MONITORPOWER, -); //关闭显示器
::SendMessage(m_hWnd, WM_SYSCOMMAND, SC_MONITORPOWER, );

GetSystemMetrics

 int i;
i = GetSystemMetrics(SM_CLEANBOOT);//启动方式 0
i = GetSystemMetrics(SM_CMONITORS);//显示器数目 1
i = GetSystemMetrics(SM_CMOUSEBUTTONS);//鼠标上按键的数目 3
i = GetSystemMetrics(SM_CXBORDER);//窗口边框的宽度,3D外观下与SM_CXEDGE值相同 1
i = GetSystemMetrics(SM_CXCURSOR);//光标的宽度 32
i = GetSystemMetrics(SM_CXDLGFRAME);//与SM_CXFIXEDFRAME相同 3
i = GetSystemMetrics(SM_CXDOUBLECLK);//鼠标在某个矩形内连击两次被认为是双击,该矩形的宽度 4
i = GetSystemMetrics(SM_CXDRAG);//鼠标在某个矩形内单击移动被认为是拖曳,该矩形的宽度 4
i = GetSystemMetrics(SM_CXEDGE);//SM_CXBORDER在3D外观下的值 2
i = GetSystemMetrics(SM_CXFIXEDFRAME);//具有title bar和固定border的窗体的border的宽度 3
i = GetSystemMetrics(SM_CXFOCUSBORDER);//DrawFocusRect画出的矩形的左右边框的 1
i = GetSystemMetrics(SM_CXFRAME);//与SM_CXSIZEFRAME相同 4
i = GetSystemMetrics(SM_CXFULLSCREEN);//客户区(工作区)的宽度 1280
i = GetSystemMetrics(SM_CXHSCROLL);//水平滚动条上箭头位图的宽度 17
i = GetSystemMetrics(SM_CXHTHUMB);//水平滚动条滑块的宽度 17
i = GetSystemMetrics(SM_CXICON);//图标的默认宽度,LoadIcon只能载入该宽度和SM_CYICON指定高度的icon 32
i = GetSystemMetrics(SM_CXICONSPACING);//icon view中各icon的所占矩形的宽度,大于等于SM_CXICON 75
i = GetSystemMetrics(SM_CXMAXIMIZED);//最大化*窗口的宽度 1288
i = GetSystemMetrics(SM_CXMAXTRACK);//一个具有title bar和sizable的窗口所能达到的最大宽度 1292
i = GetSystemMetrics(SM_CXMENUCHECK);//菜单上位图的宽度 13
i = GetSystemMetrics(SM_CXMENUSIZE);//菜单条按钮的宽度,例如多文档中ChildFrame(最大化时)右上角的关闭按钮 19
i = GetSystemMetrics(SM_CXMIN);//窗体的最小宽度 123
i = GetSystemMetrics(SM_CXMINIMIZED);//最小化窗体的宽度 160
i = GetSystemMetrics(SM_CXMINSPACING);//各个最小化窗体所占的矩形的宽度,大于等于SM_MINIMIZED 160
i = GetSystemMetrics(SM_CXMINTRACK);//窗口能拖拽的最小宽度 123
i = GetSystemMetrics(SM_CXSCREEN);//屏幕宽度 1280
i = GetSystemMetrics(SM_CXSIZE);//title bar上按钮的宽度 25
i = GetSystemMetrics(SM_CXSIZEFRAME);//sizable窗体的横向border的宽度 4
i = GetSystemMetrics(SM_CXSMICON);//推荐的小图标(如title bar上的和icon view中的)的宽度 16
i = GetSystemMetrics(SM_CXSMSIZE);//caption中图标的宽度 17
i = GetSystemMetrics(SM_CXVIRTUALSCREEN);//virutal screen的宽度 1280
i = GetSystemMetrics(SM_CXVSCROLL);//垂直滚动条的宽度 17
i = GetSystemMetrics(SM_CYBORDER);//
i = GetSystemMetrics(SM_CYCAPTION);//
i = GetSystemMetrics(SM_CYCURSOR);//
i = GetSystemMetrics(SM_CYDLGFRAME);//
i = GetSystemMetrics(SM_CYDOUBLECLK);//
i = GetSystemMetrics(SM_CYDRAG);//
i = GetSystemMetrics(SM_CYEDGE);//
i = GetSystemMetrics(SM_CYFIXEDFRAME);//
i = GetSystemMetrics(SM_CYFOCUSBORDER);//
i = GetSystemMetrics(SM_CYFRAME);//
i = GetSystemMetrics(SM_CYFULLSCREEN);//
i = GetSystemMetrics(SM_CYHSCROLL);//
i = GetSystemMetrics(SM_CYICON);//
i = GetSystemMetrics(SM_CYICONSPACING);//
i = GetSystemMetrics(SM_CYMAXIMIZED);//
i = GetSystemMetrics(SM_CYMAXTRACK);//
i = GetSystemMetrics(SM_CYMENU);//
i = GetSystemMetrics(SM_CYMENUCHECK);//
i = GetSystemMetrics(SM_CYMIN);// 34=4+4+26
i = GetSystemMetrics(SM_CYMINIMIZED);//
i = GetSystemMetrics(SM_CYMINSPACING);//
i = GetSystemMetrics(SM_CYMINTRACK);//
i = GetSystemMetrics(SM_CYSCREEN);//
i = GetSystemMetrics(SM_CYSIZE);//
i = GetSystemMetrics(SM_CYSIZEFRAME);//
i = GetSystemMetrics(SM_CYSMCAPTION);//
i = GetSystemMetrics(SM_CYSMICON);//
i = GetSystemMetrics(SM_CYSMSIZE);//
i = GetSystemMetrics(SM_CYVIRTUALSCREEN);//
i = GetSystemMetrics(SM_CYVSCROLL);//
i = GetSystemMetrics(SM_CYVTHUMB);//

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, );
ModifyStyle(WS_BORDER, ); //获得屏幕长度和高度
int cx = GetSystemMetrics(SM_CXSCREEN);
int cy = GetSystemMetrics(SM_CYSCREEN); //设置对话框位置和大小
SetWindowPos(NULL, , , 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(,,,),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,
,,
Lr_CreateDibSection|Lr_DefaultSize|Lr_LoadFromFile); PWnd->ModifyStytle(,SS_BITMAP);
PWnd->SetBitmap(hBitmap);

对话框bmp背景图片显示

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

HBITMAP  hBitmap = (HBITMAP)Load Image(
NULL,
_T(“xxx.bmp”),
Image_Bitmap,
,,
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,
, , bmp.bmWidth, bmp.bmHeight,
SRCCOPY); Return true;// 将OnEraseBkgnd(CDC *pDC) 函数中最后的return语句改为本句

解决StretchBlt()压缩图片失真

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

SetStretchBltMode(pDC->m_hDC,STRETCH_HALFTONE);

  MFC学习笔记

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 object
graphics.DrawImage(&image, , , 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