#106 MFC系列(一)创建空白窗口

首先,我们需要创建一个MFC的项目。

创建完成后,我们点击下一步来到如图所示的设置界面,选择单个文档,取消文档/视图结构支持,取消使用 Unicode 库。

点击下一步,选择无数据库支持。再点击下一步,主要框架仅勾选最小化框和系统菜单,并选择使用菜单栏和工具栏,然后直接点击完成。

在右侧我们可以看到解决方案资源管理器中的文件列表。

直接运行程序会发现已经自行生成了很多内容,包括菜单栏等,下面我们删除它。 我们的项目名称为MFCTest,因此在MFCTest.cpp中,函数BOOL CMFCTestApp::InitInstance()的最后三行代码为自动生成的代码:

1
2
3
4
// 唯一的一个窗口已初始化,因此显示它并对其进行更新
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;

我们将其改为:

1
2
3
4
5
// 调整窗口大小
m_pMainWnd->SetWindowPos(NULL,0,0,811,632,SWP_NOMOVE); //800*600
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;

更改后的代码将窗口渲染为811*632的大小,而实际上用于显示的部分仅800*600。 在文件MainFrm.cpp中,找到函数 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct): 可以发现为了兼容性和创建各种控件已经生成了很多的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWndEx::OnCreate(lpCreateStruct) == -1)
return -1;

BOOL bNameValid;

if (!m_wndMenuBar.Create(this))
{
TRACE0("未能创建菜单栏\n");
return -1; // 未能创建
}

m_wndMenuBar.SetPaneStyle(m_wndMenuBar.GetPaneStyle() | CBRS_SIZE_DYNAMIC | CBRS_TOOLTIPS | CBRS_FLYBY);

// 防止菜单栏在激活时获得焦点
CMFCPopupMenu::SetForceMenuFocus(FALSE);

// 创建一个视图以占用框架的工作区
if (!m_wndView.Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST, NULL))
{
TRACE0("未能创建视图窗口\n");
return -1;
}

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(theApp.m_bHiColorIcons ? IDR_MAINFRAME_256 : IDR_MAINFRAME))
{
TRACE0("未能创建工具栏\n");
return -1; // 未能创建
}

CString strToolBarName;
bNameValid = strToolBarName.LoadString(IDS_TOOLBAR_STANDARD);
ASSERT(bNameValid);
m_wndToolBar.SetWindowText(strToolBarName);

CString strCustomize;
bNameValid = strCustomize.LoadString(IDS_TOOLBAR_CUSTOMIZE);
ASSERT(bNameValid);
m_wndToolBar.EnableCustomizeButton(TRUE, ID_VIEW_CUSTOMIZE, strCustomize);

// 允许用户定义的工具栏操作:
InitUserToolbars(NULL, uiFirstUserToolBarId, uiLastUserToolBarId);

// TODO: 如果您不希望工具栏和菜单栏可停靠,请删除这五行
m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY);
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_wndMenuBar);
DockPane(&m_wndToolBar);

// 启用 Visual Studio 2005 样式停靠窗口行为
CDockingManager::SetDockingMode(DT_SMART);
// 启用 Visual Studio 2005 样式停靠窗口自动隐藏行为
EnableAutoHidePanes(CBRS_ALIGN_ANY);
// 基于持久值设置视觉管理器和样式
OnApplicationLook(theApp.m_nAppLook);

// 启用工具栏和停靠窗口菜单替换
EnablePaneMenu(TRUE, ID_VIEW_CUSTOMIZE, strCustomize, ID_VIEW_TOOLBAR);

// 启用快速(按住 Alt 拖动)工具栏自定义
CMFCToolBar::EnableQuickCustomization();

if (CMFCToolBar::GetUserImages() == NULL)
{
// 加载用户定义的工具栏图像
if (m_UserImages.Load(_T(".\\UserImages.bmp")))
{
CMFCToolBar::SetUserImages(&m_UserImages);
}
}

// 启用菜单个性化(最近使用的命令)
// TODO: 定义您自己的基本命令,确保每个下拉菜单至少有一个基本命令。
CList<UINT, UINT> lstBasicCommands;

lstBasicCommands.AddTail(ID_APP_EXIT);
lstBasicCommands.AddTail(ID_EDIT_CUT);
lstBasicCommands.AddTail(ID_EDIT_PASTE);
lstBasicCommands.AddTail(ID_EDIT_UNDO);
lstBasicCommands.AddTail(ID_APP_ABOUT);
lstBasicCommands.AddTail(ID_VIEW_STATUS_BAR);
lstBasicCommands.AddTail(ID_VIEW_TOOLBAR);
lstBasicCommands.AddTail(ID_VIEW_APPLOOK_OFF_2003);
lstBasicCommands.AddTail(ID_VIEW_APPLOOK_VS_2005);
lstBasicCommands.AddTail(ID_VIEW_APPLOOK_OFF_2007_BLUE);
lstBasicCommands.AddTail(ID_VIEW_APPLOOK_OFF_2007_SILVER);
lstBasicCommands.AddTail(ID_VIEW_APPLOOK_OFF_2007_BLACK);
lstBasicCommands.AddTail(ID_VIEW_APPLOOK_OFF_2007_AQUA);
lstBasicCommands.AddTail(ID_VIEW_APPLOOK_WINDOWS_7);

CMFCToolBar::SetBasicCommands(lstBasicCommands);

return 0;
}

可以看到:

1
2
3
4
// 启用 Visual Studio 2005 样式停靠窗口行为
CDockingManager::SetDockingMode(DT_SMART);
// 启用 Visual Studio 2005 样式停靠窗口自动隐藏行为
EnableAutoHidePanes(CBRS_ALIGN_ANY);

这两行代码仅仅只是为了兼容VS2005的窗口样式。随时代发展,这些老版本我们不考虑其兼容性,删掉以避免代码的冗长。 同样的还有很多类似的可以自行尝试。 最后修改完后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWndEx::OnCreate(lpCreateStruct) == -1)
return -1;

// 基于持久值设置视觉管理器和样式
OnApplicationLook(theApp.m_nAppLook);

// 创建一个视图以占用框架的工作区
if (!m_wndView.Create(NULL, NULL,AFX_WS_DEFAULT_VIEW, CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST, NULL))
{
TRACE0("未能创建视图窗口\n");
return -1;
}

// 消除菜单栏
SetMenu(NULL);
return 0;
}

这时我们运行的效果为:

关于windows的消息机制详细描述请查阅各种文献。这里作简要叙述:Windows为每个程序创建并维护了一个消息队列(不懂队列的请恶补数据结构),当用户操作计算机时,会产生各类消息,包括鼠标消息键盘消息等。Windows则会将这些消息放入消息队列中供程序进行while(1)读取,当遇到需要的消息时,进行消息处理,而其他情况则负责刷新渲染整个窗口。MFC作为Windows已经封装的工具,存在诸多限制,不如windows32application来得顺畅,但是十分适合初学者。

如果我的文章对你起到了帮助,你可以选择金额不限的捐助,帮助我写出更多的文章。