9.5状态栏编程

应用程序最下方就是状态栏。
状态栏分为两个部分:左边最长的那部分称为提示行;第二个部分就是最右边的三个窗格,主要用来显示CapsLock,NumLock,ScrollLock键的状态,称为状态栏指示器。

状态栏对象是在框架类中定义的:CStatusBar m_wndStatusBar;
CStatusBar类就是与状态栏相关的MFC类。
框架类的OnCreate函数中:

if (!m_wndStatusBar.Create(this) ||
  !m_wndStatusBar.SetIndicators(indicators,
    sizeof(indicators)/sizeof(UINT)))
 {
  TRACE0("Failed to create status bar\n");
  return -1;      // fail to create
 }

这段代码首先调用Create函数创建了状态栏对象。
BOOL Create( CWnd* pParentWnd, DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, UINT nID = AFX_IDW_STATUS_BAR );
第一个参数指定状态栏的父窗口指针;第二个参数指定状态栏的风格;第三个就是状态栏的ID。

接着调用SetIndicators函数设置状态栏指示器,其中用到了一个数组参数:indicators。在框架类的源文件中有定义。

static UINT indicators[] =
{
 ID_SEPARATOR,           // status line indicator
 ID_INDICATOR_CAPS,
 ID_INDICATOR_NUM,
 ID_INDICATOR_SCRL,
};

第一个ID是提示行,后面三个是Caps Lock,Num Lock,Scroll Lock键的状态指示器。
后三个ID都是MFC实现设定好的字符串资源ID。Resource View->双击String Table。就可以找到相应的字符串资源。

如果想要修改状态栏的外观,例如添加或减少状态栏上面的窗格,在indicators数组中添加或者减少相应的字符串资源ID即可。本例想在状态栏上显示当前系统的时间和一个进度条控件。
首先在字符串表中增加两个新的字符串资源:
ID:IDS_TIMER,Caption:时钟。
ID:IDS_PROGRESS,Caption:进度栏。
然后将两个字符串的ID添加到indicate数组中。
运行,结果如图。
在这里插入图片描述

现在准备将时钟的字符串显示为系统当前时间。这时用到MFC类:CTime。该类有一个静态的成员函数:GetCurrentTime,返回系统的当前时间。CTime另一个成员函数:Format,用来对CTime类型的时间对象进行格式化,得到时间的字符串。格式:%y表示年,%m表示月,%d表示天,%H表示24小时制,%I表示12小时制。
将字符串显示到状态栏窗格上:CStatusBar类的SetPaneText函数

BOOL SetPaneText( int nIndex, LPCTSTR lpszNewText, BOOL bUpdate = TRUE );

第一个参数是指示数组中的索引,第二个参数是窗格上面显示的文本;第三个参数默认为TRUE,表示设置窗格文本后,该窗格是无效的。
在框架类OnCreate函数最后加上:

CTime t=CTime::GetCurrentTime();
CString str=t.Format("%H:%M:%S");
m_wndStatusBar.SetPaneText(4,str);

由于在指示数组中时钟字符串索引为4。运行结果如图。
在这里插入图片描述

如果不知道窗格字符串ID在indicate数组中的索引,可以利用CStatusBar类成员函数:CommandToIndex函数获取指定ID资源的索引

CTime t=CTime::GetCurrentTime();
CString str=t.Format("%H:%M:%S");
int index;
index=m_wndStatusBar.CommandToIndex(IDS_TIMER);
m_wndStatusBar.SetPaneText(index,str);

但是时间字符串显示地并不完整,需要将窗格的宽度加大一些。因此需要调用CStatusBar类另一个成员函数:SetPanInfo。

BOOL SetPaneText( int nIndex, LPCTSTR lpszNewText, BOOL bUpdate = TRUE );

第一个参数是窗格字符串索引;第二个参数为指定窗格设置新的ID;第三个指示窗格的样式;第四个参数指定窗格新的宽度。

CTime t=CTime::GetCurrentTime();
CString str=t.Format("%H:%M:%S");
CClientDC dc(this);
//获取字符串的宽度
CSize sz=dc.GetTextExtent(str);
int index;
index=m_wndStatusBar.CommandToIndex(IDS_TIMER); m_wndStatusBar.SetPaneInfo(index,IDS_TIMER,SBPS_NORMAL,sz.cx);
m_wndStatusBar.SetPaneText(index,str);

运行结果如图。
在这里插入图片描述

