MFC状态栏编程
1、在状态栏中设置两个新的栏位Timer和Progress。首先到ResourceView中编辑String Table,增加IDS_TIMER(时间),PROGRESS(进度)。然后在MainFrame的OnCreate函数中修改indicators数组,插入IDS_TIMER和PROGRESS。插入的位置,即为显示的位置。
2、现在我们让状态栏上的IDS_TIMER的位置显示系统当前的时间。我们只要在MainFrame的OnCreate函数中写入:
CTime t=CTime::GetCurrentTime(); //获得当前的系统时间
CClientDC dc(this);
CSize sz=dc.GetTextExtent(str); //设置一个CClientDC对象来获取str的长度
CString str=t.Format("%H:%M:%S"); //格式可以参考MSDN中的strftime函数
int index=0;
index=m_wndStatusBar.CommandToIndex(IDS_TIMER); //此处也可以直接输入IDS_TIMER在indicators数组中从0开始的序号。
m_wndStatusBar.SetPaneInfo(index,IDS_TIMER,SBPS_NORMAL,sz.cx); //设置状态栏宽度
m_wndStatusBar.SetPaneText(index,str); //设置IDS_TIMER指示器字符串
如果我们在MainFrame的OnCreate函数中,添加一个
SetTimer(1,1000,NULL);
并在OnTimer消息响应函数中,插入以上代码,便可实现随时变化时间的状态栏。
3、在窗口中创建一个进度栏
首先,在MainFrame中新建一个成员变量 private: CProgressCtr m_progress; 然后在OnCreate函数中,写入:
m_progress.Create(WS_CHILD|WS_VISIBLE, CRect(100,100,200,130), this, 123); /*在MainFrame的10,40位置,产生一个长100,高30的进度栏,注意在CRect中,后两个数一定要大于前两个,否则会产生一个虚框,看不见任何东西。如果产生一个垂直的进度栏,只要在第一个参数中或一个PBS_VERTICAL参数即可*/
m_progress.SetPos(50); /*进度栏显示为50%*/
4、现在我们将状态栏中的PROGRESS栏位显示一个50%的进度栏。如果我们只是简单的在MainFrame的OnCreate函数中写入以下代码:
CRect rect;
m_wndStatusBar.GetItemRect(m_wndStatusBar.CommandToIndex(IDS_PROGRESS),&rect); /*获取IDS_PROGRESS栏位的CRect值,付给rect*/
m_progress.Create(WS_CHILD|WS_VISIBLE, rect, &m_wndStatusBar, 123); /*在IDS_PROGRESS的位置显示一个50%的进度栏*/
m_progress.SetPos(50);
你会发现IDS_PROGRESS栏位仍然显示的是“进度”两个字。这是为什么呢?原因是因为,在OnCreate函数中,窗台和状态栏并没有真正创建完毕,所以GetItemRect函数无法获取IDS_PROGRESS栏位的CRect值。解决的办法是,添加一个自定义的消息,把消息放入消息队列,等窗体和状态栏创建完毕后,再通过消息响应,创建进度栏。步骤如下:
4.1在MainFrame的头文件中,自定义个消息
#include UM_PROGRESS WM_USER+1 //自定义的消息ID,注意要在WM_USER= 0x0400之后。
4.2然后添加消息响应原型
protected:
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnViewNewtool();
afx_msg void OnUpdateViewNewtool(CCmdUI* pCmdUI);
//}}AFX_MSG
afx_msg void OnProgress(); //定义消息响应原形
DECLARE_MESSAGE_MAP()
4.3接着在MainFrame中添加消息映射
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_WM_TIMER()
ON_COMMAND(IDM_VIEW_NEWTOOL, OnViewNewtool)
ON_UPDATE_COMMAND_UI(IDM_VIEW_NEWTOOL, OnUpdateViewNewtool)
//}}AFX_MSG_MAP
ON_MESSAGE(UM_PROGRESS,OnProgress) //添加消息映射
END_MESSAGE_MAP()
4.5然后再return 0;之前发布消息:
PostMessage(UM_PROGRESS); /*注意这里要用PostMessage不能用SendMessage,否则系统会先处理OnProgress消息响应,然后再创建窗体,出现仍看不见进度栏的情况。必须用PostMessage先把消息放入消息响应队列当中,等窗体创建完毕后,系统再处理OnProgress函数。*/
4.4最后写OnProgress函数
void CMainFrame::OnProgress()
{
CRect rect;
m_wndStatusBar.GetItemRect(m_wndStatusBar.CommandToIndex(IDS_PROGRESS),&rect);
m_progress.Create(WS_CHILD|WS_VISIBLE, rect, &m_wndStatusBar, 123);
m_progress.SetPos(50);
}
这时在IDS_PROGRESS状态栏中就能看到进度栏了。但是还存在问题,当改变窗口大小时,进度栏会离开IDS_PROGRESS栏位位置。
5、另一种简单的方法可以实现以上相同的功能,并且可以解决以上的问题,是利用windows窗体发生重绘时的消息响应函数。在MainFrame中右键增加一个windows的WM_PAINT消息响应函数,然后加入一下代码:
CRect rect;
m_wndStatusBar.GetItemRect(m_wndStatusBar.CommandToIndex(IDS_PROGRESS),&rect);
if (!m_progress.m_hWnd) /*注意这里必须增加一个判断,否则在窗体再次发生重绘时,
由于已经存在m_progress实例,会出现一个错误。*/
{
m_progress.Create(WS_CHILD|WS_VISIBLE|PBS_SMOOTH , rect, &m_wndStatusBar, 123);
}
else
{
m_progress.MoveWindow(rect); /*移动窗口,比SetWindowPos简单。*/
}
m_progress.SetPos(50);
6、现在我们要让状态栏上显示鼠标在窗口中的坐标。由于View窗体在MainFrame框架前,所以我们要响应的是View窗体上OnMouseMove事件。
首先我们在View类中右键新建一个window消息响应函数,添加一个WM_MOUSEMOVE的消息响应函数。添加如下代码:
CString str;
str.Format("x=%d,y=%d",point.x,point.y); /*对鼠标坐标进行格式化*/
((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str); /*由于作用的是View窗口的父窗口MainFrame下的m_wndStatusBar对象,所以必须将m_wndStatusBar设置为public,然后用(CMainFrame*)指名其为框架类指针,否则->后面不会出现m_wndStatusBar对象。注意在View程序中必须包含MainFrame的头文件*/
也可以将最有一句改为: ((CMainFrame*)GetParent())->SetMessageText(str);直接由框架类函数中的SetMessageText实现对状态栏文字的更改。
第三种实现方法是: ((CMainFrame*)GetParent())->GetMessageBar()->SetWindowText(str);通过框架类函数GetMessageBar来获得指向状态栏窗口的指针。这种方法的好处是,不需要再把MainFrame中的m_wndStatusBar设置为public。
第四种方法: GetParent()->GetDescendantWindow(AFX_IDW_STATUS_BAR)->SetWindowText(str); 因为GetDescendantWindow本来就是CWnd的成员函数,所以不需要转换为框架类对象,并根据ID号,来获得子孙窗口的对象指针,这里的AFX_IDW_STATUS_BAR,是由CStatusBar::Create默认产生的。
1、在状态栏中设置两个新的栏位Timer和Progress。首先到ResourceView中编辑String Table,增加IDS_TIMER(时间),PROGRESS(进度)。然后在MainFrame的OnCreate函数中修改indicators数组,插入IDS_TIMER和PROGRESS。插入的位置,即为显示的位置。
2、现在我们让状态栏上的IDS_TIMER的位置显示系统当前的时间。我们只要在MainFrame的OnCreate函数中写入:
CTime t=CTime::GetCurrentTime(); //获得当前的系统时间
CClientDC dc(this);
CSize sz=dc.GetTextExtent(str); //设置一个CClientDC对象来获取str的长度
CString str=t.Format("%H:%M:%S"); //格式可以参考MSDN中的strftime函数
int index=0;
index=m_wndStatusBar.CommandToIndex(IDS_TIMER); //此处也可以直接输入IDS_TIMER在indicators数组中从0开始的序号。
m_wndStatusBar.SetPaneInfo(index,IDS_TIMER,SBPS_NORMAL,sz.cx); //设置状态栏宽度
m_wndStatusBar.SetPaneText(index,str); //设置IDS_TIMER指示器字符串
如果我们在MainFrame的OnCreate函数中,添加一个
SetTimer(1,1000,NULL);
并在OnTimer消息响应函数中,插入以上代码,便可实现随时变化时间的状态栏。
3、在窗口中创建一个进度栏
首先,在MainFrame中新建一个成员变量 private: CProgressCtr m_progress; 然后在OnCreate函数中,写入:
m_progress.Create(WS_CHILD|WS_VISIBLE, CRect(100,100,200,130), this, 123); /*在MainFrame的10,40位置,产生一个长100,高30的进度栏,注意在CRect中,后两个数一定要大于前两个,否则会产生一个虚框,看不见任何东西。如果产生一个垂直的进度栏,只要在第一个参数中或一个PBS_VERTICAL参数即可*/
m_progress.SetPos(50); /*进度栏显示为50%*/
4、现在我们将状态栏中的PROGRESS栏位显示一个50%的进度栏。如果我们只是简单的在MainFrame的OnCreate函数中写入以下代码:
CRect rect;
m_wndStatusBar.GetItemRect(m_wndStatusBar.CommandToIndex(IDS_PROGRESS),&rect); /*获取IDS_PROGRESS栏位的CRect值,付给rect*/
m_progress.Create(WS_CHILD|WS_VISIBLE, rect, &m_wndStatusBar, 123); /*在IDS_PROGRESS的位置显示一个50%的进度栏*/
m_progress.SetPos(50);
你会发现IDS_PROGRESS栏位仍然显示的是“进度”两个字。这是为什么呢?原因是因为,在OnCreate函数中,窗台和状态栏并没有真正创建完毕,所以GetItemRect函数无法获取IDS_PROGRESS栏位的CRect值。解决的办法是,添加一个自定义的消息,把消息放入消息队列,等窗体和状态栏创建完毕后,再通过消息响应,创建进度栏。步骤如下:
4.1在MainFrame的头文件中,自定义个消息
#include UM_PROGRESS WM_USER+1 //自定义的消息ID,注意要在WM_USER= 0x0400之后。
4.2然后添加消息响应原型
protected:
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnViewNewtool();
afx_msg void OnUpdateViewNewtool(CCmdUI* pCmdUI);
//}}AFX_MSG
afx_msg void OnProgress(); //定义消息响应原形
DECLARE_MESSAGE_MAP()
4.3接着在MainFrame中添加消息映射
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_WM_TIMER()
ON_COMMAND(IDM_VIEW_NEWTOOL, OnViewNewtool)
ON_UPDATE_COMMAND_UI(IDM_VIEW_NEWTOOL, OnUpdateViewNewtool)
//}}AFX_MSG_MAP
ON_MESSAGE(UM_PROGRESS,OnProgress) //添加消息映射
END_MESSAGE_MAP()
4.5然后再return 0;之前发布消息:
PostMessage(UM_PROGRESS); /*注意这里要用PostMessage不能用SendMessage,否则系统会先处理OnProgress消息响应,然后再创建窗体,出现仍看不见进度栏的情况。必须用PostMessage先把消息放入消息响应队列当中,等窗体创建完毕后,系统再处理OnProgress函数。*/
4.4最后写OnProgress函数
void CMainFrame::OnProgress()
{
CRect rect;
m_wndStatusBar.GetItemRect(m_wndStatusBar.CommandToIndex(IDS_PROGRESS),&rect);
m_progress.Create(WS_CHILD|WS_VISIBLE, rect, &m_wndStatusBar, 123);
m_progress.SetPos(50);
}
这时在IDS_PROGRESS状态栏中就能看到进度栏了。但是还存在问题,当改变窗口大小时,进度栏会离开IDS_PROGRESS栏位位置。
5、另一种简单的方法可以实现以上相同的功能,并且可以解决以上的问题,是利用windows窗体发生重绘时的消息响应函数。在MainFrame中右键增加一个windows的WM_PAINT消息响应函数,然后加入一下代码:
CRect rect;
m_wndStatusBar.GetItemRect(m_wndStatusBar.CommandToIndex(IDS_PROGRESS),&rect);
if (!m_progress.m_hWnd) /*注意这里必须增加一个判断,否则在窗体再次发生重绘时,
由于已经存在m_progress实例,会出现一个错误。*/
{
m_progress.Create(WS_CHILD|WS_VISIBLE|PBS_SMOOTH , rect, &m_wndStatusBar, 123);
}
else
{
m_progress.MoveWindow(rect); /*移动窗口,比SetWindowPos简单。*/
}
m_progress.SetPos(50);
6、现在我们要让状态栏上显示鼠标在窗口中的坐标。由于View窗体在MainFrame框架前,所以我们要响应的是View窗体上OnMouseMove事件。
首先我们在View类中右键新建一个window消息响应函数,添加一个WM_MOUSEMOVE的消息响应函数。添加如下代码:
CString str;
str.Format("x=%d,y=%d",point.x,point.y); /*对鼠标坐标进行格式化*/
((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str); /*由于作用的是View窗口的父窗口MainFrame下的m_wndStatusBar对象,所以必须将m_wndStatusBar设置为public,然后用(CMainFrame*)指名其为框架类指针,否则->后面不会出现m_wndStatusBar对象。注意在View程序中必须包含MainFrame的头文件*/
也可以将最有一句改为: ((CMainFrame*)GetParent())->SetMessageText(str);直接由框架类函数中的SetMessageText实现对状态栏文字的更改。
第三种实现方法是: ((CMainFrame*)GetParent())->GetMessageBar()->SetWindowText(str);通过框架类函数GetMessageBar来获得指向状态栏窗口的指针。这种方法的好处是,不需要再把MainFrame中的m_wndStatusBar设置为public。
第四种方法: GetParent()->GetDescendantWindow(AFX_IDW_STATUS_BAR)->SetWindowText(str); 因为GetDescendantWindow本来就是CWnd的成员函数,所以不需要转换为框架类对象,并根据ID号,来获得子孙窗口的对象指针,这里的AFX_IDW_STATUS_BAR,是由CStatusBar::Create默认产生的。