1.在窗口创建之前改变窗口的外观和大小:
如果我们要改变一个窗口的外观和大小,我们只需要去修改CREATESTRUCT这个结构体当中的成员变量值。
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
//先将窗口的大小修改一下:
cs.cx=300;
cs.cy=200;
//2.将窗口的标题修改一下
//取反之后相与
cs.style&=~FWS_ADDTOTITLE; //方法一
//跟上一条代码的一样:cs.style=cs.style & ~FWS_ADDTOTITLE
cs.style=WS_OVERLAPPEDWINDOW;//方法二
cs.lpszName="http://www.sunxin.org";
return TRUE;
}
2.我在窗口创建之后可不可以修改窗口的外观呢:
在CMainFrame::OnCreate中编写代码:
//在窗口创建之后去修改窗口的外观
//SetWindowLong可以改变一个指定窗口的属性
//在MFC当中所以在CMainFrame中创建的类都是窗口类,窗口中都有一个公有的成员变量
//保存了和窗口相关的句柄
// SetWindowLong(m_hWnd,GWL_STYLE,WS_OVERLAPPEDWINDOW);//运行之后文档的标题被去掉了
//将现有的类型和最大化框类型取反,这样实际上是去掉了现有类型当中的MAXIMIZEBOX这个类型
SetWindowLong(m_hWnd,GWL_STYLE,GetWindowLong(m_hWnd,GWL_STYLE) & ~WS_MAXIMIZEBOX);
//运行之后“最大化框变灰了”
如果我们再设置窗口的类型的时候,我们想要先获取到现有的窗口的一个类型,然后再现有的窗口的类型上去做一些修改。这个时候我们可以使用GetWindowLong,它可以获取一个指定窗口的信息。
(2)对于窗口的类型和大小是在创建窗口的时候去设定的,而图标、光标和背景是在设计窗口类的时候去指定的。窗口类的设计和注册时有MFC的底层代码自动帮助我们完成的,我们不可能也不应该去修改MFC的底层代码。
那么我们应该如何才能改变窗口的图标、光标、背景呢?
虽然说我们不能编写MFC的底层代码,但是,我们可以去编写自己的窗口类进行注册,让随后的窗口按照我们自己编写的窗口类去创建。
①关于AfxGetInstanceHandle:
凡是函数前面带有Afx,都是应用程序框架类函数,也就是全局的一个函数,我们在所以的类当中都可以直接去调用,这个函数就返回应用程序当前实例的一个句柄。
②对于我们这个单文档应用程序来说,我们这个应用程序有两个窗口,框架窗口包含了一个View类窗口,View类窗口时覆盖在框架窗口上,所以我们看到的背景实际上是View类窗口的背景。
也就是说我们在框架窗口当中,我们只能够去改变图标,我们要想改变应用程序,窗口的背景、光标,我们应该在View类窗口当中去改变。
下面我们可以再CMainFrame::PreCreateWindow当中去编写自己的窗口类:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
//1.先将窗口的大小修改一下:
// cs.cx=300;
// cs.cy=200;
/*
//2.将窗口的标题修改一下
//如何在一个现有的类型的基础上却掉一种类型,只需要对FWS_ADDTOTITLE取反
//然后将它跟我们现有的类型进行与操作
//取反之后相与
cs.style&=~FWS_ADDTOTITLE; //方法一
//跟上一条代码的一样:cs.style=cs.style & ~FWS_ADDTOTITLE
cs.style=WS_OVERLAPPEDWINDOW;//方法二
cs.lpszName="http://www.sunxin.org";
*/
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);
//hInstance应用程序的句柄,
//直接用AfxGetInstanceHandle这个函数获取当前应用程序的句柄
wndcls.hInstance=AfxGetInstanceHandle();
//窗口过程:直接用缺损的窗口过程
//如果我们想用全局API函数,需要加上两个冒号,也就是作用域标识符
wndcls.lpfnWndProc=::DefWindowProc;
wndcls.lpszClassName="sunxin.org";
wndcls.lpszMenuName=NULL;
//窗口类型,垂直重画和水平重画
wndcls.style=CS_HREDRAW | CS_VREDRAW;
//设计完这个窗口类之后,我们应该将这个窗口类注册一下,
//这样我们就有了一个新的窗口类:
RegisterClass(&wndcls);
//接下来我们就要让这个框架窗口的创建按照我们新设计的窗口去创建
//修改类名:
cs.lpszClass="sunxin.org";
//运行之后,只有窗口的图标改变了,箭头光标和背景都没有改变
//原因:对于我们这个单文档应用程序来说,我们这个应用程序有两个窗口,
//框架窗口包含了一个View类窗口,View类窗口时覆盖在框架窗口上,所以
//我们看到的背景实际上是View类窗口的背景。也就是说我们在框架窗口当中,
//我们只能够去改变图标,我们要想改变应用程序,窗口的背景、光标,我们
//应该在View类窗口当中去改变。
return TRUE;
}
BOOL CStyleView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
//这个窗口类我们已经注册了,所以我们可以直接引用这个类名:
cs.lpszClass="sunxin.org";
//运行之后,背景和光标都改过来了
return CView::PreCreateWindow(cs);
}
(3)在CMainFrame::PreCreateWindow中利用AfxRegisterWndClass来修改窗口的图标、光标、背景:
//使用AfxRegisterWndClass来改变窗口的图标、光标、背景是很方便的,
//我们不必重新设计一个窗口类了
cs.lpszClass=AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,0,0,
LoadIcon(NULL,IDI_WARNING));
(4)在CStyleView::PreCreateWindow中利用AfxRegisterWndClass来修改窗口的图标、光标、背景:
BOOL CStyleView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
cs.lpszClass=AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,
LoadCursor(NULL,IDC_CROSS),(HBRUSH)GetStockObject(BLACK_BRUSH),0);
return CView::PreCreateWindow(cs);
}
(5)在CMainFrame::PreCreateWindow和CStyleView::PreCreateWindow中编写代码:
BOOL CStyleView::PreCreateWindow(CREATESTRUCT& cs)
{
//在调用AfxRegisterWndClass的时候,将这些参数都设置成缺损值
//只用给第一个参数赋值,后面的都有缺损值
cs.lpszClass=AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW);
return CView::PreCreateWindow(cs);
}
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
//在调用AfxRegisterWndClass的时候,将这些参数都设置成缺损值
//只用给第一个参数赋值,后面的都有缺损值
cs.lpszClass=AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW);
return TRUE;
}
运行的结果是窗口的图标是一个“波浪状”,光标是“箭头状”,窗口时透明状:
(6)如果说我们在一个窗口已经创建之后,那么我们还能不能够去修改它的图标、光标和背景呢?
我们可以用一个全局的API函数SetClassLong:这个函数可以改变WNDCLASSEX当中的的成员变量的值,这个函数是我们之前所用的WNDCALSS的扩展和SetWindowLong很类似:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
SetClassLong(m_hWnd,GCL_HICON,(LONG)LoadIcon(NULL,IDI_ERROR));
//运行后改变了窗口图标
return 0;
}
(7)对于View类来说,它没有OnCreate函数,所以要增加一个OnCreate响应:
int CStyleView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
SetClassLong(m_hWnd,GCL_HBRBACKGROUND,(LONG)GetStockObject(BLACK_BRUSH));
SetClassLong(m_hWnd,GCL_HCURSOR,(LONG)LoadCursor(NULL,IDC_HELP));
return 0;
}
3.改变窗口的图标,三个图片轮换改:
extern CStyleApp theApp;
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
//加载图标:
m_hIcons[0]=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_ICON1));
//利用CStyle的全局对象theApp去调用它内部的数据成员m_hInstance
m_hIcons[1]=LoadIcon(theApp.m_hInstance,MAKEINTRESOURCE(IDI_ICON2));
//我们可以首先调用AfxGetApp,获取CWinApp的指针,然后再去调用它内部的数据成员,
//因为AfxGetApp这个函数式全局的函数,所以在所以的类当中都可以直接去调用。
m_hIcons[2]=LoadIcon(AfxGetApp()->m_hInstance,MAKEINTRESOURCE(IDI_ICON3));
//将第一幅图标设置为窗口的图标
SetClassLong(m_hWnd,GCL_HICON,(LONG)m_hIcons[0]);
//定时器:设置成每隔1000毫秒触发一次
SetTimer(1,1000,NULL);
return 0;
}
问题在index的变量:
index变量是一个局部的变量,也就是说,每次我们调用OnTimer这个函数的时候,index这个变量呢重新在这个栈上分配初始化为0,然后我们调用SetClassLong,始终显示的是第一幅图片。
解决这个问题的方法:
①让index这个变量的生命周期加长,我们可以将index定义为CMainFrame的成员变量,它的生命周期和CMainFrame的对象保持一致了;
②将index定义为一个全局的变量。如果我们就想在OnTimer这个函数的内部去定义index这个变量,那么我们可以将它声明为一个静态的,作为一个静态的成员变量,它是存放在数据区当中的,而不是分配在栈中的。当我们第一调用OnTimer这个函数的时候,那么给Index分配数据空间,初始化为0,那么以后再进入OnTimer这个函数的时候,index的空间已经有了,只是增加它的值。
void CMainFrame::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
//利用SetClassLong去改变窗口的图标
//将index设置为1,显示第二幅图表
static int index=1;
SetClassLong(m_hWnd,GCL_HICON,(LONG)m_hIcons[index]);
//要让一个值始终控制在一定的范围之内,最好的办法就是取模
//要想让一个值在一个范围内不断的变化,我们就可以模上这个范围
index=++index%3;//index在0~2之间变化
CFrameWnd::OnTimer(nIDEvent);
}
4.工具栏的编程:
(1)在工具栏上对按钮的操作:
通常设计完一个菜单之后,我们可以讲一些常用的菜单命令做成按钮摆放到工具栏上面,来方便用户的操作。
怎样将按钮与按钮之间加上一个分隔符呢?
我们只需要在这个按钮上面,按住鼠标左键,然后呢,向右拖动一下,再松开,就出现分隔符了。
怎样删除按钮呢?
可以按住鼠标左键,然后拖动到工具栏以外的空白处,这样就删除了按钮。
(2)去创建工具栏:
方法一:
创建一个工具栏的资源;
构造一个CToolBar的对象;
在CMainFrame中:
//构造一个CToolBar的对象
CToolBar m_newToolBar;
调用Create或者CreateEx(扩展函数),创建一个Windows的工具栏,然后将Windows的工具栏和CToolBar联系在一起;
调用一个LoadToolBar去加载一个toolbar的资源。
void CMainFrame::OnViewNewtool()
{
// TODO: Add your command handler code here
if(m_newToolBar.IsWindowVisible())
{
m_newToolBar.ShowWindow(SW_HIDE);
}
else
{
m_newToolBar.ShowWindow(SW_SHOW);
}
//做一个调整
RecalcLayout();
DockControlBar(&m_newToolBar);
//方法二:
ShowControlBar(&m_newToolBar,!m_newToolBar.IsWindowVisible(),FALSE);
}
//添加复选标记
void CMainFrame::OnUpdateViewNewtool(CCmdUI* pCmdUI)
{
// TODO: Add your command update UI handler code here
pCmdUI->SetCheck(m_newToolBar.IsWindowVisible());
}
方法二:
构造一个CToolBar的对象;
然后调用Create或者CreateEX函数创建Windows工具栏将它和CToolBar关联在一起;
调用LoadBitmap去加载一个包含了工具栏按钮图像的一个位图;
调用SetButtons设置这个按钮的样式,让它和位图里面的每一幅图像关联。
状态栏的编程
IDS_TIMER 61446 时钟
IDS_PROGRESS 61447 进度栏
在MainFrm.Cpp中添加代码:
Static UINT indicators[]=
{
ID_SEPARATOR,
IDS_TIMER,
IDS_PROGRESS,
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR SCRL,
}
5.要显示这个系统的时间,我们首先要获取这个系统的时间:
先要在状态栏的时钟的面板上面,窗格上面显示系统的时间,要想显示这个系统的时间,我们首先应该获取这个系统的时间,此时用一个类:CTime
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
//我们可以用CTime当中的静态方法得到当前的时间
//然后用CTime当中的Format方法将时间格式化,返回一个CString的对象
//先定义一个CTime的对象,它可以按照%H:%M:%S进行格式化,为当天的小时、分钟、秒
CTime t=CTime::GetCurrentTime();
CString str=t.Format("%H:%M:%S");
CClientDC dc(this);
//通过CSize对象我们就可以获取到字符串在屏幕所显示宽度和高度
//GetTextExtent用来得到字符串面板它显示的时候的一个宽度
CSize sz=dc.GetTextExtent(str);//加宽
//设置面板
int index=0;
index=m_wndStatusBar.CommandToIndex(IDS_TIMER);
//设置指示器面板的信息
m_wndStatusBar.SetPaneInfo(index,IDS_TIMER,SBPS_NORMAL,sz.cx);//也显示秒钟
//SetPaneText是CStatusBar的成员变量,所以用它的对象m_wndStatusBar去调用
m_wndStatusBar.SetPaneText(index,str);
//运行后:在时钟的指示器面板上,这个时间的秒钟也显示出来了
return 0;
}
//让时间动起来,有秒的变动
//这个定时器是每隔1000毫秒发送一个WTimer消息,我们就可以在响应
//WTimer消息响应函数当中再一次的去得到当前的时间,然后将它设置
//到我们时钟的指示器窗格当中。
void CMainFrame::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
//利用SetClassLong去改变窗口的图标
//将index设置为1,显示第二幅图表
static int index=1;
SetClassLong(m_hWnd,GCL_HICON,(LONG)m_hIcons[index]);
//要让一个值始终控制在一定的范围之内,最好的办法就是取模
//要想让一个值在一个范围内不断的变化,我们就可以模上这个范围
index=++index%3;//index在0~2之间变化
CTime t=CTime::GetCurrentTime();
CString str=t.Format("%H:%M:%S");
CClientDC dc(this);
//通过CSize对象我们就可以获取到字符串在屏幕所显示宽度和高度
//GetTextExtent用来得到字符串面板它显示的时候的一个宽度
CSize sz=dc.GetTextExtent(str);//加宽
//设置面板
//设置指示器面板的信息
m_wndStatusBar.SetPaneInfo(1,IDS_TIMER,SBPS_NORMAL,sz.cx);//也显示秒钟
m_wndStatusBar.SetPaneText(1,str);
CFrameWnd::OnTimer(nIDEvent);
}
6.创建进度栏:
(1)首先在CMainFrame中创建一个对象:
//给进度条创建一个对象:
CProgressCtrl m_progress;
然后再OnCreate函数返回之前创建一个进度条:
void CMainFrame::OnTimer(UINT nIDEvent)
{
//创建进度栏:
//水平进度栏
// m_progress.Create(WS_CHILD | WS_VISIBLE,CRect(100,100,200,120),this,123);
//垂直进度栏
m_progress.Create(WS_CHILD | WS_VISIBLE | PBS_VERTICAL,CRect(100,100,120,200),this,123);
//SetPos设置进度栏的当前位置
m_progress.SetPos(50);
return 0;
}
(2)为什么没有得到窗格的矩形区域呢?
创建矩形区域无效:
解决方法:
我们可以采用自定义的消息,我们在OnCreate函数中发送一个自定义的消息,然后在自定义的消息响应函数当中去获取这个状态栏上窗格的矩形区域。因为我们的消息要放到消息队列当中,而OnCreate函数呢,是对WM_Create函数进行响应的函数,在这个函数执行完成之后,随后才从消息队列当中取出我们的自定义消息,并调用相应的消息相应函数,这样我们就可以保证获取状态栏窗格的矩形区域的操作是在OnCreate函数执行完成之后执行的。
首先在MainFrm.h中定义一个消息:
//先定义一个消息
//用户的消息用UN开头的
#define UN_PROGRESS WH_USER+1
然后做消息响应函数原型的声明:
在MainFrm.h中:
//做消息响应函数原型的声明
Afx msg void OnProgress();
然后是消息映射:
在MainFrm.cpp中:
//然后就是消息映射
ON_MESSAGE(UM_PROGRESS,OnProgress)
最后是消息响应函数的实现:
在CMainFrame::OnCreate函数中去发送一个消息:
在返回之前添加:
PostMessage(UM_PROGRESS);
return 0;
}
void CMainFrame::OnProgress()
{
CRect rect;
//GetItemRect得到指定窗格的它的一个矩形的区域:
m_wndStatusBar.GetItemRect(2,&rect);
m_progress.Create(WS_CHILD | WS_VISIBLE,rect,&m_wndStatusBar,123);
m_progress.SetPos(50);
}