注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

jasonyang9的博客

随便写写

 
 
 

日志

 
 

(怀旧系列)VC程序设计(孙鑫老师)听课笔记:09 外观、工具栏、状态栏  

2013-01-27 11:53:39|  分类: programming |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
(怀旧系列)VC程序设计(孙鑫老师)听课笔记:09 外观、工具栏、状态栏 - jasonyang9 - jasonyang9的博客
 
====================
外观、工具栏、状态栏
====================

外观
====

新建单文档工程。
一般来说,要修改窗口外观和大小,应该在窗口创建之前进行。
修改CMainFrame::PreCreateWindow函数(一个虚函数),如果派生类中有,则会调用派生类的函数。

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) // 传入一个CREATESTRUCT类型的参数,注意声明为引用类型,函数中对其的修改结果会返回给调用者(MFC底层代码,创建窗口时会反应出修改)
{
if (!CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: Modify the Window class or styles here by modifying // 这个CREATESTRUCT结构体成员变量类型、个数和API函数CreateWindowEx的参数完全一致(只是顺序正好相反)
// the CREATESTRUCT cs

cs.cx = 300; // 修改窗口宽度为300
cs.cy = 200; // 修改高度为200
// 对于SDI(单文档界面程序)来说,默认的窗口类型(Style,样式)为WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,FWS_ADDTOTITLE表示让框架将文档标题增加到窗口标题前(或后?如WORD标题),所以要修改窗口标题时,必须取消FWS_ADDTOTITLE这个类型,不让框架自作主张
cs.style &= ~FWS_ADDTOTITLE; // 用取反后与上某个类型(Style)来取消它(由于cs.style原来等于WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,所以这里改为cs.style = WS_OVERLAPPEDWINDOW亦可)
cs.lpszName = "http://www.sunxin.org"; // 修改窗口标题

return TRUE;
}


在窗口创建完成之后修改窗口外观,利用API函数SetWindowLong可以实现。

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
...
SetWindowLong(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW); // 用SetWindowLong直接设置窗口类型
SetWindowLong(m_hWnd, GWL_STYLE, GetWindowLong(m_hWnd, GWL_STYLE) & ~WS_MAXIMIZEBOX); // 用GetWindowLong获取现有窗口类型,然后去掉WS_MAXIMIZEBOX

return 0;
}


窗口的图标、光标和大小等是在设计窗口类的时候指定的,窗口类的设计和注册是MFC底层代码完成的,不可能直接修改MFC底层代码,但可以通过自己编写窗口类来实现定制。

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CFrameWnd::PreCreateWindow(cs))
return FALSE;

WNDCLASS wndcls;
wndcls.cbClsExtra = 0;
wndcls.cbWndExtra = 0;
wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); // 黑色画刷(GetStockObject返回类型为GDIOBJECT,要做强制类型转换)
wndcls.hCursor = LoadCursor(NULL, IDC_HELP); // 帮助式的光标
wndcls.hIcon = LoadIcon(NULL, IDI_ERROR); // 错误图标
wndcls.hInstance = AfxGetInstanceHandle(); // 用框架函数获取当前应用程序实例句柄
wndcls.lpfnWndProc = ::DefWindowProc; // 窗口过程为API窗口过程
wndcls.lpszClassName = "sunxin.org"; // 类名
wndcls.lpszMenuName = NULL; // MFC程序的菜单并不是在这里创建的,而是在构造单文档模板时传递了资源标识
wndcls.style = CS_HREDRAW | CS_VREDRAW; // 窗口类的类型(不是窗口类型)

RegisterClass(&wndcls); // 注册窗口类

cs.lpszClass = "sunxin.org"; // 将CREATESTRUCT结构体类名改为自定义类的类名

return TRUE;
}


注意:在框架窗口中自定义窗口类只能改变框架窗口的类型,SDI程序有一个View类窗口覆盖在框架窗口之上,所以除了图标能够看到修改效果之外,背景和光标都无法改变,要修改View类窗口类才能看到效果。