但是这是一个静止的时间,要想实时显示时间,需要在OnTimer定时器消息响应函数加入显示时钟窗格字符串的代码。

void CMainFrame::OnTimer(UINT nIDEvent) 
{
 // TODO: Add your message handler code here and/or call default
 static int index=0;
 SetClassLong(m_hWnd,GCL_HICON,(LONG)m_hIcons[index]);
 index=++index%3;
 CTime t=CTime::GetCurrentTime();
 CString str=t.Format("%H:%M:%S");
 CClientDC dc(this);
 //获取字符串的宽度
 CSize sz=dc.GetTextExtent(str);
m_wndStatusBar.SetPaneInfo(4,IDS_TIMER,SBPS_NORMAL,sz.cx);
 m_wndStatusBar.SetPaneText(4,str);
 CFrameWnd::OnTimer(nIDEvent);
}

9.6进度栏编程

MFC中,进度栏也有一个相关的类:CProgressCtrl,派生于CWnd类,它也是一个窗口类。
在程序中使用进度栏,首先构造一个CProgressCtrl对象,然后调用CProgressCtrl类的Create函数创建进度栏控件。

BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );

第一个指进度栏控件的类型,除了窗口的所有类型,它也有自己的类型:PBS_VERTICAL表示垂直放置;PBS_SMOOTH表示水平放置。
第二个参数表示进度栏控件的大小和位置。
第三个控件表示进度栏的父窗口。
第四个控件是进度栏的ID。

9.6.1在窗口中创建进度栏

首先为框架类添加一个一个CProgressCtrl类型的成员变量:m_progress。
然后在框架类OnCreate函数之中创建一个进度栏,该消息响应函数中添加代码:

m_progress.Create(WS_CHILD|WS_VISIBLE,CRect(100,100,200,120),this,123);

运行结果如图。
在这里插入图片描述
利用CProgressCtrl类型的成员函数:SetPos可以设置进度栏当前进度。在上述代码之后添加:

m_progress.SetPos(50);

运行结果如图。
在这里插入图片描述
也可以设置一个垂直的进度栏:

m_progress.Create(WS_CHILD|WS_VISIBLE|PBS_VERTICAL,CRect(100,100,200,120),this,123);

9.6.2在状态栏的窗格中创建进度栏

在状态栏的窗格中显示进度栏,首先得获得该窗格的区域。这里用CStatusBar类的GetItemRect成员函数来完成:

void GetItemRect( int nIndex, LPRect lprect) const;

第一个参数是窗格的索引,第二个参数指示窗格的矩形区域。

在框架类OnCreate函数中修改:

CRect rect;
m_wndStatusBar.GetItemRect(5,&rect); m_progress.Create(WS_CHILD|WS_VISIBLE,rect,this,123);
m_progress.SetPos(50);

字符串为进度栏的窗格的索引为5。
运行发现,在状态栏上面并未创建进度栏。
原因是此时状态栏的初始化工作,即窗格的摆放操作还没有完成。只有框架类的OnCreate函数完成之后,才能获得状态栏上窗格的矩形区域。

为了避免自定义的消息与已有的消息冲突,可以利用Windows提供的一个常量:WM_USER,小于这个常量的值都是Windows系统保留的消息。
首先在框架类的头文件中定义一条自定义消息:
#define UM_PROGRESS WM_USER+1
接着为这条自定义消息添加消息响应函数原型的声明:

//{{AFX_MSG(CMainFrame)
 afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
 afx_msg void OnTimer(UINT nIDEvent);
 afx_msg void OnTest();
 afx_msg void OnViewNewtoolbar();
 afx_msg void OnUpdateViewNewtoolbar(CCmdUI* pCmdUI);
 //}}AFX_MSG
 afx_msg void OnProgress();
 DECLARE_MESSAGE_MAP()

接下来为自定义消息添加消息映射,对于自定义消息,使用ON_MESSAGE宏来实现这一功能:

//{{AFX_MSG_MAP(CMainFrame)
 ON_WM_CREATE()
 ON_WM_TIMER()
 ON_COMMAND(IDM_TEST, OnTest)
 ON_COMMAND(IDM_VIEW_NEWTOOLBAR, OnViewNewtoolbar)
 ON_UPDATE_COMMAND_UI(IDM_VIEW_NEWTOOLBAR, OnUpdateViewNewtoolbar)
 //}}AFX_MSG_MAP
 ON_MESSAGE(UM_PROGRESS,OnProgress)

