孙鑫视频教程第九课——工具栏、状态栏、启动画面

  

       如何修改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中可以快速的添加组件就可以解决启动画面的组件,所以需要自己去设计一个类来实现,这可以参照我博客的文章,有提到如何去添加这个类的!

你可能感兴趣的:(timer,框架,null,delete,mfc,工具)