1.修改应用程序窗口的外观
(1)在窗口创建之前修改:在CMainFrame类的PreCreateWindow成员函数中进行修改
(2)在窗口创建之后修改:在CMainFrame类的Oncreate函数中使用SetWindowLong函数(第二个参数指定为GWL_STYLE)
Todo:获得窗口的现有类型用GetWindowLong函数
2.修改窗口的光标、图标和背景
(1)在窗口创建之前修改:
1.在CMainFrame类的PreCreateWindow成员函数中进行修改(编写自己的窗口类并注册,菜单名可以设为NULL,MFC模板自己提供了菜单)
全局函数AfxGetInstanceHandle可以用来获取当前应用程序的实例句柄(所有以Afx开头的函数都是应用程序框架类函数,也就是全局函数,程序的所有类都可以调用)
修改后程序图标改变但背景和光标没有改变,因为视类窗口覆盖在框架类窗口之上。修改背景和光标只能在视类的PreCreateWindow中修改
2.用全局函数AfxRegisterWndClass,设定窗口的类型,光标,图标,背景
函数声明:LPCTSTR AFXAPI AfxRegisterWndClass(UINT nClassStyle,HCURSOR hCursor = 0,HBRUSH hbrBackground = 0,HICON hIcon = 0);
//它的返回值就是注册后的类名,可以直接将这个返回值作为程序中随后创建窗口时所依赖的类
在CMainFrame类的PreCreateWindow函数中添加代码:
cd.lpszClass = AfxRegisterWndClass(参数);
这样窗口的图标就改变了,不需要再去整个重新定义一个窗口类,在视类中也可以调用这个函数来修改背景和光标
(2)在窗口创建之后修改:利用全局API函数:SetClassLong,该函数用来重置指定的窗口所属窗口类的WNDCLASSEX结构体中指定数据成员的属性(CMainFrame的OnCreate函数中修改图标,视类的OnCreate函数中修改背景和光标)
函数声明:DWORD SetClassLong(HWND hWnd,int nIndex,LONG dwNewLong);
//nIndex指定要设置的属性的索引
//dwNewLong指定要设置的新属性的值
//eg:SetClassLong(m_hWnd,GCL_HICON,(LONG)LoadIcon(NULL,IDI_ERROR));
3.模拟动画图标(几幅图标,循环显示):利用定时器和SetClassLong函数实现
(1)加载图标资源:添加资源,调用LoadIcon函数加载
m_hIcons = LoadIcon(AfxGetInstanceHandle(),MAKINTRESOURCE(IDI_ICON1));
//使用系统图标,第一个参数为NULL,使用自定义图标第一个参数为当前实例句柄
//得到实例句柄的其他方法:theApp.m_hInstance或AfxGetApp->m_hInstance,
//其中,使用theApp要声明这个变量在外部定义的extern CStyleApp theApp
//第二个参数需要图标的名称,或者图标资源标识符的字符串,而我们只知道图标的ID
//需要通过MAKEINTRESOURCE宏将资源ID转化为相应的资源标识符字符串
(2)定时器的处理
1.在CMainFrame类的OnCreate函数中添加代码:SetTimer(1,1000,NULL);
2.为CMainFrame类添加定时器消息(WM_TIMER)的响应函数,并在该函数中调用SetClassLong函数改变应用程序的图标
4.工具栏编程
(1)资源编辑窗口中,双击工具栏图标就可以知道该图标的属性(工具栏图标的ID和菜单栏某一菜单项是相同的)
(2)创建工具栏
1.CToolBar—CControlBar—CWnd—CCmdTarget—CObject,所以工具栏也是一个窗口
2.方法一:(1)创建工具栏资源
(2)构造CToolBar对象
(3)调用Create或CreateEx函数创建Windows工具栏,并把它与已创建的CToolBar对象关联起来
(4)调用LoadToolBar函数加载工具栏资源
函数原型:BOOL Create(CWnd* pParentWnd,
DWORD dwStyle = WS_CHILD|WS_VISIBLE|CBRS_TOP,UINT nID = AFX_IDW_TOOLBAR);
// pParentWnd指向工具栏对象的父窗口
//指定工具栏的样式
//指定工具栏子窗口的ID
3.方法二:(1)构造CToolBar对象
(2)调用Create或CreateEx函数创建Windows工具栏,并把它与已创建的CToolBar对象关联起来
(3)调用LoadBitmap函数加载包含工具栏按钮图像的位图
(4)调用SetButton函数设置按钮样式,并把工具栏上的一个按钮与位图中的一个图像关联
(3)MFC创建工具栏的过程
1. CMainFrame的OnCreate函数中:先调用CreateEx函数创建程序的工具栏对象,接着调用LoadToolBar函数加载工具栏资源,接着调用工具栏对象的EnableDocking成员函数设置工具栏停靠的位置,接着调用CMainFrame对象的EnableDocking成员函数设置主框架窗口的停靠,最后调用DockContrilBar函数,让工具栏停靠在主框架窗口上
(4)创建自定义的工具栏
1.按方法一创建
2.工具栏的显示和隐藏:显示和隐藏用ShowWindow函数;用IsWindowVisible函数判断工具栏窗口当前是否可见
3.窗口上的工具栏被隐藏或显示后,停靠在窗口的其他控制条对象的位置可能会有所变动,需要调用框架类的RecalcLayout成员函数来重新调整它们的位置
函数原型:virtual void RecalcLayout(BOOL bNotifu = TRUE);
4.工具栏拖动以后,隐藏后再显示,在拖动后的位置出现:CFrameWnd类的成员函数ShowControlBar,该函数的作用是隐藏或显示指定的控制条(不再用ShowWindow函数)
函数原型:void ShowContrilBar(CContrilBar* pBar,BOOL bShow,BOOL bDelay);
// pBar指向将要显示或隐藏的控制条
// bShow TRUE:显示;FALSE:隐藏
// bDelay TRUE:延迟显示,FALSE:立即显示
5.状态栏编程
(1)状态栏分为两部分,左边是提示行,右边3个窗格,主要用来显示Caps Lock,Num Lock和Scroll Lock键的状态,称为状态栏指示器
(2)状态栏对象也在CMainFrame类中定义,与状态栏相关的MFC类是CStatusBar
1.MFC在OnCreate函数中创建状态栏:先调用状态栏对象的Create函数创建状态栏对象,接着调用SetIndicators函数设置状态栏指示器,其中用了一个数组参数:indicators,这个数组是在CMainFrame类源文件的前部定义的,数组内容分别对应状态栏的提示行和Caps Lock,Num Lock和Scroll Lock键的状态
2.想要修改状态栏的外观,例如添加或减少状态栏上的窗格,则只需要在indicators数组中添加或减少想要的字符串资源ID即可
(3)在程序的状态栏上显示系统时间和一个进度条控件
1.为程序增加两个新的字符串资源
2.将这两个新的字符串资源ID添加到indicators数组中
3.显示系统时间:CTIME类成员函数GetCurrentTime(得到系统时间);Format(得到一个包含格式化时间的字符串);为了将字符串显示到状态栏的窗格上,可以调用CStatusBar类的SetPaneText函数
函数原型:BOOL SetPaneText(int nIndex,LPCTSTR lpszNewText,BOOL bUpdate = TRUE);
//第一个参数是窗格在指示器数组中的索引,第二个参数是窗格上显示的文本,、、//第三个参数是BOOL类型,TRUE:设置窗格文本后,该窗格变成是无效的,当下//次WM_PAINT消息发送后,该窗格将发生重绘,该参数默认值为TRUE
CTime t = CTime::GetCurrentTime();
CString str = t.Format(“%H:%M:%S”);
m_wndStatusBar.SetPaneText(1,str);
(1)如果在设置窗格的文本时不知道窗格的索引,可以调用CStatusBar类的另一个成员函数;CommandToIndex,通过指定资源ID来得到相应的索引
(2)如果窗格太小不能显示完整时间,可以利用CStatusBar类的另一个成员函数:SetPaneInfo实现
函数原型:void SetPaneInfo(int nIndex,UINT nID,UINT nStyle,int cwWidth);
//第一个参数指定将要设置其样式的窗格索引,第二个参数指定窗格重新设置的//新ID,第三个参数指示窗格的样式,第四个参数指定窗格的新宽度
获得字符串长度,要用CDC的成员函数GetTextExtent,所以先定义一个DC,如:CClientDC,然后调用GetTextExtent得到时间字符串显示的长度
(3)这个时间是静止的,如果让它动起来,要利用定时器,在WM_TIMER消息响应函数中重新获得当前时间并显示。
6.进度栏编程
(1) CProgressCtrl是Cwnd的子类,也是一个窗口类,要在程序中使用进度栏,首先需要构造一个CProgressCtrl对象,然后调用CProgressCtrl类的Create函数创建进度栏控件
函数原型:BOOL Create(DWORD dwStyle,const RECT& rect,
CWnd* pParentWnd, UINT nID);
//第一个参数为进度栏控件的类型,第二个参数指定进度栏控件的大小和位置
//第三个参数指定进度栏的父窗口,第四个参数指定进度栏控件的ID
(2)在窗口中创建进度栏
1.在CMainFrame类的头文件中定义一个CPregressCtrl类型的成员变量;
2.在CMainFrame类的OnCreate函数中在窗口创建完成后,创建进度栏控件,调用Create函数
3.利用CProgressCtrl类的SetPos成员函数可以设置进度栏上的当前进度
4.在状态栏的窗格中创建进度栏
(1)获得该窗格的区域:CStatusBar类的GetItemRect成员函数
函数原型:void GetItemRect(int nIndex,LPRECT lpRect) const;
//第一个参数为窗格索引,第二个参数用来接受指定索引的窗格的矩形区域
(2)在该区域上显示进度条,将状态栏对象作为进度栏的父窗口
CRect rect; (在CMainFrame的OnCreate函数中)
m_wndStatusBar.GetItemRect(2,&rect); m_progress.Create(WS_CHILD|WS_VISIBLE,rect,&m_wndStatusBar,123);
m_progress.SetPos(50);
(3)此时得到的矩形无效,因为此时状态栏的初始化工作还没完成,只有OnCreate函数完成后,才能获得窗口状态栏上窗格的矩形区域。我们可以这样:自定义一个消息,然后在CMainFrame类的OnCreate函数中在其返回之前发送这条消息,最后在自定义消息的响应函数中获得状态栏上的窗格的矩形区域
1.在Windows中每条消息都用一个特定的整数值表示,WM_USER是系统提供的一个常量,小于这个常量的都是Windows系统保留的,我们自定义的消息要大于这个常(用户定义的消息一般要以UM_开头)
在CMainFrame类的头文件定义自定义消息#define UM_PROGRESS WM_USER+1
2.在CMainFrame类的头文件中,在两个AFC_MSG宏的外面添加自定义消息响应函数的声明(OnProgress),如果在发送该消息时,还需要附带发送一些数据,在声明这个函数时,就应该让其带有与消息相关的两个附加参数wParam和lParam,否则,可以没有参数 afx_msg void OnProgress();
3.在CMainFrame类的源文件中,在消息映射表的两个注释宏之外添加UM_PROGRESS这一自定义消息的消息映射ON_MESSAGE(UM_PROGRESS,OnProgress)
4.添加这个消息响应函数的实现
CRect rect;
m_wndStatusBar.GetItemRect(2,&rect);
m_progress.Create(WS_CHILD|WS_VISIBLE,rect,&m_wndStatusBar,123);
m_progress.SetPos(50);
5.在CMainFrame的OnCreate函数中利用SendMessage函数发送UM_PROGRESS这个自定义的消息 SendMessage(UM_PROGRESS);
6.使用SendMessage发送消息,不能达到我们的目的,因为这个函数发送消息的机制,它是直接把消息发送给消息响应函数,由消息响应函数处理完成后,SendMessage函数才返回。这里要用PostMessage函数,PostMessage函数把消息放到消息队列中,然后立即返回,之后程序使用GetMessage函数把消息取出,再逐条执行
7.窗口尺寸发生变化时,进度栏显示的位置发生错误,这因为当程序尺寸发生变化时,窗口重绘,窗口的状态栏的尺寸也会随之变化,之前OnProgress代码中获得窗格区域就不正确了,我们需要为CMainFrame类添加WM_PAINT消息的响应函数然后在其中重新确定进度栏的位置
CRect rect;
m_wndStatusBar.GetItemRect(2,&rect);
if(!m_progress.m_hWnd)
{
m_progress.Create(WS_CHILD|WS_VISIBLE|PBS_SMOOTH,rect,&m_wndStatusBar,123);
}
else
{
m_progress.MoveWindow(rect);//也可以用SetWindowPos函数
}
m_progress.SetPos(50);
//如果不进行判断,第二次调用该函数时,因为m_progress.Create重复创建进度//条并与m_progress关联,会出错
8.让进度栏动起来:CProgress类的StepIt成员函数,该函数将使进度栏控件的当前位置按照一定的步长前进,而步长可以用SetStep来设置,对于进度栏来说,还可以设置它的范围,这可以通过SetRange函数实现
7.在状态栏上显示鼠标当前位置
(1)捕获鼠标移动消息(视类中完成)
(2)响应函数中添加代码
CString str;
Str.Format(“x=%d,y=%d”,point.x.point.y);
((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str);
//这样就可以把鼠标当前位置显示在状态栏的第一个窗格中
(3)不同的方法
1.调用SetWindowText函数设置状态栏窗口的文本((2)所用的方法),因为上述代码用到了框架类的类型,所以要在视类包含头文件”MainFrm.h”
2.利用CFrameWnd类的成员函数:SetMessageText实现,该函数的作用是在ID为0值的状态栏窗格上设置一个字符串
类CMainFrame是CFrameWnd的子类,继承了这个函数
((CMainFrame*)GetParent())->SetMessageText(str);
3.CFrameWnd类的成员函数:GetMessageBar返回状态栏对象的指针
((CMainFrame*)GetParent())->GetMessageBar()->SetWindowText(str);
4.利用CWnd类的成员函数GetDescendantWindow获得程序状态栏对象的指针,这个函数的功能是通过指定的ID来获得子孙窗口,这个函数将搜索当前窗口的整个子窗口树,不仅仅搜索当前窗口的直接子窗口
函数原型:CWnd* GetDescendantWindow(int nID,BOOL bOnlyPerm = FALSE)const; GetParent()->GetDescendantWindow(AFX_IDW_STATUS_BAR)->SetWindowText(str);
TODO:对于一些常用的控制条,MFC都已经预先定义了ID号
8.启动画面
(1)VC++组件库中选择Splash screen组件,然后单击【insert】按钮
(2)在CMainFrame类的OnCreate函数中添加代码:
CSplashWnd::showSplashScreen(this);