接下来我们看一下状态栏的编程。
分为两类:
第一类:提示行,左边上的那一块,主要用来菜单项的命令和工具栏的命令
第二类:窗格形式,显示开关的状态,状态指示器。例如键盘Caps LOCK这样的开关的状态
状态栏也是在框架类里边定义的
CStatusBar m_wndStatusBar; //定义一个状态栏的对象
然后呢, 在OnCreate的函数中创建一个状态栏。
if (!m_wndStatusBar.Create(this) ||
这里 调用了一个函数 SetIndicators()设计一个指示器 ,在这里 第一个参数用了一个指示器的数组。第二个参数
那么让我们来看一下指示器数组的定义。
static UINT indicators[] =
{
};
如果我们想修改状态栏窗格的数目,我们只需要在indicators当中添加字符串资源的ID,或者减少字符串资源的ID。在字符串表中定义。
我们可以在ID_SEPAPATOR的下一行添加窗格。
首先在字符串资源中定义一个IDS_TIMER
然后添加。
这就是指示器的数组,我们可以看到的一些ID号。ID_SEPARATOR这个ID号就表示了状态栏最长的部分,我们叫做提示行。后便的三个ID号,分别表示了键盘上的:NumLock键,CapsLock键 ScrollLock键
接下来我们完成一个功能,就是在我们时钟的窗格显示一个系统的时间。
这就需要我们先去获取系统的时间。用到CTime类。
有这样一个方法static CTime PASCAL GetCurrentTime( );
能够返回一个CTime对象,表示当前的时间。
然后我们用CTime中的
CString Format( LPCTSTR pFormat ) const;
CString Format( UINT nFormatID ) const;
这个方法将时间做一个格式化,返回这个时间的小时,分钟和秒
我们获取了表示时间的字符串对象,如何将它设置为我们时钟这个窗格显示的内容
这个时候我们需要用到状态栏所对应的C++中的类,CStatusBar中的一个函数SetPaneText()
BOOL SetPaneText( int nIndex, LPCTSTR lpszNewText, BOOL bUpdate = TRUE );
第一个参数,面板的一个索引,就是在指示器中的索引,第二个参数,新的文本,第三个参数,如果为真在文本输入之后面板是无效的,当下一次WM_PANE消息发送的时候面板会发生重绘。
例:
CTime t=CTime::GetCurrentTime();
如果我们不知道这个字符串的索引。
我们就可以用CStatucBar类的一个方法CommandToIndex
int CommandToIndex( UINT nIDFind ) const;
作用是给定一个字符串资源的ID号来获取索引。
例:
int index=0;//定义一个变量储存索引
index=m_wndStatusBar.CommandToIndex(IDS_TIMER); //得到字符串的索引。
下面我们来了解如何改变窗格的宽度。
这就用到了CStatusBar这个类中的方法:GetPaneInfo
void GetPaneInfo( int nIndex, UINT& nID, UINT& nStyle, int& cxWidth ) const;
第一个参数,要修改的指示器面板的索引。第二个参数,给面板可以重新分配一个ID号。第三个参数,它的一个类型,有参数列表。第四个参数,面板的宽度。
要得到字符串显示时候的一个宽度。用GetTextExtent(str0;)
例:
CTime t=CTime::GetCurrentTime();
此时的时间是一个固定的时间,而我们想要的时间是一个变化的。我们就需要一个定时器
SetTimer(1,1000,NULL); 每一秒钟发送一个消息。
然后我们在OnTimer中执行
CTime t=CTime::GetCurrentTime();
进度栏:
MFC中有一个相关类:CProgressCtrl
CProgressCtrl( );
在我们构造了CProgressCtrl之后,调用Create方法去创建一个进度栏的控件。
我们看一下这个Create方法
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
第一个参数
接下来我们就创建一个进度栏
首先我们在框架类构造一个框架类的对象:CProgressCtrl m_progress;
然后在OnCreate这个函数当中。在窗口创建完成之后,创建:
m_progress.Create(WS_CHILD|WS_VISIBLE|PBS_VERTICAL,CRect(100,100,120,200),this,123);//垂直进度栏
我们可以让进度栏的位置设置为我们指定的位置,我们可以用CProgressCtrl类中的一个方法:SetPos
int SetPos( int nPos );
设置进度栏的当前位置。
例:
m_progress.SetPos(50); //进度栏进度到一半的状态。即50%
接下来,我们要把进度栏添加到状态栏的窗格处:
在CStatusBar中有一个方法
void GetItemRect( int nIndex, LPRECT lpRect ) const;
得到指定索引的窗格的区域,第一个参数,指示器的索引,第二个参数,接收指定索引的指示器矩形区域的坐标
状态栏的初始化没有完全完成,所以直接用GetItemRect这个函数,得到的是一个无效的矩形区域。所以我们要定义个消息,在OnCreate这个函数调用完成之后,在消息响应函数中完成矩形区域的获取。
我们在头文件中定义一个消息:
在Windows当中消息都是用整数来表示的,我们如何避免跟系统的消息发生冲突呢?Windows为我们提供了一个宏
WM_USER,在这个WM_USER数值一下的有我们的系统保留着,我们定义消息时,就定义一个比WM_USER大的就可以了,于是我们定义消息是,让WM_USER+一个数,加多少自己定义,这里我们加1就可以。
例:#define UM_PROPRESS
有了消息之后,我们就要做消息响应函数的原型的声明。
同样是在头文件中:
在消息响应函数声明的区域,做消息响应函数的原型的声明。
protected:
接下来消息映射,我们在源文件中定义消息映射:
对于消息来说我们用ON_MESSAGE这个宏。对于命令消息来说我们用ON_COMMAND这个宏,将我们的消息跟消息响应函数关联起来。
例:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
END_MESSAGE_MAP()
最后就是我们的消息响应函数实现部分。
void CMainFrame::OnProgress()
{
//
}
我们在OnCreate函数中发送一个消息。
SendMessage(UM_PROGRESS); //因为SendMessage它发送消息,直接把消息发送给消息响应函数,当消息处理完成以后再返回。所以在这里我们发送消息的时候不能用SendMessage
我们这里用到PostMessage(UM_PROGRESS);这个函数
它将消息放到消息队列当中,然后根据消息摆放的顺序,通过GetMessage一条一条的消息取出,他把消息放到消息队列就立即返回了。OnCreate函数就执行完成了。执行完成后,MFC底层代码GetMessage取出代码后,消息响应函数才执行了。
当我们改变了窗口的大小的时候,我们的进度栏就不在我们的窗格里了,这是因为我们取到的是窗格的坐标,一旦窗体大小改变了,窗格的坐标也改变了,我们想使进度栏一直在窗格里应该怎么实现呢?
实现方法是,当我们窗格发生变化的时候,重新获取窗格的矩形区域。
因为窗口重绘的时候会发送一个WM_PAINT的消息,我们只需要在响应这个消息的函数中,重新获取窗格的矩形的区域,把进度栏移动到这个区域就可以了。
在OnPaint这个函数中我们需要判断一下,状态栏是否创建了。没有创建,那么我们就创建一个,创建了,我们就直接把状态栏移动到窗格的矩形区域就可以了。
例:
CRect rect;
接下来我们要做的是让进度栏进度动起来。
那么我们就要在CProgressCtrl的成员函数中调用一个
int StepIt( );
作用就是按一定的步长前进。
我们通过
int SetStep( int nStep );
这个函数来设置步长。
我们也可以设置进度栏的范围
void SetRange( short nLower, short nUpper );
void SetRange32( int nLower, int nUpper );
缺省范围最小是0最大100。根据我们要完成的功能,去设定范围。例如你要播放影片,那么就可以根据影片播放的时间来设定进度栏的范围。
我们在On_Timer中完成一个功能,每一秒钟走一个步长。
就可以直接用m_progress.StepIt(); 就可以完成了。当然,我们步长用的是默认的值10;
首先,我们要捕获鼠标移动的这个消息。
因为我们的View类在框架类之上,所以,鼠标移动是在View的窗口上进行的,所以我们在View类中捕获鼠标移动,然后呢,我们要获取状态栏,状态栏在框架类中定义的,首先我们需要获取框架类的指针,即通过
GetParent()这个函数获得
例:
void CStyleView::OnMouseMove(UINT nFlags, CPoint point)
{
}
我们可以用
void SetMessageText( LPCTSTR lpszText );
void SetMessageText( UINT nID );
这个函数,它属于CFrameWnd的成员函数,可以直接把str放置到状态栏的指示行上去。不再需要获取状态栏的对象或者指针。
我们可以把((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str);换成
((CMainFrame*)GetParent())->SetMessageText(str);来实现我们想要的功能。
我们看第三种方式:
要用到另外一种函数,这个函数叫 GetMessageBar 也是CFrameWnd的成员函数。它返回的是一个指向状态栏窗口的指针。就可以不用把m_wndStatusBar的保护类型换成公共类型;
例:
((CMainFrame*)GetParent())->GetMessageBar()->SetWindowText(str);
我们看一下第四种方式:
要用到一个函数GetDescendantWindow
CWnd* GetDescendantWindow( int nID, BOOL bOnlyPerm = FALSE ) const;
它是CWnd的成员函数,返回一个CWnd的指针,这个函数通过给定的一个ID号还获取一个子孙窗口,搜索整个子窗口处,知道找到我们所给定ID的子孙窗口。我们传递状态栏的ID他就会得到一个状态栏的指针。因为我们状态栏属于框架类,所以我们需要通过框架类的指针来调用这个函数。
我们看一下第二个参数:它是用来指定是否这个窗口对象可以作为一个临时的被返回,如果为真只有持久的窗口对象被返回,如果为false,他可以返回一个临时的窗口对象。
MFC临时窗口和永久窗口:
在MFC中,都是以C++对象来操作窗口,而窗口是用句柄来标识的,这样就需要将窗口和C++对象关联起来,通过C++对象的成员变量m_hWnd来建立这种联系。如果一个窗口对象和一个窗口相关联了,那么我们要获取这个窗口对象(通常都是CWnd*形式),返回的就是一个持久的C++对象,如果你要获取一个窗口(不是通过MFC类库创建的)的C++对象,那么MFC就会为你临时创建一个C++对象,返回其指针,这就是一个临时的对象。作为临时对象,它在它产生的函数中有效,例如:你在OnMouseMove中获取到一个临时对象的指针,那么它在OnMouseMove函数中是有效的,但出来这个函数,就不一定了,因为在Windows消息循环的空闲时间,临时对象将会被删除。
我们看一下状态栏类CStatusBar的类成员:Create
BOOL Create( CWnd* pParentWnd, DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, UINT nID = AFX_IDW_STATUS_BAR );
我们会发现有一个ID号,这个ID号就是系统预先为状态栏定义的ID号。
例:
GetParent()->GetDescendantWindow(AFX_IDW_STATUS_BAR)->SetWindowText(str);
我们看一下MFC为我们定义的常用的控件的ID号
#define AFX_IDW_TOOLBAR
#define AFX_IDW_STATUS_BAR
#define AFX_IDW_PREVIEW_BAR
#define AFX_IDW_RESIZE_BAR
#define AFX_IDW_REBAR
#define AFX_IDW_DIALOGBAR