BOOL CXXXView::PreCreateWindow(CREATESTRUCT& cs)
{
cs.lpszClass = "sunxin.org"; // 将CREATESTRUCT结构体类名改为自定义类的类名(窗口类已经注册)
return CView::PreCreateWindow(cs);
}


用AfxRegisterWndClass来修改,可避免重写整个窗口类(貌似这个函数就是将注册窗口类的过程简化了,可以设定的只有窗口类的类型、光标、背景画刷和图标,返回一个已注册的窗口类的名字,用于给CREATESTRUCT结构变量赋值cs.lpszClass):

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CFrameWnd::PreCreateWindow(cs))
return FALSE;

cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW, // 可以设置窗口类的类型(CS_),返回值为窗口类的类名,直接赋给cs.lpszClass
0, // 设置光标(为0,表示默认)
0, // 设置背景画刷(为0,表示默认)
LoadIcon(NULL, IDC_WARNING)); // 设置图标(为标准图标IDC_WARNING)
return TRUE;
}

BOOL CXXXView::PreCreateWindow(CREATESTRUCT& cs)
{
cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW, // 在View类中同样调用AfxRegisterWndClass
LoadCursor(NULL, IDC_CROSS), // 设置光标(为IDC_CROSS)
(HBRUSH)GetStockObject(BLACK_BRUSH), // 设置背景画刷(为BLACK_BRUSH)
0); // 设置图标(为0,View类没有标题栏,不存在图标)
return CView::PreCreateWindow(cs);
}


如果只给AfxRegisterWndClass第一个参数:

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CFrameWnd::PreCreateWindow(cs))
return FALSE;

cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW);
return TRUE;
}

BOOL CXXXView::PreCreateWindow(CREATESTRUCT& cs)
{
cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW);
return CView::PreCreateWindow(cs);
}


图标变成默认的Windows旗帜(Windows Logo icon),光标是默认的白色箭头(IDC_ARROW),背景画刷是透明画刷(NULL,即不擦除背景)。

在窗口创建完成之后修改窗口外观,利用API函数SetClassLong也可以实现。

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
...
SetClassLong(m_hWnd, CGL_HICON, (LONG)LoadIcon(NULL, IDI_ERROR)); // 修改窗口图标

return 0;
}

// 对View类增加WM_CREATE消息处理函数OnCreate
int CXXXView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;

SetClassLong(m_hWnd, CGL_HBRBACKGROUND, (LONG)GetStockObject(BLACK_BRUSH)); // 修改背景画刷
SetClassLong(m_hWnd, CGL_HCURSOR, (LONG)LoadCursor(NULL, IDC_HELP)); // 修改光标

return 0;
}


制作图标不断变化的应用程序:

// 新建或导入图标资源(IDI_ICON1、IDI_ICON2、IDI_ICON3),在CMainFrame中增加一个HICON类型的成员数组变量m_hIcons[3],在OnCreate函数中加载图标

extern CXXXApp theApp; // 声明外部变量(SDI程序的唯一一个APP实例,代表了应用程序本身,声明后可以引用)
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
...
m_hIcon[0] = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_ICON1)); // 用LoadIcon加载图标(需要注意:这里的第一个参数不再是NULL而是用AfxGetInstanceHandle获取当前进程的句柄,第二个参数也不再是资源标识,而是用MAKEINTRESOURCE宏将资源标识转换为字符指针)
m_hIcon[1] = LoadIcon(theApp.m_hInstance, MAKEINTRESOURCE(IDI_ICON2)); // 用先前声明的theApp变量来获取应用程序进程的句柄
m_hIcon[2] = LoadIcon(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(IDI_ICON3)); // 用框架函数AfxGetApp获取CWinApp的指针来获取应用程序进程的句柄

SetClassLong(m_hWnd, CGL_HICON, (LONG)m_hIcons[0]); // 直接设置第一个图标(否则初始还是会显示MFC默认图标)

