VC++ 中 改变窗口的大小和样式.docx
《VC++ 中 改变窗口的大小和样式.docx》由会员分享,可在线阅读,更多相关《VC++ 中 改变窗口的大小和样式.docx(16页珍藏版)》请在冰豆网上搜索。
VC++中改变窗口的大小和样式
改变窗口的外观和大小
改变窗口的外观和大小需要在窗口创建以前改变。
所以我们可以在CMainFrame的PreCreateWindow中改变CREATESTRUCT结构体的值就行了。
E.G.
BOOLCMainFrame:
:
PreCreateWindow(CREATESTRUCT&cs)
改变窗口的大小->cs.cx=300;cs.cy=200;
改变窗口的显示位置坐标是cs.x和cs.y
这里常用的一个函数是:
:
GetSysMetrics(SM_CXSCREEN);
:
:
GetSysMetrics(SM_CYSCREEN);
用来取得屏幕的大小。
要改变窗口标题栏的字符串:
cs.lpszName="Seven";会发现标题栏不会改变窗口的标题栏的上的字符串。
改变单文档应用程序的标题栏的字符串参考MSDNwindowstyles\Frame-windowstyles下面有一个ChangingthestylesofawindowcreatebyMFC.\TheSDICase
默认的情况是WS_OVERLAPPEDWINDOWandFWS_ADDTOTITLEstyles
FWS_ADDTOTITLEisaddthedocumenttitletothewindow’scaption.
去掉FWS_ADDTOTITLE就可以更改窗口标题栏字符串。
cs.style&=~FWS_ADDTOTITLE;
cs.lpszName="Seven";
如果我们需要改变背景,画刷,光标等等时候。
我们可以在:
PreCreateWindow中创建窗口类,WNDCLASSwndClass;
把这个类里的值改变成自己想要的内容就可以了。
E.G.
wndClass.cbClsExtra=0;
wndClass.cbWndExtra=0;
wndClass.hbrBackground=(HBRUSH)GetStockObject(DKGRAY_BRUSH);
wndClass.hCursor=LoadCursor(NULL,IDC_WAIT);
wndClass.hIcon=LoadIcon(NULL,IDI_WARNING);
wndClass.hInstance=AfxGetInstanceHandle();
获取应用程序的实例句柄可用AfxGetInstanceHandle函数,这个函数是一个全局的函数,前面有一个AFX表示是一个应用程序框架类函数,哪里都可用。
wndClass.lpfnWndProc=:
:
DefWindowProc;
我们只是想要改变窗口的图标,光标,不想改变条用过程所以用defWindowProc函数来处理。
因为在CWnd中也有一个defWindowProc函数,比全局的函数少一个参数如果不加:
:
的话调用就要报错。
wndClass.lpszMenuName=NULL;
创建菜单并不是在在设计窗口类的时候创建,菜单的创建实在CStyleAPP:
:
InitInstance函数中将菜单的标识传进去。
wndClass.style=CS_HREDRAW|CS_VREDRAW;
这里的style并不是窗口的类型而是窗口类的类型。
RegisterClass(&wndClass);
注册窗口类。
cs.lpszClass="GL";
后来把我们刚刚设计好的类赋值个cs.lpszClass。
运行程序之后我们会发现只有图标改变了,cursor和brush都没有改变。
这是因为,我们是在frame中改变的,在frame上边还覆盖一个子窗口类。
所以我要改变这些需要把在子窗口的View类中PreCreateWindow中把类名复制。
E.g
在CStyleView中cs.lpszClass="GL";
就可以把我们刚刚的想要的类型来改变view中的窗口类型,因为“GL”窗口类已经在CMainFrame框架类中已经注册了,所以我们可以直接赋值就可以了。
在frame中只可以改变ICON,为了改变图标我们重写窗口类我很不划算,在MFC中为我们提供了一个函数AfxRegisterWndClass直接改变icon详见MSDN
cs.lpszName=AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW,0,0,
LoadIcon(NULL,IDI_WARNING));
这样就直接改变了frame中Icon。
我们也可以在CStyleView中调用这个函数来改变窗口的画刷,光标。
窗口创建之后改变外观
用SetWinowLong函数具体见MSDN
LONGSetWindowLong(HWNDhWnd,//handleofwindow
intnIndex,//offsetofvaluetoset
LONGdwNewLong//newvalue);
在CMainFrame的OnCreate中调用SetWindowLong
SetWindowLong(m_hWnd,GWL_STYLE,WS_OVERLAPPEDWINDOW);
这样就可以去掉了文档标题是窗口标题了。
我们可以通过GetWindowLong函数得到当前窗口的类型。
详见MSDN
SetWindowLong(m_hWnd,GWL_STYLE,GetWindowLong(m_hWnd,GWL_STYLE)&~WS_MAXIMIZEBOX);
这样就可以灰掉窗口的最大化窗口了。
窗口创建之后改变窗口类
上面的是改变窗口的大小和最大化最小化等等的按钮。
我们可以用SetClassLong来改变创建之后的窗口类的参数例如光标,鼠标,画刷等等。
在单文档的应用程序中,在CMainFrame类中只可以改变Icon调用:
在OnCreate函数中
SetClassLong(m_hWnd,GCL_HICON,(LONG)LoadIcon(NULL,IDI_QUESTION));
可以改变application的icon。
在VIEW类中改变brushcursorbackground。
在view类中调用
SetClassLong(m_hWnd,GCL_HBRBACKGROUND,(LONG)GetStockObject(DKGRAY_BRUSH));
改变了VIEW的背景为灰色。
实现一个每一秒中自动换Icon的功能。
Precondition
在resources中添加3个icon的资源。
1.首先在CMainFrame类中添加一个数组用于存放资源的句柄。
在类中添加HICONm_hIcon[3];的一个数组。
2.在CMainFrame函数中加载Icon用LoadIcon这个函数。
HICONLoadIcon(HINSTANCEhInstance,//handletoapplicationinstanceLPCTSTRlpIconName//icon-namestringoriconresource//identifier);
如果用的系统的Icon话,LoadIcon这函数的第一个参数必须为NULL。
如果用的是自己定义的Iicon的话,第一个参数是这个应用程序的一个实例句柄。
第二参数是一个是icon的icon-namestring可以通过MAKEINTRESOURCE这个宏把资源的ID号转换为资源的字符串名字。
LPTSTRMAKEINTRESOURCE(WORDwInteger//integertoconvert);
得到应用程序实例句柄我们可以通过几种方法:
第一种:
AfxGetInstanceHandle()函数获得当前应用程序的实例句柄。
第二种:
因为CStyleApp这个类是从CWinApp这个类中继承过来的所以继承了m_hInstance这个变量,这个变量就是应用程序的实例句柄。
因为MFC在全局中建立了一个CStyleApp中变量theApp如果在CMainFrame中得到这个变量就可以得到应用程序的实例句柄。
在CMainFrame中用到全局变量需要声明这个变量添加externCStyleApptheApp;这样就在CMainFrame中用theApp了。
theApp.m_hInstance.
第三种:
CWinApp*AfxGetApp();返回的是一个CWinApp的指针。
这样可以调用它的实例句柄。
3.设置一个定时器在OnCreate中
我们这里调用的是CWND中的定时器。
UINTSetTimer(UINTnIDEvent,UINTnElapse,void(CALLBACKEXPORT*lpfnTimer)(HWND,UINT,UINT,DWORD));
第一个参数定时器的ID第二个参数是设置的时间第三个参数是处理函数,如果设置为NULL发送WM_TIME消息让消息处理系统调来处理。
CWnd:
:
SetTimer(1,1000,NULL);
4.在CMainFrame类中添加一个WM_TIME的消息处理函数。
staticintindex=1;
SetClassLong(m_hWnd,GCL_HICON,(LONG)m_hIcons[index]);
index=++index%3;
这样就可以定义出一个会随时间改变的ICON。
工具栏的编程ToolBar
工具栏在resource中添加一个工具栏Item它的ID和menu中的菜单中的一个菜单的Id一样,那么这个工具栏的Item就代表着那个菜单。
E.g
在resourcesiView中在帮助的下面添加一个菜单,叫TEST它的ID叫ID_TEST并且建立对应的command消息响应函数,弹出对话框。
在resourcesView的ToolBar中建立一个Item他的ID也叫ID_TEST这样她们就关联起来了啊。
Click两个都是一样的。
添加一个自己的工具栏
在MSDN中查看CToolBar提供了两种创建工具栏的方法
1.首先在resourcesView中创建一个自己的ToolBar,ToolBar上的Item自己设定。
2.在CMainFrame中添加一个CToolBar的类成员,构造一个CToolBar的变量。
3.调用CToolBar的Create或者CteateEX参考MSDN
可以参考CMainFrame中构造ToolBar的方法。
if(!
m_wndToolBar.CreateEx(this,TBSTYLE_FLAT,WS_CHILD|WS_VISIBLE|CBRS_TOP
|CBRS_GRIPPER|CBRS_TOOLTIPS|CBRS_FLYBY|CBRS_SIZE_DYNAMIC)||
!
m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failedtocreatetoolbar\n");
return-1;//failtocreate
}
4.调用LoadToolBar这个函数加载资源。
5.调用CToolBar的成员函数EnableDocking(CBRS_ALIGN_ANY);设置这个对象是可以停靠的。
如果不设置这个那么工具栏将不可以悬浮。
6.再调用CMainFrame:
:
EnableDocking(CBRS_ALIGN_ANY);设置框架类可以被停靠。
7.最后在调用CMainFrame中的函数DockControlBar(&m_wndToolBar);停靠工具栏。
8.我们还可以通过在菜单栏上设置一个Item并且设置command消息处理函数。
在处理函数中写上
if(m_toolBar.IsWindowVisible())
{
m_toolBar.ShowWindow(SW_HIDE);
}
else
{
m_toolBar.ShowWindow(SW_SHOW);
}
CWnd:
:
IsWindowVisible当有WS_VISIBLE的时候返回TRUE.用IsWindowVisible来返回ToolBar是不是可见的。
当我调用这个处理的时候点击菜单里的Item来显示或者隐藏工具栏。
但是发现虽然ToolBar消失了,但是这个工具条还在。
这个时候工具栏的停靠位子有所变化,这个时候需要调用
CFrameWnd:
:
RecalcLayout函数。
voidRecalcLayout(BOOLbNotify=TRUE);
在上面的程序后面接上
RecalcLayout();
当把工具栏拖出来,处于浮空的ToolBar的时候发现,然后点击菜单来显示或者隐藏ToolBar发现当浮空的时候ToolBar的工具条不会隐藏。
这个时候我们还需要调用一个函数
DockControlBar(&m_toolBar);
显示或隐藏工具栏的第二种方法
这个时候调用一个函数
ShowControlBar(&m_toolBar,!
m_toolBar.IsWindowVisible(),FALSE);具体调用参见MSDN。
ShowControlBar是CFrameWnd框架类的一个函数。
9.我们还需要标记菜单的Item。
当选中是标记,当隐藏时取消标记。
这个时候我们需要对这个Item的UPDATE_COMMAND_UI进行响应。
建立消息响应来标记活隐藏标记。
调用CCmdUI的SetCheck();这个函数。
来标记或者取消标记。
pCmdUI->SetCheck(m_toolBar.IsWindowVisible());具体用法参见MSDN。
状态栏的编写
1.首先construct一个CStatusBar的对象。
2.然后调用变量的Create函数把状态栏窗口和一个CStatusBar的对象绑定。
CStatusBar:
:
Create
BOOLCreate(CWnd*pParentWnd,DWORDdwStyle=WS_CHILD|WS_VISIBLE|CBRS_BOTTOM,UINTnID=AFX_IDW_STATUS_BAR);
在MFC中是这样调用的:
if(!
m_wndStatusBar.Create(this)||!
m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failedtocreatestatusbar\n");
return-1;//failtocreate
}
m_wndStausBar.Create(this),其他的参数都用默认的参数。
后来条用m_wndStatusBar.Setindicators(indicators,sizeof(indicators)/sizeof(UINT));
Indicators是一个数组储存字符串ID和每一个指示符。
后面一个参数是数组里面的数是多少个。
可以添加indicator在数组中,首先在StringTable中添加字符串ID,然后把字符串ID添加在indicators的数组里面。
这样就可以在状态栏中显示了。
有三种方法更新状态栏上的字符。
1.CallCWnd:
:
SetWindowTexttoupdatethetextinpane0only.
2.CallCCmdUI:
:
SetTextinthestatusbar’sON_UPDATE_COMMAND_UIhandler.
3.CallSetPaneTexttoupdatethetextforanypane.
我们可以把系统当前时间显示在状态栏上。
这个时候需要用到CTime
CTimet=CTime:
:
GetCurrentTime();
CStringtimeStr=t.Format("%H:
%M:
%S");
格式输出到一个字符串中。
然后调用m_wndStatusBar.SetPaneText(1,timeStr);
第一个参数是索引Indexofthepanewhosetextistobeset。
timeStr是要显示的字符串了。
如果不知道要显示的索引,我们可用调用m_wndStatusBar的一个成员函数CommandToIndex(IDS_TIMER);括号里面的参数是表示要显示的字符串ID。
运行程序,我们会发现时钟的秒不能显示。
这个是因为状态栏空间不够所以不能完全显示内容。
我们可以调用SetPaneInfo();
CStatusBar:
:
SetPaneInfo
voidSetPaneInfo(intnIndex,UINTnID,UINTnStyle,intcxWidth);
函数改变状态栏空间大小。
为了获得字符串的显示宽度,我们需要调用GetTextExtent(Cstringstr);来返回字符创的空间大小,CSize。
CClientDCdc(this);
CSizesz=dc.GetTextExtent(timeStr);
之后再调用SetPaneText();来显示内容
这个时候发现时间是不动的。
需要动,要把刚刚这段代码放到SetTimer的响应函数中不停调用显示时间。
创建一个进度栏
进度栏的类是CProgressCtrl。
定义一个变量,并且调用Create初始化一个进度栏。
CProgressCtrl:
:
Create
BOOLCreate(DWORDdwStyle,constRECT&rect,CWnd*pParentWnd,UINTnID);
第一个参数是一个窗口类,E.GWS_CHILD|WS_VISIBLE|PBS_SMOOTH
第二个参数是一个矩形区域。
CProgressCtrlm_progressCtrl;
m_progressCtrl.SetPos(50);设置到中间。
如果我们想把这个矩形区域显示在状态栏里。
首相我们在CMainFrame类中编写这样一段程序
CRectrect;
m_wndStatusBar.GetItemRect(2,&rect);
m_progressCtrl.Create(WS_CHILD|WS_VISIBLE|PBS_SMOOTH,rect,
&m_wndStatusBar,1);
注意这里的&m_wndStatusBar是进度条的父窗口。
在View类中创建进度条时,父类窗口就是this,代表这个对象的指针。
运行程序发现进度条并没有出现,我们在CRectrect;这里设置断点,运行函数,看断点的值发现rect的值是一个无效的值,这个时候我们猜想,在OnCreat结束钱状态栏还没有初始化好。
这样我们可以在OnCreate之后调用一个函数来处理进度条。
我们可以自己定义一个消息,在OnCreate中把这个消息投递到消息队列中,来处理这个函数。
首先我先定义一个消息,在window中消息都是用一个整数来表示的,在CMainFrame的头文件中添加一个消息。
#defineUM_PROGRESSWM_USER+1
因为window的很多消息已经占用了很多的整数,我们胡乱定义很可可能和系统冲突,WM_USER以下的是系统消息保留的。
以上的可以是用户自己定义的。
这样我们就定义了一个UM_PROGRESS的消息。
添加完这个消息后,要有相应的消息响应函数来处理这个消息。
protected:
//{{AFX_MSG(CMainFrame)
afx_msgintOnCreate(LPCREATESTRUCTlpCreateStruct);
afx_msgvoidOnTimer(UINTnIDEvent);
afx_msgvoidOnTest();
afx_msgvoidOnAppendToolbar();
afx_msgvoidOnUpdateAppendToolbar(CCmdUI*pCmdUI);
afx_msgvoidOnPaint();
//}}AFX_MSG
先要在头文件的这段代码后面添加
afx_msgvoidOnProgress();
DECLARE_MESSAGE_MAP()
参考MFC的程序;
然后在。
CPP文件中
BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_WM_TIMER()
ON_COMMAND(ID_TEST,OnTest)
ON_COMMAND(ID_APPEND_TOOLBAR,OnAppendToolbar)
ON_UPDATE_COMMAND_UI(ID_APPEND_TOOLBAR,OnUpdateAppendToolbar)
ON_WM_PAINT()
//}}AFX_MSG_MAP
ON_MESSAGE(UM_PROGRESS,OnProgress)
END_MESSAGE_MAP()
消息响应函数是通过ON_MESSAGE()把消息和消息处理函数关联起来的。
最后在添加消息处理函数
VoidCMainFrame:
:
OnProgress()
CRectrect;
m_wndStatusBar.GetItemRect(2,&rect);
m_progressCtrl.Create(WS_CHILD|WS_VISIBLE|PBS_SMOOTH,rect,
&m_wndStatusBar,1);
在这个函数中处理进度条。
在OnCreate函数中用PostMessage();
不能用SendMessage();因为SendMessage是直接调用了处理函数,这个时候状态栏还没有处理好。
现在运行应用程序,这个时候progress显示在状态栏上,但是当我们拉伸或者缩放的时候进度栏不再第二个状态栏上的时候而是在别的地方这是为什么呢?
?
每当窗口的尺寸变化的时候就会重绘发送ON_PAIN这个消息我们可以创建响应函数,在这个函数中编写上面的程序。
当改变尺寸的时候会当初dialog错误,这是因为每当发送一个ON_PAIN消息函数的时候都会Create一个进度栏,所照成了错误。