最后就是添加消息响应函数实现:

void CMainFrame::OnProgress()
{
 CRect rect;
 m_wndStatusBar.GetItemRect(5,&rect); m_progress.Create(WS_CHILD|WS_VISIBLE,rect,&m_wndStatusBar,123);
 m_progress.SetPos(50);
}

将框架类之前创建进度栏的代码注释起来,但是在框架类OnCreate函数的最后发送UM_PROGRESS消息:

SendMessage(UM_PROGRESS);

运行,发现还是在相应的窗格上没有出现进度栏,原因就是,发送完该消息时,程序直接执行相应的消息处理函数,而此时OnCreate函数并没有执行完毕。

因此不能使用SendMessage函数,而应该使用PostMessage,该函数把消息放到消息队列中,立即返回;执行完OnCreate函数后,再取出UM_PROGRESS消息,执行相应的消息响应函数。
将SendMessage换成PostMessage,运行如图。
在这里插入图片描述
如果想要连续平滑的效果,在创建进度栏时候,就需要设置PBS_SMOOTH类型。如图。
在这里插入图片描述
但是窗口尺寸发生变化时,进度栏显示的位置就会发生错误,如图。
在这里插入图片描述

这就需要我们在窗口尺寸发生变化时,重新获取索引号为5的窗格,将进度栏移动到该窗格中。由于窗口第一次显示就需要调用OnPaint函数,因此不需要发送自定义消息,注释PostMessage这一行。
为框架类添加WM_PAINT消息响应函数:

void CMainFrame::OnPaint() 
{
 CPaintDC dc(this); // device context for painting
 // TODO: Add your message handler code here
 CRect rect;
 m_wndStatusBar.GetItemRect(5,&rect);
m_progress.Create(WS_CHILD|WS_VISIBLE|PBS_SMOOTH,rect,&m_wndStatusBar,123);
 m_progress.SetPos(50);
 // Do not call CFrameWnd::OnPaint() for painting messages
}

运行程序,会弹出一个非法操作框,原因就是之前程序已经创建了一个进度栏与m_progress对象相关联了。这里不能再一次创建进行关联。因此需要增加一个判断:

void CMainFrame::OnPaint() 
{
 CPaintDC dc(this); // device context for painting 
 // TODO: Add your message handler code here
 CRect rect;
 m_wndStatusBar.GetItemRect(5,&rect);
 if(!m_progress.m_hWnd)
 {  m_progress.Create(WS_CHILD|WS_VISIBLE|PBS_SMOOTH,rect,&m_wndStatusBar,123);
 }
 else
 {
  m_progress.MoveWindow(rect);
 }
 m_progress.SetPos(50);
 // Do not call CFrameWnd::OnPaint() for painting messages
}

这里用到了一个MoveWindow函数,将进度栏移动到目标矩形区域中。运行如图。
在这里插入图片描述

下面让进度栏动起来,即在进度栏上以某种显示方式不断增加当前位置,这可以通过CProgressCtrl类的StepIt成员函数完成;而每次前进的步长可以通过CProgressCtrl类的SetStep来设置;还可以通过CProgressCtrl类的SetRange成员函数来设置进度栏的范围,默认范围是0~100。
现在实现每隔一秒,就前进一步,在框架类的OnTimer函数倒数第二行添加:

m_progress.StepIt();

运行如图。
在这里插入图片描述

9.7在状态栏上显示鼠标的位置

要捕获与鼠标相关的消息,应该在视类中添加WM_MOUSEMOVE消息的响应函数。

void CStyleView::OnMouseMove(UINT nFlags, CPoint point) 
{
 // TODO: Add your message handler code here and/or call default
 CString str;
 str.Format("x=%d,y=%d",point.x,point.y);
 ((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str);
 CView::OnMouseMove(nFlags, point);
}

因为视类用到框架类的成员变量,所以要将m_wndStatusBar设置为public类型。而且在源文件中还需要包含"MianFrm.h"头文件,SetWindowText(str)可以直接将鼠标坐标信息放置到状态栏的第一个窗格中。

第二种方法就是利用框架类成员函数:SetMessageText函数,该函数的作用就是将ID为0的状态栏窗格设置一个字符串。

((CMainFrame*)GetParent())->SetMessageText(str);

9.8启动画面

由于VC++6.0添加的控件只有在WindowsXP下才可以用,这里不做具体分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

身影王座

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值