SetTimer(1, 1000, NULL); // 设置一个定时器,每隔1000毫秒触发一次(标识为1)
return 0;
}

// 增加WM_TIMER消息处理

void CMainFrame::OnTimer(UINT nIDEvent)
{
static int index = 1; // 设定一个计数器(必须为静态(存放在数据区中,而不是栈中,当第一次调用时,在数据区分配空间,初始化为0,之后再进入此函数就不会再次分配空间)或成员变量)

SetClassLong(m_hWnd, CGL_HICON, (LONG)m_hIcons[index]); // 改变图标

index = ++index % 3; // 用取模的方式来让一个变量在0和某个整数之间循环变化(也可以用常规的if语句来判断等实现)

CFrameWnd::OnTimer(nIDEvent);
}



工具栏
======

工具栏将常用的(菜单)命令以按钮形式提供给用户,方便操作。
用资源编辑器在SDI程序自带的Toolbar\IDR_MAINFRAME增加一个按钮,在Menu\IDR_MAINFRAME中增加一个菜单项(IDM_TEST,Test),增加菜单项的命令处理函数:
(工具栏资源的ID号和菜单资源是一样的,同为IDR_MAINFRAME)

void CMainFrame::OnTest()
{
MessageBox("test");
}


回到资源编辑器,双击新增加的按钮,修改其ID为IDM_TEST(即菜单项的ID号),这样该按钮和菜单项都能发送IDM_TEST消息,执行相同的OnTest函数。
按钮之间的竖线为分隔符,在资源编辑器中向右拖动按钮可增加分隔符,向左拖动可删除分隔符。将按钮拖出工具栏可删除该按钮。

CToolBar派生于CControlBar,CControlBar派生于CWnd,所以工具栏是一个窗口。
创建工具栏的步骤(第一种):1)创建工具栏资源;2)构建CToolBar对象;3)调用Create或CreateEx函数创建Windows工具栏并和CToolBar对象关联;4)调用LoadToolBar加载工具栏资源。
创建工具栏的步骤(第二种):1)构建CToolBar对象;2)调用Create或CreateEx函数创建Windows工具栏并和CToolBar对象关联;3)调用LoadBitmap加载包含工具栏按钮的图像;4)调用SetButtons设置按钮的样式,关联按钮和每个位图上的图标。

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;

if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMING) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar\n");
return -1;
}

...

m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); // 让工具栏允许停靠在任意位置
EnableDocking(CBRS_ALIGN_ANY); // 这是CMainFrame调用的FrameWnd::EnableDocking,表示可以被停靠在任何位置
DockControlBar(&m_wndToolBar); // 让工具栏停靠

...
}


新建一个工具栏
--------------

1、用资源编辑器插入一个新的工具栏(IDR_TOOLBAR1),增加3个按钮。
2、在MainFrame.h中增加成员变量。

class CMainFrame : public CFrameWnd
{
...

protected:
...
CToolBar m_newToolbar;
...
};


3、在OnCreate函数中调用CreateEx函数创建Windows工具栏并和CToolBar对象关联。
4、调用LoadToolBar加载工具栏资源。

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
...

if (!m_newToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_RIGHT
| CBRS_GRIPPER | CBRS_TOOLTOPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_newToolBar.LoadToolBar(IDR_TOOLBAR1))
{
TRACE0("Failed to create toolbar\n");
return -1;
}
m_newToolBar.EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_newToolBar);

...
}


在查看菜单中增加菜单项,用于显示和隐藏工具栏
--------------------------------------------

用CToolBar的ShowWindow成员函数(由于CToolBar派生于CControlBar,CControlBar派生于CWnd,所以也能够使用ShowWindow)

1、在查看菜单中增加菜单项(IDM_VIEW_NEWTOOL,“新的工具栏”)。
2、增加命令响应。

