1、在窗口建立之前改变窗口外观,第三课讲mfc运行机制的时候说过这个函数了,留了一个后门给我们
BOOL CMainFrame::PreCreateWindow(GREATESTRUCT& cs)
{
if(!CFrameWnd::PreCreateWindow(cs))
return FALSE;
改变cs结构体
return TRUE;
}
改宽高,可以直接cs.x=*,cs.y=*
改窗口标题,必须先改窗口的类型,因为原来默认FWS_ADDTOTITLE | WS_OVERLAPPEDWINDOW
FWS_ADDTOTITLE 默认使用文档标题,去掉这个标志就可以了
//cs.style&=~FWS_ADDTOTITLE;
cs.style=WS_OVERLAPPEDWINDOW;
cs.lpszName="http://www.sunxin.org";
1、在窗口建立之后改变窗口外观,在OnCreate()用SetWindowLong()
LONG SetWindowLong(HWND hWnd,int nIndex,LONG dwNewLong); //sdk函数,改变指定窗口的属性
要改变的窗口的句柄 你要改变的属性 新的值
改变属性用GWL_STYLE指定改变的是窗口类型
获取窗口信息
LONG SetWindowLong(HWND hWnd,int nIndex); //哪个窗口,什么类型
//SetWindowLong(m_hWnd,GWL_STYLE,WS_OVERLAPPEDWINDOW);
SetWindowLong(m_hWnd,GWL_STYLE,GetWindowLong(m_hWnd,GWL_STYLE) & ~WS_MAXIMIZEBOX);
2、改变图标,光标,背景,应该在PreCreateWindow()中编写
获取当前应用程序的句柄
HINSTANCE AfxGetInstanceHandle();
通过重写窗口类,达到修改程序外观的目的
WNDCLASS wndcls; //重写窗口类
wndcls.cbClsExtra=0;
wndcls.cbWndExtra=0;
wndcls.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);
wndcls.hCursor=LoadCursor(NULL,IDC_HELP);
wndcls.hIcon=LoadIcon(NULL,IDI_ERROR);
wndcls.hInstance=AfxGetInstanceHandle();
wndcls.lpfnWndProc=::DefWindowProc; //cwnd类中有一个同名的函数,所以要用::区分
wndcls.lpszClassName="sunxin.org"; //窗口类名
wndcls.lpszMenuName=NULL;
wndcls.style=CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wndcls); //注册窗口类
cs.lpszClass="sunxin.org"; //把新的窗口类送给PreCreateWindow()中传入的参数cs
修改窗口图标,应该在MainFrame类中修改,因为图标是属于框架类的
修改光标,背景应该在View类中修改,因为那是view类窗口上的,把最后一句移到view同函数中即可
3、上面的方法太麻烦了,用另一个函数,
LPCTSTR AFXAPI AfxRegisterWndClass(UINT nClassStyle,HCURSOR hCursor=0,HBRUSH hbrBackground=0,HICON hIcon=0);//窗口类型,光标,画刷,图标,返回新类的类名
缺省参数,箭头光标,空画刷,windows标志图标
可以这样写,达到上面一样的目的
cs.lpszClass=AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,0,0,LoadIcon(NULL,IDI_WARNING));
4、窗口建立后改变外观
DWORD SetClassLong(HWND hWnd,int nIndex,LONG dwNewLong); //改变WNDCLASSEX结构体的内容
哪个窗口 改变类型 新类型
改变图标,可以用GCL_HICON,改变背景可以用GCL_HBRBACKCROUND
SetClassLong(m_hWnd,GCL_HICON,(LONG)LoadIcon(NULL,IDI_ERROR));
把资源id号改变为LPCTSTR的一个宏
LPCTSTR MAKEINTRESOURCE(WORD wInteger);
m_hIcons[0]=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_ICON1));
CTestApp的父类CWinApp有一个数据成员m_hInstance表示的当前的程序实例
m_hIcons[1]=LoadIcon(theApp.m_hInstance,MAKEINTRESOURCE(IDI_ICON2));
一个类内需要调用另一个类的成员对象,需要用extern声明
extern CStyleApp theApp; //声明在外部源文件当中定义的
则,theApp.m_hInstance和AfxGetInstanceHandle()的效果是一样的
m_hIcons[1]=LoadIcon(theApp.m_hInstance,MAKEINTRESOURCE(IDI_ICON2));
CWinApp* AfxGetApp(); //返回一个CWinApp的指针
m_hIcons[2]=LoadIcon(AfxGetApp()->m_hInstance,MAKEINTRESOURCE(IDI_ICON3));
让一个值在某一个范围内变化,可以用取模的方法
index=++intdex%3
5、工具栏
工具图标的id设成和某菜单项的一样,然后对菜单项命令消息编程就行了
把工具栏图标往右拖动一点距离,就能产生分隔符了
删除工具栏按钮,拖出工具栏就行了,用del键只能删除其上图象
6、创建工具栏
CToolBar : CControlBar : CWnd : CCmdTarget : CObject
第一种方法:
1 建立工具栏资源
2 创建CToolBar对象
3 用Create()或者CreateEx()建立一个工具栏并与工具栏对象相关联
4 用LoadToolBAr() 载入工具栏
CToolBar::Create
BOOL Create(CWnd* pParentWnd,DWORD dwStyle=WS_CHILD | WS_VISIBLE | CBRS_TOP,UINT nID=AFX_IDW_TOOLBAR); //父窗口指针,工具栏样式,工具栏id
CToolBar::CreateEx
BOOL CreateEx(CWnd* pParentWnd,DWORD dwCtrlStyle=TBSTYLE_FLAT,DWORD dwStyle=WS_CHILD|WS_VISIBLE|CBRS_ALIGH_TOP,CRect rcBorders+CRect(0,0,0,0),UINT nID=AFX_IDW_TOOLBAR); //父窗口指针,扩展风格,工具栏样式,工具栏宽度,工具栏id
第二种方法:
1 创建一个CToolBar对象
2 调用Create()或者CreateEx()建立一个工具栏并与工具栏对象相关联
3 调用LoadBitmap()载入工具栏图象
4 调用SetButtons()设置工具栏样式并与图标关联
让工具栏可以停靠
CControlBar::EnableDocking
void EnableDocking(DWORD dwStyle);
CBRS_ALIGN_TOP 顶部
CBRS_ALIGN_BOTTON 底部
让框架窗口可以被停靠
CFrameWnd::EnableDocking
void EnableDocking(DWORD dwDockStyle); //MainFrame从CFrameWnd继承,所以此函数可以被直接调用
CBRS_ALIGN_TOP 顶部
CBRS_ALIGN_BOTTON 底部
让工具栏停靠在主窗口下
DockControlBar(CToolBar* hhh);
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
}
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
7、判断一个窗口是否是可视的
判断工具栏是否可视:m_newToolBar.IsWindowVisible(); //可视返回真,否则返回假
如果工具栏处在停靠状态,调用IsWindowVisible()只会让工具栏上的按钮消失,工具栏还在
调用下面的函数可以解决该问题
CFrameWnd::RecalcLayout
virtual void RecalcLayout(BOOL bNotify=TRUE); //重新调整工具栏位置
当工具栏处于浮动状态,调用以上2个函数后,只是按钮消失了,工具栏还在
要解决该问题,还必须调用DockControlBar(&m_wndToolBar);
这时,工具栏隐藏没问题,但恢复时会使工具栏出现在顶端,而不是在原来的位置浮动显示
第一种显示方法示例
void CMainFrame::OnViewNewtool()
{
if(m_newToolBar.IsWindowVisible())
{
m_newToolBar.ShowWindow(SW_HIDE);
}
else
{
m_newToolBar.ShowWindow(SW_SHOW);
}
RecalcLayout();
DockControlBar(&m_newToolBar);
}
8、第二种显示方法
CFrameWnd::ShowControlBar
void ShowControlBar(CControlBar* pBar,BOOL bShow,Bool bDelay);
工具栏指针 真显假隐 显示延时否,真延时,假立即显示
一个函数完成以上功能
ShowControlBar(&m_newToolBar,!m_newToolBar.IsWindowVisible(),FALSE);
9、为菜单项加上复选标记
添加菜单项UPDATE_COMMAND_UI响应消息及函数
void CMainFrame::OnUpdateViewNewtool(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(m_newToolBar.IsWindowvisible()); //用SetCheck()设置
}
10、状态栏
CMainFrame.h中
CStatusBar m_wndStatusBar;
CMainFrame.cpp中
static UINT indicators[] = //内容的个数就是状态栏里的项目数
{
ID_SEPARATOR, // status line indicator
ID_INDICATOR_CAPS, //在资源里的string tool定义
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
};
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
}
如果想要在状态栏中添加自己的项目,资源里的string table定义自己的常量
11、在状态栏中添加时钟
获取当前时间
CTime::GetCurrentTime
static CTime PASCAL GetCurrentTime(); //返回一个CTime对象,他表示了当前的时间
格式化时间对象
CTime::Format
CString Format(LPCTSTR pFormat)const; //格式化一个时间对象,返回为指定格式的字符串
CString Format(UINT nFormatID)const;
%D 天,%H 小时,%M 分钟,%S 秒,%%
把信息输出到状态栏
CStatusBar::SetPaneText
BOOL SetPaneText(int nIndex,LPCTSTR lpszNewText,BOOL bUpdate=TRUE);
状态栏哪个格的索引 要输出的内容 wm_paint时重绘
如果不知道你要输出的小格的索引,可以用下面的函数
CStatusBar::CommandToIndex
int CommandToIndex(UINT nIDFind)const; //通过资源字符串(就是那个静态数组)的id获取其索引
改变小格的大小,以适应输出的内容,默认小格是很小的
CStatusBar::SetPaneInfo
void SetPaneInfo(int nIndex,UINT nID,UINT nStyle,int cxWidth);
小格索引 重新分配新的id 类型 小格宽度
如何获取我们要显示的内容的宽度以赋值给cxWidth呢,用GetTextExtent();啊,前面说过了
SetTimer(1,1000,NULL);
CTime t=CTime::GetCurrentTime();
CString str=t.Format("%H:%M:%S");
CClientDC dc(this);
CSize sz=dc.GetTextExtent(str);
int index=0;
index=m_wndStatusBar.CommandToIndex(IDS_TIMER);
m_wndStatusBar.SetPaneInfo(index,IDS_TIMER,SBPS_NORMAL,sz.cx);
m_wndStatusBar.SetPaneText(index,str);
12、进度栏
CProgressCtrl::CProgressCtrl
CProgressCtrl(); //构造对象后用create()创建
CProgressCtrl::Create
BOOL Create(DWORD dwStyle,constRECT& rect,CWnd* pParentWnd,UINT nID);
类型 大小 父窗口 进度栏id号
类型有:PBS_VERTICAL 垂直(默认是水平的) PBS_SMOOTH 平滑
设置进度栏进到哪个位置
CProgressCtrl::SetPos
SetPos(这个参数填个整数,50代表50%,也就是一半,当百分比用了)
如何把进度条放到状态栏的小格当中呢
首先要获得小格的矩形区域
CStatusBar::GetItemRect
void GetItemRect(int nIndex,LPRECT lpRect)const; //小格索引,用来接收矩形区域坐标的参数
1 CProgressCtrl m_progress;
2
/* CRect rect;
m_wndStatusBar.GetItemRect(2,&rect);
//m_progress.Create(WS_CHILD | WS_VISIBLE | PBS_VERTICAL,
// CRect(100,100,120,200),this,123);
m_progress.Create(WS_CHILD | WS_VISIBLE,// | PBS_VERTICAL,
rect,&m_wndStatusBar,123);
m_progress.SetPos(50);*/
// SendMessage(UM_PROGRESS);
// PostMessage(UM_PROGRESS);
// CG: The following line was added by the Splash Screen component.
CSplashWnd::ShowSplashScreen(this);
13、自定义消息
1 在头文件中#define UM_PROGRESS WM_USER+1
消息都是一个整数值表示的
2 写上消息宏 afx_msg void OnProgress();
3 消息映射 ON_MESSAGE(UM_PROGESS,OnProgress)
4 写响应函数
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.SetPos(50);
}
5 在使用的地方发送消息 SendMessage(UM_PROGRESS);或者PostMessage(UM_PROGRESS);两者是有差别的,
SendMessage(UM_PROGRESS); //发送消息后直接到消息响应函数运行,结束后回到原函数直接运行
PostMessage(UM_PROGRESS); //将消息添加到消息对列当中后立即回到调用函数,系统GetMessage()到该消息后才执行消息响应函数,这样就能够让原函数执行完毕后再操作
在实例中,必须要让oncreate()执行完成,将窗口建立完毕后才能获取状态栏信息,所以用SendMessage(UM_PROGRESS);不合适,应该用PostMessage(UM_PROGRESS);
void CMainFrame::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
CRect rect;
m_wndStatusBar.GetItemRect(2,&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
}
14、让进度栏前进
设置进度栏前进步长
CProgressCtrl::SetStep
int SetStep(int nStep);
进度栏前进
StepIt();
设置进度栏前进范围
CProgressCtrl::SetRange
void SetRange(short nLower,short nUpper);
void SetRange32(int nLower,int nUpper); //默认0,100
m_progress.StepIt();
15、状态栏左条显示文本
直接用框架类的成员变量,但要把成员变量改成共有的,同时包含CMainFrame头文件
((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str);
用框架类的成员函数
CFrameWnd::SetMessageText
void SetMessageText(LPCTSTR lpszText); //直接在状态栏放置文本,不需要框架类对象了
((CMainFrame*)GetParent())->SetMessageText(str);
用GetMessageBar()
CFrameWnd::GetMessageBar
virtual CWnd* GetMessageBar(); //返回一个指向状态栏的指针
((CMainFrame*)GetParent())->GetMessageBar()->SetWindowText(str);
用GetDescendantWindow()
CWnd::GetDescendantWindow
CWnd* GetDescendantWindow(int nID,BOOL bOnlyPerm=FALSE) const; //搜索所有子窗口直到找到给定id的窗口,并返回该窗口的指针 窗口id,是否返回临时窗口,true返回持久窗口,false可以返回临时窗口
状态栏的id是多少呢?
CStatusBar::Create
BOOL Create(CWnd* pParentWnd,DWORD dwStyle=WS_CHILD|WS_VISIBLE|CBRS_BOTTOM,UINT nID=AFX_IDW_STATUS_BAR); //-------->最后这个就是系统给定的状态栏的id号
系统为每个常用的工具窗口都预设了一个id号,可以通过一个已知的id用查找定义的方法找到定义的地方,或到mfc/include/找到定义文件
GetParent()->GetDescendantWindow(AFX_IDW_STATUS_BAR)->SetWindowText(str);
//这里不用(CMainFrame*)强制转换窗口类型,要不要转换,要看你后面所用的函数是属于哪个类的,属于CMainFrame,就转换为CMainFrame,属于CWnd就转换为CWnd, 但这里用的是CWnd::GetParent(),所以明显不用转换了
void CStyleView::OnMouseMove(UINT nFlags, CPoint point)
{
CString str;
str.Format("x=%d,y=%d",point.x,point.y);
//((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str);
//((CMainFrame*)GetParent())->SetMessageText(str);
//((CMainFrame*)GetParent())->GetMessageBar()->SetWindowText(str);
GetParent()->GetDescendantWindow(AFX_IDW_STATUS_BAR)->SetWindowText(str);
CView::OnMouseMove(nFlags, point);
}
16、启动画面
1 工程-增加到工程-组件控件-选择splash screen-ok
这样就已经有了一个启动画面了,他增加的是一个位图IDB_SPLASH和一个类CSplashWnd
另外在框架类OnCreate()中添加了一句 CSplashWnd::ShowSplashScreen(this);
2 延长显示时间
在定时器中改变时间间隔 SetTimer(1,750,NULL); //