如何修改MFC AppWizard向导生成的框架程序的外观和大小,修改图标、光标、背景的三种方法。如何增加和删除工具栏按钮,如何给应用程序增加工具栏,如何显示和隐藏工具栏。定制状态栏,在状态栏中添加时钟显示,CTime类及其用法。在状态栏中添加进度条(主窗口产生后立即产生进度条的巧妙思想,不能在OnCreate函数中直接处理,要用到自定义消息的方法)。鼠标坐标显示,在CView中获取状态栏对象的几种方式。如何为应用程序添加启动画面。
1.修改窗口的标题和大小:主要在cmainframe类中的precreatewindow()函数中来完成,代码如下:
cs.cx = 800;//对窗口的宽度修改
cs.cy = 600;//对窗口的高度修改
cs.style &= ~FWS_ADDTOTITLE;//去掉一种类型,与下面的语句等同
//cs.style = cs.style & ~FWS_ADDTOTITLE;
//cs.style = WS_OVERLAPPEDWINDOW;//与上语句作用等同
cs.lpszName = TEXT("http://www.sunxun.org");//对窗口标题的修改,只有经过上面的语句才可以显示自己的标题!
2.修改窗口的图标、光标和背景:注意:单文档的view类总是覆盖在框架类的上面,所以对图标的修改需要在cmainframe类中去修改,而对光标和背景的修改需要在view类中去修改!而又分为在窗口创建之前的修改和在窗口创建之后的修改:
a.在窗口创建之前对窗口图标的修改需要在cmainframe类中的precreatewindow()函数中去完成:
//编写一个新的窗口类 WNDCLASS wndcls; wndcls.cbClsExtra = 0; wndcls.cbWndExtra = 0; wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); wndcls.hCursor = LoadCursor(NULL, IDC_HELP); wndcls.hIcon = LoadIcon(NULL, IDI_ERROR); wndcls.hInstance = AfxGetInstanceHandle();//获取当前应用程序的句柄 wndcls.lpfnWndProc = ::DefWindowProc;//利用缺省的回调函数 wndcls.lpszClassName = TEXT("sunxin.org"); wndcls.lpszMenuName = NULL; wndcls.style = CS_HREDRAW | CS_VREDRAW;//是窗口类的类型,而不是窗口的类型 RegisterClass(&wndcls); cs.lpszClass = TEXT("sunxin.org");//这个一定要,要不然就不可以按照所写的类进行修改,在框架类中修改图标! //不用编写整个窗口类来修改窗口图标 cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW, 0, 0, LoadIcon(NULL, IDI_WARNING)); cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW);
b.在窗口创建之后对窗口图标的修改需要在cmainframe类中的oncreate()函数中去完成:
//在窗口创建之后对窗口的修改 SetWindowLong(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);//改变指定窗口的属性 SetWindowLong(m_hWnd, GWL_STYLE, GetWindowLong(m_hWnd, GWL_STYLE) &~ WS_MAXIMIZEBOX);//先获取窗口的属性,再修改窗口的属性 SetClassLong(m_hWnd, GCL_HICON, (LONG)LoadIcon(NULL, IDI_ERROR));
c.在窗口创建之前对窗口光标和背景的修改需要在cview类中的precreatewindow()函数中去完成:
cs.lpszClass = TEXT("sunxin.org");//在view类中修改光标和背景 //不用编写整个窗口类来修改窗口光标和背景 cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW, LoadCursor(NULL, IDC_CROSS), (HBRUSH)GetStockObject(BLACK_BRUSH), 0);*/ cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW);
d.在窗口创建之后对窗口光标和背景的修改需要在cview类中的oncreate()函数中去完成:
//在窗口创建之后对窗口背景和光标的修改 SetClassLong(m_hWnd, GCL_HBRBACKGROUND, (LONG)GetStockObject(BLACK_BRUSH)); SetClassLong(m_hWnd, GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_HELP));
3.让图标不断变换的功能的完成:需要在cmainframe类中的oncrea()函数中设置一个计时器:
SetTimer(1, 1000, NULL);//设置一个计时器
需要添加几个ico图标,然后在oncreate()函数中加载其图标:
//创建一个不断变换的图标 m_hIcons[0] = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_ICON1));//第一种获取程序句柄的方法,后面的函数主要是将id号转化为字符串 m_hIcons[1] = LoadIcon(theApp.m_hInstance, MAKEINTRESOURCE(IDI_ICON2));//第二种获取程序句柄的方法 m_hIcons[2] = LoadIcon(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(IDI_ICON3));//第三种获取程序句柄的方法 m_hIcons[3] = LoadIcon(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(IDI_ICON4)); SetClassLong(m_hWnd, GCL_HICON, (LONG)m_hIcons[0]);//将第一幅图标设置为窗口图标
还需要添加一个计时器的消息响应函数:ontimer(),然后在ontimer()函数中完成其功能:
void CMainFrame::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 static int index = 1;//声明为静态的变量 SetClassLong(m_hWnd, GCL_HICON, (LONG)m_hIcons[index]);//对窗口图标的改变 index = ++index % 4;//让index在0--2范围内不断变化 CFrameWnd::OnTimer(nIDEvent); }
4.创建一个新的工具栏:(工具栏也是一个窗口)这个创建类同与菜单栏的创建:要创建新的资源,在加载资源:
//创建一个新的工具栏 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)) { TRACE0("未能创建工具栏\n"); return -1; // 未能创建 } m_newToolBar.EnableDocking(CBRS_ALIGN_ANY);//让工具栏可以停靠 DockControlBar(&m_newToolBar);//让工具栏可以被停靠于框架窗口上
另外还需要注意的两点就是:在工具栏上添加分割符只需要将按钮拖动一下就可以;删除工具栏上的按钮时,如果只按delete的话,只是删除按钮上面的图片,而删除不了按钮,只有将按拖出工具栏才可以将这个按钮彻底删除!
5.让工具栏隐藏和显示:通过菜单项的消息函数来响应:
void CMainFrame::OnNewTool() { // TODO: 在此添加命令处理程序代码 //第一种显示工具栏的方法 //if (m_newToolBar.IsWindowVisible())//判断窗口是否可见 //{ // m_newToolBar.ShowWindow(SW_HIDE); //} //else //{ // m_newToolBar.ShowWindow(SW_SHOW); //} //RecalcLayout();//重新对控制条和客户窗口进行调整 //DockControlBar(&m_newToolBar);//让浮动的工具栏可以在框架窗口上停靠 //第二种显示工具栏的方法 ShowControlBar(&m_newToolBar, !m_newToolBar.IsWindowVisible(), FALSE);//这种方法也可以改变上面方法的缺陷,即可以让浮动的工具栏在原来的位置出现! }
给菜单项添加复选标记:通过对菜单项上的按钮添加update()函数来实现:void CMainFrame::OnUpdateNewTool(CCmdUI *pCmdUI) { // TODO: 在此添加命令更新用户界面处理程序代码 pCmdUI->SetCheck(m_newToolBar.IsWindowVisible());//为菜单项添加复选标记 }
6.状态栏的修改:
a.在状态栏上面添加项目:只需要在string table 中创建其ID号和在cmainframe()的实现文件中对其ID号的声明即可!
static UINT indicators[] = { ID_SEPARATOR, // 状态行指示器 IDS_TIMER, //在状态栏上面添加两项内容,需要在这里声明其ID号 IDS_PROGRESS, ID_INDICATOR_CAPS, ID_INDICATOR_NUM, ID_INDICATOR_SCRL, };
b.在状态栏上的时钟处显示系统时间:在oncreate()函数中完成
//让状态栏上面的时钟处显示系统时间 CTime t = CTime::GetCurrentTime();//返回一个系统时间 CString str = t.Format("%H:%M:%S"); CClientDC dc(this); CSize sz = dc.GetTextExtent(str);//获取显示文本字体的高度和宽度 int index = 0; index = m_wndStatusBar.CommandToIndex(IDS_TIMER);//通过ID号来获取索引号 m_wndStatusBar.SetPaneInfo(index, IDS_TIMER, SBPS_NORMAL, sz.cx);//对状态栏上的窗格的改变! m_wndStatusBar.SetPaneText(index, str);//对状态栏上项目进行设置文本
c.让本来静止的时间动起来:在ontimer()函数中完成:
void CMainFrame::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 //让静止的时间动起来 CTime t = CTime::GetCurrentTime();//返回一个系统时间 CString str = t.Format("%H:%M:%S"); CClientDC dc(this); CSize sz = dc.GetTextExtent(str);//获取显示文本字体的高度和宽度 m_wndStatusBar.SetPaneInfo(1, IDS_TIMER, SBPS_NORMAL, sz.cx);//对状态栏上的窗格的改变! m_wndStatusBar.SetPaneText(1, str);//对状态栏上项目进行设置文本 CFrameWnd::OnTimer(nIDEvent); }
7.进度栏的创建:
a.首先需要在cmainframe类的头文件中添加一个进度栏的对象:CProgressCtrl m_progress
//创建水平进度栏 //m_progress.Create(WS_CHILD | WS_VISIBLE, CRect(100, 100, 300, 120), this, 123); //m_progress.SetPos(50);//设置进度栏的位置 //创建垂直进度栏 m_progress.Create(WS_CHILD | WS_VISIBLE | PBS_VERTICAL, CRect(100, 100, 120, 300), this, 123); m_progress.SetPos(50);
b.将进度栏添加到状态栏上面去:由于消息不能在oncreate()函数中响应,所以需要重新定义一个消息响应函数:首先在cmainframe类的头文件中定义一个消息:#define UM_PROGRESS WM_USER+1 //定义一个消息;然后消息函数原型的声明:afx_msg LRESULT OnProgress(WPARAM, LPARAM);//消息响应函数原型声明(此函数原型的声明不同于vc6.0,需要注意!);还有在oncreate函数中的消息传递:PostMessage(UM_PROGRESS);最后是在cmainframe的cpp文件中的函数关联和函数的实现ON_MESSAGE(UM_PROGRESS, OnProgress)!
LRESULT CMainFrame::OnProgress(WPARAM, LPARAM) { CRect rect; m_wndStatusBar.GetItemRect(2, &rect);//获取状态栏上窗格的矩形大小 m_progress.Create(WS_CHILD | WS_VISIBLE, rect, &m_wndStatusBar, 123); m_progress.SetPos(50); return 0; }
不过上面的实现有一个缺陷:就是当窗口发生变化的时候,而进度栏的位置不会随着窗口的变化而变化,所以还需要作以下的修改:
void CMainFrame::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: 在此处添加消息处理程序代码 CRect rect; m_wndStatusBar.GetItemRect(2, &rect);//获取状态栏上窗格的矩形大小 if (!m_progress.m_hWnd)//判断进度栏是否已创建 m_progress.Create(WS_CHILD | WS_VISIBLE, rect, &m_wndStatusBar, 123); else m_progress.MoveWindow(&rect);//移动窗口 m_progress.SetPos(50); // 不为绘图消息调用 CFrameWnd::OnPaint() }
注意:有了以上的代码之后就不再需要postmessage()这个函数传递消息了!
c.让进度栏前进:只需要在ontimer()函数中添加代码:
m_progress.StepIt();//让进度栏前进!
8.获取鼠标在文本框上面的坐标:
需要在view类中添加消息响应函数onmousemove():
void CID092View::OnMouseMove(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 CString str; str.Format(TEXT("X = %d, Y = %d"), point.x, point.y);//以一定的格式输出 //第一种方法 //((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowTextW(str);//在状态栏上面设置文本信息 //第二种方法 ((CMainFrame*)GetParent())->SetMessageText(str); //第三种方法 ((CMainFrame*)GetParent())->GetMessageBar()->SetWindowTextW(str); //第四种方法 GetParent()->GetDescendantWindow(AFX_IDW_STATUS_BAR)->SetWindowTextW(str); CView::OnMouseMove(nFlags, point); }
注意:需要将m_wndStatusBar定义为公有,要不就访问不了!
9.为程序设计一个启动画面:在vs2008中,没有像vc6.0中可以快速的添加组件就可以解决启动画面的组件,所以需要自己去设计一个类来实现,这可以参照我博客的文章,有提到如何去添加这个类的!