本节主要学习了
1. 修改MFC AppWizard向导生成的框架程序外观和大小,这既可以在窗口创建之前,也可以在窗口创建之后进行。
2. 修改程序窗口的图标,光标,背景的方法。在创建窗口之前,通过设计窗口类来修改程序窗口的图标,光标,背景;在窗口创建之后,通过SetClassLong函数修改窗口的图标,光标和背景。
3. 实现一个动态变化的图标的例子。
4. 工具栏和状态栏编程。
5. 程序启动画面的创建。
BOOLCMainFrame::PreCreateWindow(CREATESTRUCT& cs)//这是一个虚函数
{
if(!CFrameWnd::PreCreateWindow(cs) )
returnFALSE;
//TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
//1.在窗口创建之前修改窗口的默认外观。
/*cs.cx = 300;
cs.cy= 200;
cs.style&= ~FWS_ADDTOTITLE; //改变将文档标题添加到窗口标题上。可以让窗口显示自己设置的标题。
//cs.style= cs.style & ~ FWS_ADDTOTITLE;
//cs.style= WS_OVERLAPPEDWINDOW;
//cs.style= WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE; //该成员的初始定义代码。
cs.lpszName= "xiaobai";*/
//2.修改窗口的光标,图标,背景
//通过编写自己的窗口类并注册,然后让随后的窗口按照自己编写的窗口类去创建。
/*WNDCLASSwndclass;
wndclass.cbClsExtra= 0;
wndclass.cbWndExtra= 0;
wndclass.hbrBackground= (HBRUSH)GetStockObject(BLACK_BRUSH);
wndclass.hCursor= LoadCursor(NULL,IDC_HELP);
wndclass.hIcon= LoadIcon(NULL,IDI_ERROR);
wndclass.hInstance= AfxGetInstanceHandle(); //获取当前应用程序的实例句柄。Afx开头的函数都是应用程序框架类函数,也就是全局函数。
wndclass.lpfnWndProc= ::DefWindowProc;
wndclass.lpszClassName= "xiaobai";
wndclass.lpszMenuName= NULL;
wndclass.style= CS_HREDRAW | CS_VREDRAW;//这里并不是窗口的类型,而是窗口类的类型。指定窗口类具有水平重绘和垂直重绘这两种类型。
RegisterClass(&wndclass);
cs.lpszClass= "xiaobai"; */
//cs.lpszClass= AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,0,0,LoadIcon(NULL,IDI_WARNING));
cs.lpszClass= AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,0,0,0);
//用来设定窗口的类型,光标,背景,图标。返回值是注册之后的类名。
returnTRUE;
}
//SetWindowLong(m_hWnd,GWL_STYLE,WS_OVERLAPPEDWINDOW);
SetWindowLong(m_hWnd,GWL_STYLE,GetWindowLong(m_hWnd,GWL_STYLE) &~WS_MAXIMIZEBOX);//取消最大化
//GWL就是GetWindowLong的缩写。GWL_STYLE指定获取窗口的类型。
BOOLCStyleView::PreCreateWindow(CREATESTRUCT& cs)
{
//TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
//cs.lpszClass = "xiaobai"; //思考一下为什在CMainFram中注册的窗口类,在这里还可以用。
//在MFC程序中,如果要修改应用程序窗口的图标,则应在框架类中进行,因为在框架窗口中才有标题栏,所以才能修改位于该标题栏上的图标
//如果想要修改应用程序的背景和光标,就应该在视类中进行。
//cs.lpszClass= AfxRegisterWndClass(CS_HREDRAW |CS_VREDRAW,LoadCursor(NULL,IDC_CROSS),(HBRUSH)GetStockObject(BLACK_BRUSH),0);
cs.lpszClass= AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,0,0,0);//只给第一个参数赋值,其余参数都为0,会发现
//背景是透明的,图标光标都是默认的。
returnCView::PreCreateWindow(cs);
}
ON_MESSAGE(UM_PROGRESS,OnProgress) //对于自定义消息来说,使用的是ON_MESSAGE宏实现这一功能。
END_MESSAGE_MAP()
static UINT indicators[] = //标识提示行和状态栏指示器的ID数组
{
ID_SEPARATOR,// status line indicator 提示行
IDS_TIMER,
IDS_PROGRESS,
ID_INDICATOR_CAPS, //分别是Caps Lock,Num Lock,Scroll Lock键的状态指示器。
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
};
extern CStyleApp theApp; //声明这个变量是在外部的一个源文件中定义的
int CMainFrame::OnCreate(LPCREATESTRUCTlpCreateStruct)
{
if(CFrameWnd::OnCreate(lpCreateStruct) == -1)
return-1;
if(!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
|CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) //MFC自动创建的工具栏和主菜单的资源ID是一样的,也就是说,在MFC编程中,一个ID可以表示多种资源。
{
TRACE0("Failedto create toolbar\n");
return-1; // fail to create
}
if(!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failedto create status bar\n");
return-1; // fail to create
}
//TODO: Delete these three lines if you don't want the toolbar to
// be dockable
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); //设置工具栏停靠的位置
EnableDocking(CBRS_ALIGN_ANY);//上面的那个EnableDocking函数式工具栏对象的成员函数,目的是让工具栏对象可以停靠,而这里调用的是CFrameWnd对象的EnableDocking
//成员函数,目的是让主框架窗口可以被停靠。
DockControlBar(&m_wndToolBar);//让工具栏停靠在主框架窗口上。
//在窗口创建之后修改窗口的样式
SetClassLong(m_hWnd,GCL_HICON,(LONG)LoadIcon(NULL,IDI_ERROR));
//模拟动画图标(3中加载自定义图标的方式)
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));//AfxGetApp可以获得当前应用程序对象的指针
SetClassLong(m_hWnd,GCL_HICON,(LONG)m_hIcons[0]);
SetTimer(1,1000,NULL);
//创建自定义工具栏
if(!m_newToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_RIGHT
|CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_newToolBar.LoadToolBar(IDR_TOOLBAR1)) //MFC自动创建的工具栏和主菜单的资源ID是一样的,也就是说,在MFC编程中,一个ID可以表示多种资源。
{
TRACE0("Failedto create toolbar\n");
return-1; // fail to create
}
m_newToolBar.EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_newToolBar);
//在窗口中创建进度栏(思考一下在框架类中创建的进度栏怎嘛可以在视类窗口中显示)
//m_progress.Create(WS_CHILD| WS_VISIBLE | PBS_SMOOTH,CRect(100,100,200,120),this,123); //创建水平的进度栏
//m_progress.SetPos(50); //设置进度栏上当前位置
//m_progress.Create(WS_CHILD| WS_VISIBLE | PBS_VERTICAL,CRect(100,100,120,200),this,321); ///创建垂直的进度栏
//m_progress.SetPos(40);
//在状态栏的窗格中创建进度栏
/*CRectrect;
m_wndStatusBar.GetItemRect(2,&rect);
m_progress.Create(WS_CHILD| WS_VISIBLE,rect,&m_wndStatusBar,123);
m_progress.SetPos(50);*/ //这种方式并不能让进度条在窗格中显示。只有在窗口创建完,即OnCreate函数执行完
//才能获得窗口状态栏上窗格的矩形区域。
//SendMessage(UM_PROGRESS); //发送消息在OnCreate函数没有执行完,就去执行UM_PROGRESS消息的响应函数,
//消息响应函数返回又回到OnCreate函数中执行到函数结束,这和上面的那段代码其实
//是一样的效果
m_wndStatusBar.SetPaneInfo(2,IDS_PROGRESS,SBPS_NORMAL,100);//设置窗格的宽度
//PostMessage(UM_PROGRESS);//投递消息,把消息投递到消息队列中,程序通过GetMessage函数按顺序把消息一条一条的取出来
//对于本程序,等程序执行完WM_CREATE消息后,才去执行UM_PROGRESS消息
myCtrl.Create(WS_CHILD |WS_VISIBLE | PBS_SMOOTH,CRect(100,100,200,120),this,123);
intnLower,nUpper;
myCtrl.GetRange(nLower,nUpper);
myCtrl.SetStep((nUpper-nLower)/4);
myCtrl.StepIt();
//CG: The following line was added by the Splash Screen component.
CSplashWnd::ShowSplashScreen(this);
return0;
}
void CMainFrame::OnTimer(UINT nIDEvent)
{
staticint index = 0; //作为一个静态的局部变量,他将存放在程序的数据区中,而不是在栈中分配空间。
SetClassLong(m_hWnd,GCL_HICON,(LONG)m_hIcons[index]);
index= ++index%3; //小技巧:如果希望把某个数值始终限制在一个范围内,那么最好的办法就是进行取模运算(%)
//在时钟状态栏指示器中显示时间
CTimet = CTime::GetCurrentTime();
CStringstr = t.Format("%H:%M:%S");
CClientDCdc(this);
CSizesz=dc.GetTextExtent(str); //获得字符串的宽度和高度
//intindex = 0; //在不知道窗格的索引时,可以利用这个函数,根据窗格的ID来得到相应的索引。
//index= m_wndStatusBar.CommandToIndex(IDS_TIMER);
m_wndStatusBar.SetPaneInfo(1,IDS_TIMER,SBPS_NORMAL,sz.cx);
m_wndStatusBar.SetPaneText(1,str);
intnLower,nUpper;
m_progress.GetRange(nLower,nUpper);
m_progress.SetStep((nUpper-nLower)/10); //设置每次前进的步长
m_progress.StepIt(); //让进度栏的当前位置按照一定的步长前进
CFrameWnd::OnTimer(nIDEvent);
}
//工具栏的显示或隐藏
void CMainFrame::OnViewNewtoolbar()
{
//TODO: Add your command handler code here
/*if(m_newToolBar.IsWindowVisible()) //判断工具栏是显示还是隐藏了
{
m_newToolBar.ShowWindow(SW_HIDE);
}
else
{
m_newToolBar.ShowWindow(SW_SHOW);
}
RecalcLayout();
/*当标准工具栏或状态栏隐藏或者显示,或者窗口调整大小时,调用这个方法。
方法原型为:virtualvoid RecalcLayout( BOOL bNotify = TRUE );
Parameters: bNotify
Determines whether the active in-place itemfor the frame window receives notification of the layout change. If TRUE, theitem is notified; otherwise FALSE.
决定是否将焦点项目传递这个变化,如果是TRUE就通知子项目,否则,不传递。
这里m_wndToolBar1是自己定义的一个工具栏,:OnViewToolbar1()是一个菜单项的消息响应函数。
当工具栏显示时,点击这个菜单项,工具栏隐藏,然后调用RecalcLayout这个方法,重新布置窗口。
当工具栏隐藏式,点击这个菜单项,工具栏显示,然后调用RecalcLayout这个方法,重新布置窗口。
DockControlBar(&m_newToolBar);*/
ShowControlBar(&m_newToolBar,!m_newToolBar.IsWindowVisible(),FALSE);
//显示或隐藏一个控件条
//pBar指向要显示或隐含的控件条
//bShow如果为TRUE ,指定控件条将显示;如果为FALSE,则隐藏。
//bDelay如果为TRUE,延迟显示控件条;如果为FALSE,则立即显示
}
//显示或隐藏工具栏时,取消或显示相应的菜单上的复选标记
void CMainFrame::OnUpdateViewNewtoolbar(CCmdUI*pCmdUI)
{
pCmdUI->SetCheck(m_newToolBar.IsWindowVisible());
//通过为菜单项添加UPDATE_COMMAND_UI消息的响应,可以非常方便地为该菜单项设置或取消复选标记。
}
//这种方法在窗口发生重绘时,进度条的显示就会错位,要在窗口重绘的消息响应函数中添加下面的代码
void CMainFrame::OnProgress()
{
CRectrect;
m_wndStatusBar.GetItemRect(2,&rect);
m_progress.Create(WS_CHILD| WS_VISIBLE | PBS_SMOOTH,rect,&m_wndStatusBar,123);
m_progress.SetPos(50);
}
void CMainFrame::OnPaint()
{
CPaintDCdc(this); // device context for painting
CRectrect;
m_wndStatusBar.GetItemRect(2,&rect);
if(!m_progress.m_hWnd) //根据m_progress对象的窗口句柄是否为空判断进度栏是否被创建
{
m_progress.Create(WS_CHILD | WS_VISIBLE |PBS_SMOOTH,rect,&m_wndStatusBar,123);
}
else
{
m_progress.MoveWindow(rect);//用SetWindowPos函数设置进度栏的位置比较麻烦,但功能比较多,例如它可以将程序窗口设置为顶层窗口
}
m_progress.SetPos(50);
//Do not call CFrameWnd::OnPaint() for painting messages
}