原文地址: https://blog.csdn.net/ling_xiao007/article/details/51721441
7.1更改窗口大小、标题、风格
7.1.1 在窗口创建之前更改
如果希望在应用程序窗口创建之前修改它的大小、标题和风格,应该在CMainFrame类的PreCreateWindow成员函数进行。该函数有个类型是CREATESTRUCT结构的参数,如果在修改了这个参数中的成员变量的值,那么这种改变会反映到MFC底层代码中,当MFC底层代码调用CreateWindowEx函数去创建窗口时,它就会使用改变后的参数值去创建这个窗口。
1)更改窗口大小
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT&cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
//TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
cs.cx = 400;
cs.cy = 200;
return TRUE;
}
2)更改应用程序标题
框架的默认窗口样式是WS_OVERLAPPEDWINDOW和FWS_ADDTOTITLE样式的组合。其中FWS_ADDTOTITLE是MFC特定的一种样式,指示框架将文档标题添加到窗口标题上。因此,如果想让窗口显示自己的标题,只需将窗口的FWS_ADDTOTITLE样式去掉即可。设置窗口标题的代码之前加上:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT&cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
//TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
cs.cx = 400;
cs.cy = 200;
cs.style = cs.style & ~FWS_ADDTOTITLE;
// cs.style = WS_OVERLAPPEDWINDOW;
cs.lpszName = "标题";
return TRUE;
}
7.1.2在窗口创建之后更改其风格
在应用程序窗口创建之后修改它的风格属性,可在CMainFrame类的OnCreate函数中调用SetWindowLong函数实现。
SetWindowLong(HWND hWnd, int nIndex, LONGdwNewLong)
该函数的作用是改变制定窗口的属性(包括设置新的窗口风格、设置新的窗口过程、设置新的应用程序实例局柄等)。要改变窗口的风格,则将该函数的第二个参数指定为GWL_STYLE,然后由第三个参数指定新的窗口风格。
如果是在已有类型的基础上进行修改的话,那么可以利用GetWindowLong这个函数获得这个窗口的现有类型,然后修改。例如:
SetWindowLong(m_hWnd, GWL_STYLE, GetWindowLong(m_hWnd, GWL_STYLE) & ~WS_MAXIMIZEBOX);
7.1.3 在窗口创建之后更改标题与大小
在应用程序窗口创建之后修改标题,可在CMainFrame类的OnCreate函数中调用SetWindowText函数实现。
在应用程序窗口创建之后修改大小,可在CMainFrame类的OnCreate函数中调用SetWindowPos函数实现。
7.2 修改光标、图标、背景
7.2.3 在窗口创建前更改
之前对于窗口的大小、标题和风格是在创建窗口时设定的。而光标、图标和背景是在设计窗口类时指定的。窗口类的设计与注册是由MFC底层代码自动完成的,我们不可能、也不应该去修改MFC底层代码。但是我们可以编写自己的窗口类注册,然后让随后的窗口按照我们编写的窗口类去创建。
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT&cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
…
WNDCLASSMyWnd;
MyWnd.cbClsExtra = NULL;MyWnd.cbWndExtra = NULL;
MyWnd.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
MyWnd.hCursor = LoadCursor(NULL, IDC_CROSS);
MyWnd.hIcon = LoadIcon(NULL, IDI_WARNING);
MyWnd.hInstance = AfxGetInstanceHandle();
MyWnd.lpfnWndProc = ::DefWindowProc;
MyWnd.lpszClassName = "Hello";
MyWnd.lpszMenuName = NULL;
MyWnd.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&MyWnd);
cs.lpszClass = "hello";
return TRUE;
}
上述代码的运行结果是:仅仅是程序的标题栏图标发生了改变,但窗口的背景和光标没有改变。原因是:视类窗口覆盖在主窗口上面,我们看到的窗口实际上是视类窗口,而上述代码修改的是框架类窗口的背景和光标。应用程序的图标属于框架窗口,因此上述程序运行后,图标发生了改变。
结论:在MFC中,如果要修改应用程序窗口的图标,则应该框架类中进行,因为框架窗口才有标题栏;如果要修改程序窗口的背景和光标,则应该在视类中进行。解决方法:在视类的PreCreateWindow函数中添加代码: cs.lpszClass= "hello";。
同时MFC为我们提供了一个全局函数AfxRegisterWndClass()。
LPCTSTR AFXAPI AfxRegisterWndClass(UINTnClassStyle,
HCRSOR hCursor = 0, HBRUSH hbrBackground = 0, HICON hIcon = 0);
7.2.2 在窗口创建之后更改
要在应用程序窗口创建之后修改它的光标、图标和背景,可在OnCreate函数中调用SetClassLong函数实现。DWORD SetClassLong(HWNDhWnd, int nIndex, LONG dwNewLong)
该函数的作用是:重新设置指定窗口所属窗口类的WNDCLASS结构体中指定数据成员的属性(包括设置新的窗口背景画刷、光标、图标和窗口类样式)。
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
…
SetClassLong(m_hWnd,GCL_HICON, (LONG)LoadIcon(NULL, IDI_WARNING));
return 0;
}
int CMyMFCAppView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
…
//7.2.2 在窗口创建之后更改光标、标题栏图标、窗口背景
SetClassLong(m_hWnd, GCL_HBRBACKGROUND, (LONG)GetStockObject(BLACK_BRUSH));
SetClassLong(m_hWnd, GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_CROSS));
return 0;
}
7.3 模拟动画图标
7.3.1 加载图标资源
添加IDI_ICON1-4资源,气候在框架类定义一个图标句柄成员变量,添加数组时,类型设置为HICON [4]方能设置成功。在视类添加一下代码。
m_hIcons[0] = ::LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_ICON1));
m_hIcons[1] = ::LoadIcon(theApp.m_hInstance,MAKEINTRESOURCE(IDI_ICON2));
m_hIcons[2] = ::LoadIcon(AfxGetApp()->m_hInstance,MAKEINTRESOURCE(IDI_ICON3));
m_hIcons[3] = AfxGetApp()->LoadIcon(IDI_ICON4);
其中LoadIcon第一个参数,加载第一幅时,AfxGetInstanceHandle可以获取应用程序当前句柄。第二个参数,通过MAKEINTRESOURSE宏将ID转换为相应的资源表示符字符串。第二幅应用theApp的获取应用程序的CWinApp对象,其数据成员m_hInstance得到实例句柄。但是App文件中已经定义了一个theApp全局变量,所以在框架类OnCreate函数前extern CMyMFCApp theApp。第三幅加载,用AfxGetApp全局函数实现。
7.3.2设置定时器
在框架类OnCreate函数添加SetTime函数,没1000ms触发一次定时器消息。
SetTimer(1, 1000, NULL);
在框架类添加定时器消息(WM_TIMER)的响应函数,并在响应函数调用SetClassLong函数改变应用程序窗口图标。
void CMainFrame::OnTimer(UINT_PTR nIDEvent)
{
//TODO: 在此添加消息处理程序代码和/或调用默认值
static int index = 1;
SetClassLong(m_hWnd, GCL_HICON, (LONG)m_hIcons[index]);
index = ++index % 4;
CFrameWnd::OnTimer(nIDEvent);
CFrameWnd::OnTimer(nIDEvent);
}
因为程序每次发送定时器消息都会调用OnTimer函数,所以把index定义为静态(分配在栈中)。
7.4工具栏编程
7.4.1 在工具栏添加按钮
在资源的toolbar中在IDR_MAINFRAME的最右边添加一个按钮IDM_TEST。并在菜单栏【帮助】下添加一个同ID的按钮,Caption设置为Test。添加一个命令响应函数OnTestShow。可以将T按钮享有拖动一点,此时帮助与T按钮有一定空隙。想要删除只需del。
void CMainFrame::OnTestShow()
{
//TODO: 在此添加命令处理程序代码
MessageBox("Teston toolbar");
}
7.4.2创建工具栏——4个步骤
Step1:创建工具栏资源;
Step2:构造CToolBar对象;主框架添加私有变量
Step3:调用Create或CreateEx函数创建Window工具栏(工具栏也是窗口)
Step4:调用LoadToolBar函数加载工具栏资源。
7.4.3 创建自定义工具栏
在TOOLBAR添加新资源,在框架类加加一个CToolBar类型的成员变量,调用create函数创建工具栏,与CToolBar相关联,可以在框架类的OnCreate函数实现。添加一下代码。
if (!m_newToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_newToolBar.LoadToolBar(IDR_TOOLBAR1))
{
TRACE0("Failed to create toolbar\n");
return-1; // fail to create
}
m_newToolBar.EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_newToolBar);
CreateEx函数创建工具栏,并与工具栏对象:m_newToolBar关联,停靠位置设置为CBRS_RIGHT。调用LoadToolBar加载资源。调用EnableDocking函数允许工具栏停靠客户区任意位置。最后调用DockControlBar函数,让工具栏停靠在主框架窗口上。
7.4.4 显示和隐藏工具栏
在【视图】自此阿达您下载添加一个菜单项。ID属性为IDM_VIEW_NEWTOOLBAR,Caption为“新的工具栏”。接着添加命令响应函数。在响应函数中,实现先前新建的工具栏的显示与隐藏。可以调用ShowWindow函数。之后需要调整他们的位置,调用RecalcLayout函数。限制或隐藏后再次调用框架类DockControlBar函数。让工具栏停靠在主框架窗口上,使用DockControlBar函数。
void CMainFrame::OnViewNewtoolbar()
{
//TODO: 在此添加命令处理程序代码
if (m_newToolBar.IsWindowVisible())
m_newToolBar.ShowWindow(SW_HIDE);
else
m_newToolBar.ShowWindow(SW_SHOW);
RecalcLayout();
DockControlBar(&m_newToolBar);
}
如何让新建的工具栏舍在原先显示位置显示。需要调用ShowControlBar函数。
下面,添加复选编辑。为此为菜单项添加一个UPDATE_COMMAND_UI函数。内部添加代码。
void CMainFrame::OnUpdateViewNewtoolbar(CCmdUI *pCmdUI)
{
//TODO: 在此添加命令更新用户界面处理程序代码
// 添加复选框
pCmdUI->SetCheck(m_newToolBar.IsWindowVisible());
}
7.5 状态栏编程
7.5.1 状态栏的提示行与指示器
状态栏分为两部分:提示行与指示器。左边最长的部分为提示行,通常用于显示菜单项或工具按钮的提示信息。右边由若干窗格组成的部分为状态栏指示器,通常用来显示大小写键、数字锁定键等信息。
框架程序专门提供了一个indicators数组来管理提示行与指示器。如果要修改状态栏的外观,则只需在indicators数组中添加或减少相应的字符串资源ID即可。
① 在资源编辑器中新增字符串资源ID:
ID_TIMER 时钟
② 将新的字符串资源ID添加到indicators数组中
③ 获取系统当前时间(加在CMainFrame的OnCreate函数的后部)
CTime t = CTime::GetCurrentTime();
CString str = t.Format("%H:%M:%S");
④ 将字符串显示到状态栏的窗格上,调用CStatusBar类的成员函数SetPaneText。如果不知道窗格的索引,可以调用CStatusBar类的成员函数CommandToIndex获得。m_wndStatusBar.CommandToIndex(ID_TIMER)
m_wndStatusBar.SetPaneText(1, str);
⑤ 调整窗格大小
CStatusBar类的成员函数:SetPaneInfo,该函数可以为指定的窗格设置新的ID、样式和宽度。
CClientDC dc(this);
CSize sz = dc.GetTextExtent(str);
m_wndStatusBar.SetPaneInfo(1, ID_TIMER, SBPS_NORMAL,sz.cx);
⑥ 在OnTimer中添加相关代码。
7.6 在状态栏中添加鼠标坐标显示
方法一:调用SetWindowText函数设置状态栏提示行文本
需添加头文件并把m_wndStatusBar改为公有
CString str;
str.Format("x=%d, y=%d", point.x, point.y);
((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str);
方法二:利用CFrameWnd类的成员函数SetMessageText实现,该函数的作用是在状态栏的提示行中设置文本。
((CMainFrame*)GetParent())->SetMessageText(str);
方法三:利用CFrameWnd类的成员函数GetMessageBar可以返回状态栏对象的指针,这样也不用再访问CMainFrame类的保护成员变量:m_wndStatusBar。
((CMainFrame*)GetParent())->GetMessageBar()->SetWindowText(str);
在提示行中显位图(借助位图按钮的方法)
在框架类添加CBitmapButton m_bmp变量。
CRect rc(100, 4, 120, 20);
if (!m_bmp.Create("", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, rc, &m_wndStatusBar, 0))
return FALSE;
if (!m_bmp.LoadBitmaps(IDB_BITMAP1, NULL, NULL, NULL))
return FALSE;
7.7添加启动界面
7.7.1 利用VC++组件库中提供的类完成
单击[Project \ Add to Project \Components and controls…],在弹出的组件和控件库对话框中双击“Visual C++ Components”目录,在该目录下找到Splashscreen组件。
替换splsh.bmp位图文件可替换启动界面;
更改SetTimer(1, 2000, NULL)可设定启动画面的停留时间。
7.7.2 VS添加启动画面
1)新建一个ID_BITMAP3的位图资源。
2)现在项目下新建一个类CSplashWnd,基类为CWnd;
3)在该类下添加一个protected型的变量CBitmap m_bitmap
4)添加一个Create函数加载位图,创建窗口;
public:
BOOL Create();
BOOL CSplashWnd::Create()
{
if (!m_bitmap.LoadBitmap(IDB_BITMAP3))
return false;
BITMAP bm;
m_bitmap.GetBitmap(&bm);
return CreateEx(0, AfxRegisterWndClass(0,AfxGetApp()->LoadStandardCursor(IDC_ARROW)),
NULL, WS_POPUP | WS_VISIBLE, 0,0, bm.bmWidth, bm.bmHeight, NULL, NULL);
}
5)显示窗口发送WM_PAINT消息,映射此消息;
void CSplashWnd::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: 在此处添加消息处理程序代码
// 不为绘图消息调用 CWnd::OnPaint()
CDC dcimage;
if (!dcimage.CreateCompatibleDC(&dc)) return;
BITMAP bm;
m_bitmap.GetBitmap(&bm);
CBitmap* pOldBitmap = dcimage.SelectObject(&m_bitmap);
dc.BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &dcimage, 0, 0, SRCCOPY);
dcimage.SelectObject(pOldBitmap);
}
6)在App类中包含新类的头文件后,在InitInstance()函数中添加以下代码:
CSplashWnd *pSplashWindow = new CSplashWnd;//创建对象
pSplashWindow->Create();
pSplashWindow->CenterWindow();
pSplashWindow->ShowWindow(SW_SHOW); //显示窗口
pSplashWindow->UpdateWindow();
Sleep(2000); //表示启动画面持续时间
pSplashWindow->DestroyWindow(); //销毁启动画面
delete pSplashWindow; //删除