void CMainFrame::OnViewNewtool()
{
if (m_newToolBar.IsWindowVisible()) // 如果窗口可视
{
m_newToolBar.ShowWindow(SW_HIDE); // 隐藏工具栏

}
else
{
m_newToolBar.ShowWindow(SW_SHOW); // 显示工具栏
}

RecalcLayout(); // 由于工具栏被显示或隐藏后,其他工具栏的停靠位置可能有所变化,需要重新调整布局(如果忘记调用RecalcLayout,工具栏的显示不正常:按钮不显示)
DockControlBar(&m_newToolBar); // 停靠工具栏(如果不停靠则当显示一个浮动工具栏后再次显示它会出现问题,但这并不是完美的解决方案,如何让浮动工具栏能够以浮动模式重新显示需要自己思考)
}

用CFrameWnd::ShowControlBar函数重写OnViewNewtool:

void CMainFrame::OnViewNewtool()
{
ShowControlBar(&m_newToolBar, !m_newToolBar.IsWindowVisible(), FALSE); // 切换m_newToolBar显示状态
}

增加IDM_VIEW_NEWTOOL的UPDATE_COMMAND_UI命令响应,同步菜单项的复选标记:

void CMainFrame::OnUpdateViewNewtool(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(m_newToolBar.IsWindowVisible()); // 直接根据m_newToolBar的IsWindowVisible函数获取工具栏的可视状态来设置复选标记
}


状态栏
======

标准状态栏,最左侧的是“提示行”,右侧以窗格形式出现的是各种“状态指示器”,如大小写指示器等。

在CMainFrame中定义了CStatusBar m_wndStatusBar成员变量。

class CMainFrame : public CFrameWnd
{
...
protected:
CStatusBar m_wndStatusBar;
...
};


在CMainFrame的OnCreate函数中创建状态栏并设置指示器

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
...
if (!m_wndStatusBar.Create(this) || // 创建状态栏
!m_wndStatusBar.SetIndicators(indicators, // 设置指示器
sizeof(indicators)/sizeof(UINT))) // 指示器个数(用2个sizeof相除得到)
{
TRACE0("Failed to create status bar\n");
return -1;
}
...
}

指示器的具体配置同样在CMainFrame.cpp文件中。

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

这些ID号在String Table(字符串表)资源中,可以自行增加字符串资源,例如:增加IDS_TIMER(时钟)和IDS_PROGRESS(进度栏),并增加它们到指示器数组中。

static UINT indicators[] =
{
ID_SEPARATOR, // status line indicator
IDS_TIMER, // 时钟
IDS_PROGRESS, // 进度栏
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
};

在时钟指示器中显示系统时间
--------------------------

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
...
CTime t = CTime::GetCurrentTime(); // 定义一个CTime对象,然后用CTime的静态函数GetCurrentTime获取时间
CString str = t.Format("%H:%M:%S"); // 用"%H:%M:%S"格式化时间(小时:分钟:秒)
m_wndStatusBar.SetPaneText(1, str, TRUE); // 用CStatusBar的成员函数SetPaneText来设置文本(第一个参数是指示器的索引号,第二个参数是文本,第三个参数设为TRUE表示重绘)
return 0;
}


如果不知道指示器的索引号,可用CommandToIndex来根据ID号获取:

m_wndStatusBar.SetPaneText(m_wndStatusBar.CommandToIndex(IDS_TIMER), str, TRUE);


如果内容超出指示器显示宽度,用SetPaneInfo设置:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
...
CTime t = CTime::GetCurrentTime();
CString str = t.Format("%H:%M:%S");

CClientDC dc(this);
CSize sz = dc.GetTextExtent(str);
m_wndStatusBar.SetPaneInfo(m_wndStatusBar.CommandToIndex(IDS_TIMER), IDS_TIMER, SBPS_NORMAL, sz.cx);

m_wndStatusBar.SetPaneText(m_wndStatusBar.CommandToIndex(IDS_TIMER), str, TRUE);
return 0;
}

