MFC状态栏的编程

接下来我们看一下状态栏的编程。

分为两类:

第一类:提示行,左边上的那一块,主要用来菜单项的命令和工具栏的命令

第二类:窗格形式,显示开关的状态,状态指示器。例如键盘Caps LOCK这样的开关的状态

状态栏也是在框架类里边定义的

CStatusBar m_wndStatusBar; //定义一个状态栏的对象

然后呢, 在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
}

这里 调用了一个函数 SetIndicators()设计一个指示器 ,在这里 第一个参数用了一个指示器的数组。第二个参数

那么让我们来看一下指示器数组的定义。

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

如果我们想修改状态栏窗格的数目,我们只需要在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();
CString str;
str=t.Format("%H:%M:%S");
m_wndStatusBar.SetPaneText(1,str,true);

如果我们不知道这个字符串的索引。

我们就可以用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();
CString str;
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,true);//在窗格中显示str的内容

此时的时间是一个固定的时间,而我们想要的时间是一个变化的。我们就需要一个定时器

SetTimer(1,1000,NULL); 每一秒钟发送一个消息。

然后我们在OnTimer中执行

CTime t=CTime::GetCurrentTime();
CString str;
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,true);//在窗格中显示str的内容

进度栏:

MFC中有一个相关类:CProgressCtrl

首先我们看一下它的构造函数。

CProgressCtrl( );

在我们构造了CProgressCtrl之后,调用Create方法去创建一个进度栏的控件。

我们看一下这个Create方法

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

第一个参数 指示进度栏的一个类型,一个进度栏也是从CWnd间接派生而来,所以他有窗口的类型,也就自己的类型。第二个参数是指进度栏的大小,通过rect结构体,来指示进度栏的大小,第三个参数,设定进度栏的父窗口。最后一个参数,指定进度栏的ID号

接下来我们就创建一个进度栏

首先我们在框架类构造一个框架类的对象:CProgressCtrl m_progress;

然后在OnCreate这个函数当中。在窗口创建完成之后,创建:

m_progress.Create(WS_CHILD|WS_VISIBLE|PBS_SMOOTH,CRect(100,100,200,120),this,123);//水平进度栏

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 WM_USER+1 //系统消息一般是WM开头,而用户消息我们用UM表示

有了消息之后,我们就要做消息响应函数的原型的声明。

同样是在头文件中:

在消息响应函数声明的区域,做消息响应函数的原型的声明。

protected:
//{{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这个宏。对于命令消息来说我们用ON_COMMAND这个宏,将我们的消息跟消息响应函数关联起来。

例:

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_WM_TIMER()
ON_COMMAND(IDM_TEST, OnTest)
ON_COMMAND(IBM_VIEW_NEWTOOLBAR, OnViewNewtoolbar)
ON_UPDATE_COMMAND_UI(IBM_VIEW_NEWTOOLBAR, OnUpdateViewNewtoolbar)
//}}AFX_MSG_MAP
ON_MESSAGE(UM_PROGRESS,OnProgress) //这就是我们自己定义的消息映射
END_MESSAGE_MAP()

最后就是我们的消息响应函数实现部分。

void CMainFrame::OnProgress()
{
CRect rect;
m_wndStatusBar.GetItemRect(2,&rect);
m_progress.Create(WS_CHILD|WS_VISIBLE|PBS_SMOOTH,rect,&m_wndStatusBar,123);
// m_progress.Create(WS_CHILD|WS_VISIBLE|PBS_VERTICAL,CRect(100,100,120,200),this,124);
m_progress.SetPos(50);
}

我们在OnCreate函数中发送一个消息。

SendMessage(UM_PROGRESS); //因为SendMessage它发送消息,直接把消息发送给消息响应函数,当消息处理完成以后再返回。所以在这里我们发送消息的时候不能用SendMessage

我们这里用到PostMessage(UM_PROGRESS);这个函数

它将消息放到消息队列当中,然后根据消息摆放的顺序,通过GetMessage一条一条的消息取出,他把消息放到消息队列就立即返回了。OnCreate函数就执行完成了。执行完成后,MFC底层代码GetMessage取出代码后,消息响应函数才执行了。

当我们改变了窗口的大小的时候,我们的进度栏就不在我们的窗格里了,这是因为我们取到的是窗格的坐标,一旦窗体大小改变了,窗格的坐标也改变了,我们想使进度栏一直在窗格里应该怎么实现呢?

实现方法是,当我们窗格发生变化的时候,重新获取窗格的矩形区域。

因为窗口重绘的时候会发送一个WM_PAINT的消息,我们只需要在响应这个消息的函数中,重新获取窗格的矩形的区域,把进度栏移动到这个区域就可以了。

在OnPaint这个函数中我们需要判断一下,状态栏是否创建了。没有创建,那么我们就创建一个,创建了,我们就直接把状态栏移动到窗格的矩形区域就可以了。

例:

CRect rect;
m_wndStatusBar.GetItemRect(2,&rect);
if(!m_progress.m_hWnd) //通过判断对象句柄是否为NULL来判断进度栏是否创建
{
m_progress.Create(WS_CHILD|WS_VISIBLE|PBS_SMOOTH,rect,&m_wndStatusBar,123);
// m_progress.Create(WS_CHILD|WS_VISIBLE|PBS_VERTICAL,CRect(100,100,120,200),this,124);

}
else
{
m_progress.MoveWindow(&rect); //移动进度栏到矩形区域
}
m_progress.SetPos(50); //进度栏进度到50%的位置。

接下来我们要做的是让进度栏进度动起来。

那么我们就要在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)
{
// TODO: Add your message handler code here and/or call default
CString str; //定义一个CString对象
str.Format("x=%d,y=%d",point.x,point.y); //让str按一定的形式接受两个点的坐标
((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str);//通过获取框架类的指针,从而获取

//状态栏,然后通过函数SetWindowText()这个函数显示到指示行
//需要注意的是,这里用到了(CMainFrame*)这个强制转换,需要

//包含"MainFrm.h"这个头文件。
CView::OnMouseMove(nFlags, 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); //因为 GetDescendantWindow本身就是CWnd的一个成员函数,所以不需要对GetParent()进行强制类型转换了。

我们看一下MFC为我们定义的常用的控件的ID号

#define AFX_IDW_TOOLBAR 0xE800 //工具栏的ID
#define AFX_IDW_STATUS_BAR 0xE801 // 状态栏的ID
#define AFX_IDW_PREVIEW_BAR 0xE802 // PrintPreview Dialog Bar
#define AFX_IDW_RESIZE_BAR 0xE803 // OLE in-place resize bar
#define AFX_IDW_REBAR 0xE804 // COMCTL32 "rebar" Bar
#define AFX_IDW_DIALOGBAR 0xE805 //控制条的ID

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值