From: http://blog.csdn.net/yoyobaibai/article/details/6044101
E.G.
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
改变窗口的大小 -> cs.cx = 300; cs.cy = 200;
改变窗口的显示位置坐标是 cs.x 和 cs.y
这里常用的一个函数是::GetSysMetrics(SM_CXSCREEN);
::GetSysMetrics(SM_CYSCREEN);
用来取得屏幕的大小。
要改变窗口标题栏的字符串:
cs.lpszName = "Seven"; 会发现标题栏不会改变窗口的标题栏的上的字符串。
改变单文档应用程序的标题栏的字符串 参考MSDN window styles/Frame-window styles 下面有一个Changing the styles of a window create by MFC./ The SDI Case
默认的情况是WS_OVERLAPPEDWINDOW and FWS_ADDTOTITLE styles
FWS_ADDTOTITLE is add the document title to the window’s caption.
去掉FWS_ADDTOTITLE 就可以更改窗口标题栏字符串。
cs.style &= ~FWS_ADDTOTITLE;
cs.lpszName = "Seven";
如果我们需要改变背景, 画刷, 光标等等时候。
我们可以在:PreCreateWindow中创建窗口类, WNDCLASS wndClass;
把这个类里的值改变成自己想要的内容就可以了。
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
LONG SetWindowLong( HWND hWnd, // handle of window
int nIndex, // offset of value to set
LONG dwNewLong // new value );
在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类中改变brush cursor background。
在view类中调用
SetClassLong(m_hWnd, GCL_HBRBACKGROUND, (LONG)GetStockObject(DKGRAY_BRUSH));
改变了VIEW的背景为灰色。
实现一个每一秒中自动换Icon的功能。
Precondition
在resources 中添加3个icon的资源。
1. 首先在CMainFrame类中添加一个数组用于存放资源的句柄。
在类中添加 HICON m_hIcon[3]; 的一个数组。
2. 在CMainFrame 函数中加载Icon用LoadIcon这个函数。
HICON LoadIcon( HINSTANCE hInstance,// handle to application instance LPCTSTR lpIconName // icon-name string or icon resource // identifier );
如果用的系统的Icon话, LoadIcon这函数的第一个参数必须为NULL。
如果用的是自己定义的Iicon的话, 第一个参数是这个应用程序的一个实例句柄。 第二参数是一个是icon的icon-name string 可以通过MAKEINTRESOURCE这个宏把资源的ID号转换为资源的字符串名字。
LPTSTR MAKEINTRESOURCE( WORD wInteger // integer to convert );
得到应用程序实例句柄我们可以通过几种方法:
第一种: AfxGetInstanceHandle() 函数获得当前应用程序的实例句柄。
第二种: 因为CStyleApp这个类是从CWinApp这个类中继承过来的所以继承了m_hInstance 这个变量, 这个变量就是应用程序的实例句柄。因为MFC在全局中建立了一个CStyleApp中变量 theApp 如果在CMainFrame中得到这个变量就可以得到应用程序的实例句柄。 在CMainFrame中用到全局变量需要声明这个变量 添加extern CStyleApp theApp; 这样就在CMainFrame中用theApp了。
theApp.m_hInstance.
第三种: CWinApp* AfxGetApp( ); 返回的是一个CWinApp的指针。这样可以调用它的实例句柄。
3. 设置一个定时器在OnCreate中
我们这里调用的是CWND中的定时器。
UINT SetTimer( UINT nIDEvent, UINT nElapse, void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT, UINT, DWORD) );
第一个参数 定时器的ID 第二个参数是设置的时间 第三个参数是处理函数, 如果设置为NULL 发送WM_TIME消息让消息处理系统调来处理。
CWnd::SetTimer(1, 1000, NULL);
4. 在CMainFrame类中添加一个WM_TIME的消息处理函数。
static int index = 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("Failed to create toolbar/n");
return -1; // fail to create
}
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消失了, 但是这个工具条还在。这个时候工具栏的停靠位子有所变化, 这个时候需要调用
void RecalcLayout( BOOL bNotify = 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的对象绑定。
BOOL Create( CWnd* pParentWnd, DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, UINT nID = AFX_IDW_STATUS_BAR );
在MFC中是这样调用的:
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_wndStausBar.Create(this), 其他的参数都用默认的参数。 后来条用m_wndStatusBar.Setindicators(indicators, sizeof(indicators)/sizeof(UINT));
Indicators 是一个数组储存字符串ID和每一个指示符。 后面一个参数是数组里面的数是多少个。
可以添加indicator 在数组中, 首先在String Table 中添加字符串ID , 然后把字符串ID添加在indicators的数组里面。这样就可以在状态栏中显示了。
有三种方法更新状态栏上的字符。
1. Call CWnd::SetWindowText to update the text in pane 0 only.
2. Call CCmdUI::SetText in the status bar’s ON_UPDATE_COMMAND_UI handler.
3. Call SetPaneText to update the text for any pane.
我们可以把系统当前时间显示在状态栏上。
这个时候需要用到 CTime
CTime t = CTime::GetCurrentTime();
CString timeStr = t.Format("%H:%M:%S");
格式输出到一个字符串中。
然后调用 m_wndStatusBar.SetPaneText(1, timeStr);
第一个参数是索引Index of the pane whose text is to be set。
timeStr是要显示的字符串了。
如果不知道要显示的索引, 我们可用调用m_wndStatusBar的一个成员函数CommandToIndex(IDS_TIMER); 括号里面的参数是表示要显示的字符串ID。
运行程序, 我们会发现时钟的秒不能显示。 这个是因为状态栏空间不够所以不能完全显示内容。
我们可以调用SetPaneInfo();
void SetPaneInfo( int nIndex, UINT nID, UINT nStyle, int cxWidth );
函数改变状态栏空间大小。
为了获得字符串的显示宽度, 我们需要调用GetTextExtent(Cstring str);来返回字符创的空间大小, CSize。
CClientDC dc(this);
CSize sz = dc.GetTextExtent(timeStr);
之后再调用SetPaneText();来显示内容
这个时候发现时间是不动的。 需要动, 要把刚刚这段代码放到SetTimer的响应函数中不停调用显示时间。
创建一个进度栏
进度栏的类是CProgressCtrl。 定义一个变量, 并且调用Create初始化一个进度栏。
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
第一个参数是一个窗口类, E.G WS_CHILD | WS_VISIBLE | PBS_SMOOTH
第二个参数是一个矩形区域。
CProgressCtrl m_progressCtrl;
m_progressCtrl.SetPos(50); 设置到中间。
如果我们想把这个矩形区域显示在状态栏里。
首相我们在CMainFrame类中编写这样一段程序
CRect rect;
m_wndStatusBar.GetItemRect(2, &rect);
m_progressCtrl.Create(WS_CHILD| WS_VISIBLE| PBS_SMOOTH, rect,
&m_wndStatusBar, 1);
注意这里的&m_wndStatusBar是进度条的父窗口。 在View类中创建进度条时, 父类窗口就是this, 代表这个对象的指针。
运行程序发现进度条并没有出现, 我们在CRect rect ; 这里设置断点, 运行函数, 看断点的值发现rect的值是一个无效的值, 这个时候我们猜想, 在OnCreat 结束钱状态栏还没有初始化好。 这样我们可以在OnCreate之后调用一个函数来处理进度条。
我们可以自己定义一个消息,在OnCreate中把这个消息投递到消息队列中,来处理这个函数。
首先我先定义一个消息, 在window中消息都是用一个整数来表示的,在CMainFrame的头文件中添加一个消息。 #define UM_PROGRESS WM_USER+1
因为window的很多消息已经占用了很多的整数, 我们胡乱定义很可可能和系统冲突, WM_USER以下的是系统消息保留的。 以上的可以是用户自己定义的。这样我们就定义了一个UM_PROGRESS 的消息。添加完这个消息后, 要有相应的消息响应函数来处理这个消息。
protected:
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnTest();
afx_msg void OnAppendToolbar();
afx_msg void OnUpdateAppendToolbar(CCmdUI* pCmdUI);
afx_msg void OnPaint();
//}}AFX_MSG
先要在头文件的这段代码后面添加
afx_msg void OnProgress();
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() 把消息和消息处理函数关联起来的。
最后在添加消息处理函数
Void CMainFrame::OnProgress()
CRect rect;
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一个进度栏, 所照成了错误。应该判断是否已经创建没有创建的时候创建, 创建了移动窗口。
修改程序为一下:
CRect rect;
m_wndStatusBar.GetItemRect(2, &rect);
if(m_progressCtrl.m_hWnd == NULL)
m_progressCtrl.Create(WS_CHILD| WS_VISIBLE| PBS_SMOOTH, rect,
&m_wndStatusBar, 1);
else
m_progressCtrl.MoveWindow(rect);
如果要进度栏里的进度增加的话, 我们可以调用CProgressCtrl类的一个StepIt函数来增长, 把发在OnTimer(); 里面每次调用就会发送一个OnPaint函数来重绘进度栏。
现在我们想在在状态栏上显示当前鼠标的位置, 在View类中添加ON_MOUSEMOVE处理函数。
CString pointStr;
pointStr.Format("x=%d y=%d", point.x, point.y);
((CMainFrame*)GetParent())->m_wndStatusBar.SetPaneText(0, pointStr);
运行程序显示出位置。因为状态栏是框架类的, 所以首先要先获得框架类的指针, 通过GetParent() 这个函数来获得, 转换为CMainFrame指针类型。
需要在View中加入头文件“MainFrm.h”
或者调用 ((CMainFrame*)GetParent())->SetMessageText(pointStr);
也可以这样
((CMainFrame*)GetParent())->GetMessageBar()->SetWindowText(pointStr);
GetMessageBar()是一个CMainFrame类的函数, 无需知道状态栏的变量。
创建启动画面
在菜单上点击工程->添加到工程->组件和空间- >Visual C++ components->solash Screen ->insert.