如果要每秒自动更新时钟,利用计时器。

void CMainFrame::OnTimer(UINT nIDEvent)
{
...
CTime t = CTime::GetCurrentTime();
CString str = t.Format("%H:%M:%S");

CClientDC dc(this);
CSize sz = dc.GetTextExtent(str);
m_wndStatusBar.SetPaneInfo(m_wndStatusBar.CommandToIndex(IDS_TIMER), IDS_TIMER, SBPS_NORMAL, sz.cx);

m_wndStatusBar.SetPaneText(m_wndStatusBar.CommandToIndex(IDS_TIMER), str, TRUE);
...
}


在进度栏中显示进度
------------------

MFC提供了CProgressCtrl,在CMainFrame头文件中构造对象:

class CMainFrame : public CFrameWnd
{
...
protected:
CProgressCtrl m_progress;
...
};

在OnCreate函数中创建进度栏:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
...
CRect rect;
m_wndStatusBar.GetItemRect(2, &rect); // 获取进度栏指示器的矩形区域
m_progress.Create(WS_CHILD | WS_VISIBLE, rect, &m_wndStatusBar, 123); // 创建进度栏(注意:将父窗口设置为m_wndStatusBar,而不是this)
return 0;
}


但以上这种做法是不能够获取有效矩形区域的,那么创建进度栏也会失败,因为m_wndStatusBar在OnCreate中初始化工作还没有完成。应等到OnCreate函数调用完成后再获取状态栏上指示器的矩形区域。
在OnCreate中发送自定义消息,在该消息的响应函数中获取状态栏上指示器的矩形区域,因为消息是放到消息队列中,等到WM_CREATE消息(也就是调用OnCreate函数的消息)响应完成之后,从消息队列中取出该自定义消息,调用响应函数,也就实现了在OnCreate后再调用函数的效果。
在CMainFrame头文件中自定义消息:

#define UM_PROGRESS WM_USER + 1 // 自定义消息应大于WM_USER,避免和系统消息冲突


在CMainFrame头文件中声明消息响应函数:

class CMainFrame : public CFrameWnd
{
...
protected:
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
...
//}}AFX_MSG
afx_msg void OnProgress(); // 声明消息响应函数
DECLARE_MESSAGE_MAP()
...
};


在CMainFrame源文件中增加消息映射:

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
...
//}}AFX_MSG_MAP
ON_MESSAGE(UM_PROGRESS, OnProgress) // 映射消息
END_MESSAGE_MAP()


在CMainFrame源文件中增加消息响应函数的实现:

void CMainFrame::OnProgress()
{
CRect rect;
m_wndStatusBar.GetItemRect(2, &rect);

m_progress.Create(WS_CHILD | WS_VISIBLE, rect, &m_wndStatusBar, 123);
}


不要忘记在OnCreate中发送消息:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
...
PostMessage(UM_PROGRESS); // 注意:不能用SendMessage,因为SendMessage会立即调用消息响应函数,执行后再返回(而不是将消息发送到消息队列),起不到效果
return 0;
}


程序至此还有缺陷,即当用户调整窗口大小后,进度栏的位置会偏离状态栏指示器的矩形区域。当窗口尺寸发生变化时,系统产生WM_PAINT消息,那么,在处理这条消息时获取新的状态栏指示器矩形区域,调整进度栏的位置就能够让进度栏始终保持正确的位置。
在CMainFrame中增加WM_PAINT消息处理,同时可以去掉OnCreate中的PostMessage(UM_PROGRESS),因为当程序第一次重绘时就可以借由WM_PAINT消息响应函数(OnPaint)来创建进度栏。

void CMainFrame::OnPaint()
{
CPaintDC dc(this);

CRect rect;
m_wndStatusBar.GetItemRect(2, &rect);

if (!m_progress.m_hWnd) // 这里必须加上判断进度栏是否已创建的逻辑,因为在每次窗口需要重绘时都产生WM_PAINT消息,该OnPaint函数会被调用很多次,重复创建窗口对象会发生错误
{
m_progress.Create(WS_CHILD | WS_VISIBLE | PBS_SMOOTH, // 加上PBS_SMOOTH可以产生一个平滑显示的进度栏
rect, &m_wndStatusBar, 123);
}
else
{
m_progress.MoveWindow(rect); // 如果已经创建了进度栏对象,则不能重复创建,而是移动其位置即可
}

m_progress.SetPos(50); // 设置进度为50(百分之)
}


让进度栏不断前进:

void CMainFrame::OnTimer(UINT nIDEvent)
{
...
m_progress.StepIt(); // 调用成员函数StepIt来产生步进效果(当超过100%后自动复位)
...
}

将鼠标当前位置显示在状态栏上
----------------------------
由于View窗口始终覆盖在框架窗口之上,因此需要在View窗口中捕获WM_MOUSEMOVE消息:

#include "MainFrm.h"

void CXXXView::OnMouseMove(UINT nflags, CPoint point)
{
CString str;
str.Format("x=%d,y=%d", point.x, point.y);
((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str); // 注意1:框架类是View类的父窗口,但GetParent返回CWnd*类型,需要强制转换;注意2:由于CMainFrame中的m_wndStatusBar是一个protected变量,不能直接从外部访问,除非改为public;注意3:必须包含MainFrm.h来声明CMainFrame类

CView::OnMouseMove(nflags, point);
}


用CFrameWnd::SetMessageText重写(SetMessageText能将字符串内容显示在状态栏的ID为0的面板上):

void CXXXView::OnMouseMove(UINT nflags, CPoint point)
{
CString str;
str.Format("x=%d,y=%d", point.x, point.y);
((CMainFrame*)GetParent())->SetMessageText(str);

CView::OnMouseMove(nflags, point);
}


用CFrameWnd::GetMessageBar重写(GetMessageBar返回指向状态栏窗口的指针,有了它就不需要将m_wndStatusBar从protected改为public了):

void CXXXView::OnMouseMove(UINT nflags, CPoint point)
{
CString str;
str.Format("x=%d,y=%d", point.x, point.y);
((CMainFrame*)GetParent())->GetMessageBar()->SetWindowText(str);

CView::OnMouseMove(nflags, point);
}


用CWnd::GetDescendantWindow重写(GetDescendantWindow通过一个ID号返回一个CWnd*指针):

void CXXXView::OnMouseMove(UINT nflags, CPoint point)
{
CString str;
str.Format("x=%d,y=%d", point.x, point.y);
GetParent()->GetDescendantWindow(AFX_IDW_STATUS_BAR)->SetWindowText(str); // 这里不再需要强制类型转换,因为GetDescendantWindow本来就是CWnd成员函数,GetParent返回CWnd*指针

CView::OnMouseMove(nflags, point);
}

AFX_IDW_STATUS_BAR是MFC默认的状态栏ID号,此外,默认工具栏的ID为AFX_IDM_TOOLBAR,其他一些默认ID号可通过右键点击AFX_IDM_STATUS_BAR等宏定义,选择Goto definition菜单项来查找。
关于GetDescendantWindow的第二个参数(bOnlyPerm),如果为TRUE,表示只能返回一个持久(permanent)的窗口,如果为FALSE,表示能够返回一个临时(temporary)的窗口。关于临时的窗口,参考Technical Note 3(TN003: Mapping of Windows Handles to Objects)。

为应用程序增加启动画面
======================
调用Project→Add To Project→Componment and Controls,增加组件Splash screen;可设置类名(CSplashWnd)和位图资源ID(IDB_SPLASH),重新编译后就有了启动画面;可以自定义位图,设置显示的时间(OnCreate中的SetTimer,默认为750毫秒);在CMainFrame的OnCreate中,插入了CSplashWnd::ShowSplashScreen(this);来显示启动画面。

  评论这张
 
阅读(222)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017