文章目录
前言
C++语言是最主要的软件开发语言,几乎所有的计算机软件、手机软件以及嵌入式软件等都是使用C++语言进行开发的。在Windows桌面上的全部软件几乎都是使用C++语言开发的,例如,QQ、360、迅雷、各种Office办公软件以及各种网络游戏和播放器软件等。
Visual C++ 简称VC或VC++,是由微软提供的C++语言开发工具,它是一个编译器而且是一个集成开发环境包括编辑器、调试器和编译器等。除了VC之外还有gcc也是C++语言编译器,VC主要用于开发Windows桌面软件;gcc主要针对UNIX和Linux操作系统,开发嵌入式软件和手机软件等。
MFC(Microsoft Foundation Classes,微软基础类库)是微软专门封装的用于Windows平台开发的类库。MFC内部全部使用C++语言,分类封装Windows API和Windows SDK(Software Development Kit,软件开发工具包)中的结构和功能。MFC还提供了一个应用程序框架,例如,应用程序向导和类向导自动生成的代码,大大减少了软件开发者的工作量,提高了开发效率。
第1章 Visual C++ 6.0的安装和使用
第1节安装 Visual C++ 6.0
下载链接:
- 选择“英文版”或者“中文版”;
- 单击“next”按钮;
- 选择“I accept the agreement”,单击“next”按钮;
- 随便输入“Your name”,单击“next”按钮;
- 单击“next”按钮;
- 单击“next”按钮;
- 单击“继续”按钮;
- 单击“确定”按钮;
- 选择“typical”;
- 把勾划掉,单击“OK”按钮;
- 安装完毕后,继续安装MSDN。
第2节 安装MSDN for Visual C++ 6.0
第3节 开始使用Visual C++ 6.0
新建一个控制台程序
// 001C.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <stdio.h>
int main(int argc, char* argv[])
{
printf("这是我的第一个C语言程序!\n");
return 0;
}
默认位置C:\Program Files\Microsoft Visual Studio\MyProjects\001C
单击 build,或者F7 编译;
执行;
进入Windows控制台窗口
C:\Program Files\Microsoft Visual Studio\MyProjects\001C\Debug
然后输入001C;
第4节 Visual C++ 6.0开发环境简介
第5节 Visual C++ 6.0调试环境介绍
进入调试状态
- 直接按
<F10>
或者<F11>
,进入main函数开始单步执行程序; - 按在光标所在行设置一个断点,然后按让程序直接运行到断点处再单步执行;
- 把光标停放在某一行,然后按<Ctrl+F10>让程序直接运行到光标处再单步执行。
调试窗口
- 手动变量观察窗口(Watch)
- 自动变量观察窗口(Variables)
- Debug工具栏
Debug工具栏中显示的都是重要的调试功能,调试快捷键及功能
第2章 Windows编程基础
使用Win32和MFC两种方式讲解简单Windows软件开发的方法。Win32程序开发包括Win32程序编写、图标资源加载和对话框资源加载等。介绍Windows开发中常用的数据类型,包括Windows数据类型和MFC封装的数据类型。
第1节 第一个Win32软件
- 执行File->New命令,新建工程;
- 在左侧列表中选择“Win32 Application”,选择目录,最后在“Project Name”输入工程名;
- 单击“OK”进入程序向导,选中“A simple Win32 application”,单击“finish”完成;
- 双击“Globals”下面的“WinMain”函数,输入一个简单的输出语句;
// First32A.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
MessageBox(NULL,"这是我开发第一个Windows软件","温馨提示",MB_OK);
return 0;
}
- build或按
<F7>
; - Debug目录下生成了可执行文件First32.exe
- 修改
// First32A.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
MessageBox(NULL,"这是我开发第一个Windows软件","温馨提示",MB_OK|MB_ICONINFORMATION);
return 0;
}
第2节 Win32程序资源管理
打开第1节建立的First32工程,添加Windows资源
- File->New,添加资源;
- 选中Resource Scrip,填写名称,与工程名相同;
- 单击OK
- ResourceView展开根节点是时,出现提示框,关闭First32.rc再展开;
- 在ResourceView根节点上单击鼠标右键,选择“Insert”;
- 选中Icon,再单击Next;
- 随意勾图案;
- 按
<F7>
编译生成可执行文件,这时候应用程序就有一个自己的图标了。
第3节 基于对话框的Win32程序
本节开发基于对话框的Win32程序,输入输出功能。
- 在ResourceView根节点上单击鼠标右键,选择“Insert”;
- 选中Dialog,单击New;
- 在对话框资源上单击右键,选择Properties;
- 分别设置字体、ID、Caption;
- 添加控件Edit和Static Text;
按住 ctrl+D。可以使用Tap键; - 属性设置;
Edit的ID分别为IDC_LEFT,IDC_RIGHT、IDC_RESULT;
Static Text的Caption分别为+、-; - 计算、关闭;
- 在ClassView中双击WinMain函数修改代码;
// First32.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "resource.h"
#include <stdio.h>
BOOL CALLBACK MainProc(
HWND hwndDlg, // handle to dialog box
UINT uMsg, // message
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
if(WM_COMMAND==uMsg)
{
if(LOWORD(wParam)==IDCANCEL)
{
EndDialog(hwndDlg,IDCANCEL);
return TRUE;
}
if(LOWORD(wParam)==IDOK)
{
//MessageBox(hwndDlg,"测试了计算","测试",0);
int nLeft = GetDlgItemInt(hwndDlg,IDC_LEFT,NULL,TRUE);
int nRight = GetDlgItemInt(hwndDlg,IDC_RIGHT,NULL,TRUE);
int nResult = nLeft + nRight;
SetDlgItemInt(hwndDlg,IDC_RESULT,nResult,TRUE);
}
}
return FALSE;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
DialogBox(hInstance,(LPCSTR)IDD_FIRSTDLG,NULL,MainProc);
return 0;
}
或者
// First32.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "resource.h"
#include <stdio.h>
BOOL CALLBACK MainProc(
HWND hwndDlg, // handle to dialog box
UINT uMsg, // message
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
switch(uMsg)
{
case WM_COMMAND: //判断消息类型
switch(wParam)
{
case IDCANCEL: //判断单击的按钮
EndDialog(hwndDlg,IDCANCEL);
break;
case IDOK: //判断单击的按钮
{
int nLeft = GetDlgItemInt(hwndDlg,IDC_LEFT,NULL,TRUE);
int nRight = GetDlgItemInt(hwndDlg,IDC_RIGHT,NULL,TRUE);
int nResult = nLeft + nRight;
SetDlgItemInt(hwndDlg,IDC_RESULT,nResult,TRUE);
}
break;
}
break;
}
return FALSE;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
DialogBox(hInstance,(LPCSTR)IDD_FIRSTDLG,NULL,MainProc);
return 0;
}
- 查看MSDN中关于DialogBox弹出对话框的函数说明;
DialogBox
The DialogBox macro creates a modal dialog box from a dialog box template resource. DialogBox does not return control until the specified callback function terminates the modal dialog box by calling the EndDialog function. The DialogBox macro uses the DialogBoxParam function.
int DialogBox(
HINSTANCE hInstance, // handle to application instance
LPCTSTR lpTemplate, // identifies dialog box template
HWND hWndParent, // handle to owner window
DLGPROC lpDialogFunc // pointer to dialog box procedure
);
1)第一个参数hInstance:相当于应用程序实例,由WinMain函数代入用于加载进程内的资源;
2)第二个参数lpTemplate:指定与对话框模版关联的资源ID,代入前要对数字ID强制类型转化;
3)第三个参数hWndParent:指定父窗口句柄,一般代入NULL,因为对话框作为主窗口没有父窗口;
4)最后一个参数lpDialogFunc:是一个指定格式的回调函数的地址,该函数用于处理各类窗口事件。
- 回调函数的参数列表和返回值必须按指定的格式编写;
DialogProc
The DialogProc function is an application-defined callback function used with the DialogBox function. It processes messages sent to a modal or modeless dialog box. The DLGPROC type defines a pointer to this callback function. DialogProc is a placeholder for the application-defined function name.
BOOL CALLBACK DialogProc(
HWND hwndDlg, // handle to dialog box
UINT uMsg, // message
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
1)第一个参数hwndDlg:与该回调函数关联的对话框句柄,用户操作对话框;
2)第二个参数uMsg:消息号码,用于解析对话框窗口发生的事件,例如,单击按钮;
3)最后2个参数wParam和lParam,是窗口消息的相关数据,例如,单击按钮的ID。
第4节 Windows数据类型
第5节 初步使用MFC
基于MFC架构平台的对话框程序
- 执行File->New命令,选择MFC AppWizard(exe),在Project name 填写工程名FirstMFC;
- 进入MFC应用程序向导,选中Dialog based 基本对话框;
- 第二步不做修改;
选中As a statically linked library作为静态的DLL,finish;
- 完成工程创建后按F7编译;通过MFC应用程序向导创建的工程中,工作区(Workspace)自动带有资源视图(ResourceView)分页,其中已经自动生成了Dialog、Icon、Version以及StringTable等资源。
- 在主对话框资源中,设置对话框的标题(Caption)并添加一些控件;
- 添加设置对话框所有控件的属性;
控件类型 | ID | Caption | Styles |
---|---|---|---|
Static Text | IDC_STATIC | 工号 | |
Edit Box | IDC_NUMB | ||
Static Text | IDC_STATIC | 姓名 | |
Edit Box | IDC_NAME | ||
Static Text | IDC_STATIC | 基本工资 | |
Edit Box | IDC_SALA | ||
List Control | IDC_LIST | View:Report | |
Button | IDC_ADD | 添加 | |
Button | IDC_DEL | 删除 | |
Button | IDC_MOD | 修改 |
- 分别双击 添加、删除、修改按钮,创建3个与按钮关联的成员函数;
- 在对话框关联的源文件FirstMFCDlg.cpp中,修改初始化函数代码;
BOOL CFirstMFCDlg::OnInitDialog()
{
CDialog::OnInitDialog();
CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST);
pList ->InsertColumn(0,"工号",LVCFMT_LEFT,100);
pList ->InsertColumn(1,"姓名",0,100);
pList ->InsertColumn(2,"工资",0,100);
//后面其余部分原来的代码不变
- 再修改3个按钮关联函数的代码;
void CFirstMFCDlg::OnAdd()
{
CString str;
this ->GetDlgItemText(IDC_NUMB,str);
CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST);
int nCount = pList ->GetItemCount();
pList ->InsertItem(nCount,str);
GetDlgItemText(IDC_NAME,str);
pList ->SetItemText(nCount,1,str);
GetDlgItemText(IDC_SALA,str);
pList ->SetItemText(nCount,2,str);
}
/*
void CFirstMFCDlg::OnAdd()
{
CString szNumb,szName,szSala;
GetDlgItemText(IDC_NUMB,szNumb);
GetDlgItemText(IDC_NAME,szName);
GetDlgItemText(IDC_SALA,szSala);
CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST);
int nCount = pList ->GetItemCount();
pList ->InsertItem(nCount,szNumb);
pList ->SetItemText(nCount,1,szName);
pList ->SetItemText(nCount,2,szSala);
}
*/
void CFirstMFCDlg::OnDel()
{
CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST);
//int nSel = pList ->GetSelectionMark();
POSITION pos = pList ->GetFirstSelectedItemPosition();
int nSel = pList ->GetNextSelectedItem(pos);
if(nSel < 0)
{
AfxMessageBox("请先选中一行再删除!");
return;
}
if(IDYES==AfxMessageBox("确认删除选中的数据吗?",MB_YESNO))
pList ->DeleteItem(nSel);
}
void CFirstMFCDlg::OnMod()
{
CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST);
//int nSel = pList ->GetSelectionMark();
POSITION pos = pList ->GetFirstSelectedItemPosition();
int nSel = pList ->GetNextSelectedItem(pos);
if(nSel < 0)
{
AfxMessageBox("请先选中一行再修改!");
return;
}
CString str;
GetDlgItemText(IDC_NUMB,str);
if(IDNO==AfxMessageBox("确定要修改 " + str + " 的数据吗?",MB_YESNO))
return;
GetDlgItemText(IDC_NAME,str);
pList ->SetItemText(nSel,1,str);
GetDlgItemText(IDC_SALA,str);
pList ->SetItemText(nSel,2,str);
/*
CString szNumb,szName,szSala;
GetDlgItemText(IDC_NUMB,szNumb);
GetDlgItemText(IDC_NAME,szName);
GetDlgItemText(IDC_SALA,szSala);
pList ->InsertItem(nSel,szNumb);
pList ->SetItemText(nSel,1,szName);
pList ->SetItemText(nSel,2,szSala);
*/
}
- 编译运行;
- MFC的动态链接和静态链接。在使用MFC应用程序向导新建工程时,选择“As a statically linked library”选项编译器会把MFC类库代码编译到可执行文件内部;选择“As a shared DLL”使用系统内共享的MFC动态库。前者编译出来的可执行文件虽然较大,但是不需要系统提供动态库支持,更加安全。
- API和MFC的关系。API(Application Programming Interface,应用程序接口)是由操作系统提供给开发者的C语言格式的全局函数。GetDlgItemInt和SetDlgItemInt函数就是API函数。Win32是面向API的编程平台。MFC(Microsoft Fundation Class,微软的基础类库)这个类库分装了大部分Windows API,便于开发人员了解类库函数功能。
第6节对话框资源编辑
对话框和内部的每个控件都有自己的资源ID,ID是一些不重复的数字标志。
- 对话框资源自身的属性包括5个分页;
- General分页;ID、标题Caption、字体Font;
- Styles分页;
1)Style:默认选择Popup弹出式对话框,在下拉列表中还包括Child风格用于设置子对话框;
2)Border:默认选择DialogFrame,边框比较厚重而且固定窗口尺寸;若在下拉列表中选择Resizing可以通过鼠标拖动对话框边缘来改变窗口尺寸;选Thin则边框没有边缘,看起来比较薄(去掉Title bar属性后生效);选None则会强制去除Title bar和System menu属性,而且没有边框线;
3)Title bar:用于设置对话框是否显示标题栏;
4)System menu:设置标题栏上是否含有系统菜单;
5)Minimize box:设置是否显示最小化按钮;
6)Maximize box:设置是否显示最大化按钮; - More Styles分页,这个分页有些属性只适合在非模式对话框中使用;
Disable:使对话框内所所有控件都失效;
Context help:在标题栏显示问好按钮;
Center:对话框启动后在屏幕中央显示;
Control:对话框启动后去掉标题栏; - Extended Styles分页
Tool windows:使标题栏和关闭按钮栏变小,并且在任务栏中不显示窗口图标;
Client edge:深度下陷;Static edge浅度下陷;
Accept files:允许从外部拖放一些文件放入对话框内,并在拖放后产生WM_DROPFILES消息;
Context help:标题栏显示问好按钮,单击该按钮后单击窗口产生WM_HELP消息; - 静态文本控件(Static Text)
1)一般的静态文本控件的默认ID都是IDC_STATIC,开发时一般不需要修改默认ID即可;
2)只有当需要通过代码操作静态文本时,才设置静态控件的ID;
3)填写静态文本控件的Caption时,加入&符号可以使其后面的英文字母带有下划线;例如:工号(&N),按快捷键<alt+N>时焦点落在“工号”后面的编辑框内; - 编辑控件(Edit Box)主要设置Style分页;
1)文本对其Align text;
2)多行文字Multiline;
3)自动横向滚动Auto HScroll;
4)竖直滚动Vertical scroll;
5)换行Want return;
6)密码Password:*隐藏输入的文字;
7)只读Read-only;
8)数字Number;
9)大些Uppercase;
10)小写Lowercase: - 列表控件List Ccontrol,主要设置Style分页;
1)列表视图View:大图标Icon、小图标Small icon、简单列表List、详细列表Report;
2)排序方式Sort:不排序None、由小到大排序Ascending、由大到小排序Descending;
3)单行选取Single selection;
4)编辑表哥Edit Labels;
5)没有列表头No column header;
6)不排序表头No sort header;
7)一直显示选择项Show selection always;
第7节 MFC封装的数据类型
MFC封装的数据类型不是函数库而是类库,最常用的基本类型有字符串类(Cstring)、文件类(CFile)、时间类(CTime)等。用于几何空间的类有坐标点(CPoint)、空间尺寸(CSize)、矩形区域类(CRect)等。还有用来记录各群体数据的集合类,包括链表(CList)、动态数组(CArray)、映射类(CMap)等。
第3章 MFC原理介绍
包括C++封装原理和MFC六大关键技术,以及类向导和MFC应用程序向导的使用方法。讲解MFC消息映射机制的原理、消息映射函数的建立,以及消息发送和接收的方法。
第1节 使用时间类(CTime)
- 打开MSDN,索引输入CTime,选择CTime Class Members,查看CTime类所有成员函数;
函数原型 | 函数说明 |
---|---|
CTime( ); | 缺省构造函数,构造一个无效时间对象 |
CTime( const CTime& timeSrc ); | 拷贝构造函数,从另一个对象复制时间 |
CTime( time_t time ); | 构造函数,从C语言时间句柄构造时间 |
CTime( int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec, int nDST = -1 ); | 构造函数,通过年月日时分秒及毫秒构造时间 |
CTime( const SYSTEMTIME& sysTime, int nDST = -1 ); | 从SYSTEMTIME结构体对象构造时间 |
CTime( const FILETIME& fileTime, int nDST = -1 ); | 从FILETIME结构体对象构造时间 |
static CTime GetCurrentTime( ); | 获取当前时间 |
time_t GetTime( ) const; | 从CTime对象获取C语言时间句柄 |
int GetYear( ) const; | 获取年份 |
int GetMonth( ) const; | 获取月份 |
int GetDay( ) const; | 获取日期 |
int GetHour( ) const; | 获取小时 |
int GetMinute( ) const; | 获取分钟 |
int GetSecond( ) const; | 获取秒 |
int GetDayOfWeek( ) const; | 获取星期 |
运算符“==”、“!=”、“>”、“<”、“>=”、“<=” | 比较两个时间对象 |
- 新建工程对话框;添加ID为IDC_TEST的按钮;
双击“测试CTime”按钮,创建与按钮关联的成员函数OnTest;修改OnTest函数代码,用CTime类来显示当前时间;
void CTmDlg::OnTest()
{
// static成员无需对象,直接使用类名来调用即可
CTime t = CTime::GetCurrentTime();
int nYear = t.GetYear();
int nMonth = t.GetMonth();
int nDay = t.GetDay();
int mHour = t.GetHour();
int nMinute = t.GetMinute();
int nSecond = t.GetSecond();
CString szTime;//格式化组串:将各种类型的数据合成一个字符串
szTime.Format("%d-%d-%d %d:%d:%d",nYear,nMonth,nDay,mHour,nMinute,nSecond);
AfxMessageBox(szTime);
}
第2节 C++ 封装原理
面向对象的程序有四大特征:抽象、封装、继承和多态;在C++程序设计中经常封装一个类,让调用更容易使用;本节通过对比C语言time族函数和CTime类,学习MFC封装类的产生过程。
函数原型 | 函数说明 |
---|---|
time_t time( time_t *timer ); | 获取当前时间,返回值是一个时间句柄 |
struct tm *localtime( const time_t *timer ); | 将一个时间句柄转换为年月日时分秒的结构体对象 |
time_t mktime( struct tm *timeptr ); | 将一个年月日时分秒保存到一个时间句柄 |
添加按钮IDC_TEST2,测试time,创建与按钮关联的成员函数OnTest2;
void CTmDlg::OnTest2()
{
time_t t = time(NULL); //获取当前时间句柄
tm* pt = localtime(&t); //将时间句柄转化为tm结构体,再逐一转化成时间数值
int nYear = pt->tm_year+1900;
int nMonth = pt->tm_mon+1;
int nDay = pt->tm_mday;
int mHour = pt->tm_hour;
int nMinute = pt->tm_min;
int nSecond = pt->tm_sec;
char szTime[64]; //将时间数值合成一个字符串
sprintf(szTime,"%d-%d-%d %02d:%02d:%02d",nYear,nMonth,nDay,mHour,nMinute,nSecond);
::MessageBox(NULL,szTime,"测试time",MB_OK);
}
tm结构体的定义如下;
struct tm {
int tm_sec; /* seconds after the minute - [0,59] */
int tm_min; /* minutes after the hour - [0,59] */
int tm_hour; /* hours since midnight - [0,23] */
int tm_mday; /* day of the month - [1,31] */
int tm_mon; /* months since January - [0,11] */
int tm_year; /* years since 1900 */
int tm_wday; /* days since Sunday - [0,6] */
int tm_yday; /* days since January 1 - [0,365] */
int tm_isdst; /* daylight savings time flag */
};
了解封装的过程。在GetCurrentTime行设置一个断点,按F5调试运行到断点处按F11,进入函数查看MFC源代码;
CTime的类成员函数,全部是调用了C语言的time族函数;
CTime PASCAL CTime::GetCurrentTime()
// return the current system time
{
return CTime(::time(NULL));
}
struct tm* CTime::GetGmtTm(struct tm* ptm) const
{
if (ptm != NULL)
{
*ptm = *gmtime(&m_time);
return ptm;
}
else
return gmtime(&m_time);
}
struct tm* CTime::GetLocalTm(struct tm* ptm) const
{
if (ptm != NULL)
{
struct tm* ptmTemp = localtime(&m_time);
if (ptmTemp == NULL)
return NULL; // indicates the m_time was not initialized!
*ptm = *ptmTemp;
return ptm;
}
else
return localtime(&m_time);
}
BOOL CTime::GetAsSystemTime(SYSTEMTIME& timeDest) const
{
struct tm* ptm = GetLocalTm(NULL);
if (ptm == NULL)
return FALSE;
timeDest.wYear = (WORD) (1900 + ptm->tm_year);
timeDest.wMonth = (WORD) (1 + ptm->tm_mon);
timeDest.wDayOfWeek = (WORD) ptm->tm_wday;
timeDest.wDay = (WORD) ptm->tm_mday;
timeDest.wHour = (WORD) ptm->tm_hour;
timeDest.wMinute = (WORD) ptm->tm_min;
timeDest.wSecond = (WORD) ptm->tm_sec;
timeDest.wMilliseconds = 0;
return TRUE;
}
第3节 MFC六大关键技术
MFC所有的封装类一共有200多个,但是MFC的内部技术不只是简单的封装。MFC的内部总共有六大黑箱技术架构起整个MFC的开发平台。六大关键技术的目的是为了提高开发效率,开发者只要在局部作简单修改,即可处理大部分窗口事物。MFC的六大关键技术包括MFC程序的初始化过程、运行时类型识别(RTTI)、动态创建、永久保存、消息映射和消息传递。
新建一个Win32程序“MFC32”,手动升级到MFC程序,演示“MFC初始化过程”的原理。
1)新建“Win32 Application”列表项,名称MFC32;
2)选中“A simple Win32 application”;
3)finish,单击WinMain函数并修改代码;
#include "stdafx.h"
class CMyApp:public CWinApp
{
BOOL InitInstance()
{
AfxMessageBox("由Win32工程转换而成MFC软件工程,测试MFC启动过程");
return TRUE;
}
};
CMyApp theApp;
/*
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MessageBox(NULL,"Win32软件启动方式,WinMain","Win32软件",0);
return 0;
}
*/
4)编译,错误;
5)没有包含CWinApp类所在的头文件;
6)在FileView中双击“StdAfx.h”,去掉Win32头文件并添加MFC头文件;
6)编译,错误;
7)没有链接到MFC代码库;
执行Project,Setting命令,选择Use MFC in a Static Library;
8)编译;
9)CWinApp::InitInstance虚函数与WinMain函数的关系;
把光标停放在CMyApp类中的InitInstance函数处,按F9设置断点,再按F5运行到断点处。从Variables的调试窗口的下拉列表中,可以看到MFC执行程序的启动过程。操作系统内核(kernel32.dll)先调用WinMain函数,在WinMain函数中再调用CWinApp派生类中的InitInstance函数;
10)在AfxWinMain函数中,发现其中pThread和pApp都是指向theApp全局变量地址的指针变量。通过基类指针执行pThread->InitInstance()时,会自动回调到派生类中,因为该函数是虚函数;
11)任何应用程序启动的过程,都是从操作系统内核(kernel32.dll)调用可执行文件的主函数开始的。
12)不借助“MFC AppWizard”,手工建立MFC程序步骤;
(1)从CWinMain类派生一个应用程序框架;
(2)使用派生类定义的全局变量(theApp);
(3)在预定义头文件“stdafx.h”中禁用“Windows.h”,取而代之的是MFC头文件;
(4)编译设置选择“Use MFC in a Static Library”;
(5)在CWinApp派生类中,重写InitInstance函数作为程序启动代码。
第4节 Win32消息处理机制
在回调函数中通过分支语句对消息进行解析和处理,就是Win32程序的消息处理机制;
1)建立Win32应用程序;
2)工程名Test32;
3)File->New,添加资源脚本;Resource Scrip,资源名Test32;
4)关闭正在编辑的窗口;
5)Resource Scrip的根结点,Insert;
6)选中Dialog,New;
7)修改WinMain函数;
// Test32.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "resource.h"
#include <stdio.h>
BOOL CALLBACK theProc(
HWND hwndDlg, // handle to dialog box
UINT uMsg, // message
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
if(uMsg==WM_COMMAND)
{ //判断消息类型
if(wParam==IDCANCEL||wParam==IDOK)
{ //判断按钮ID
EndDialog(hwndDlg,wParam);
return TRUE;
}
}
return FALSE;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
::DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1),NULL,theProc);
return 0;
}
8)编译;单击cancel或OK都可以关闭对话框;
9)在关闭Visual C++ 6.0时可能会弹出提示;
说明工作区的编译列表发生了一些变化,是否保存;因为插入资源脚本时,编译列表中增加了一个“.rc”文件;
10)继续修改消息回调函数,增加一些消息的种类;
BOOL CALLBACK theProc(HWND hwndDlg,UINT uMsg,WPARAM wParam, LPARAM lParam)
{
if(uMsg==WM_COMMAND) //单击按钮消息
{
if(wParam==IDCANCEL||wParam==IDOK)
{
return EndDialog(hwndDlg,wParam);
}
}
if(uMsg==WM_LBUTTONDOWN) //单击鼠标左键消息
{
char s[100];
sprintf(s,"x=%d,y=%d",LOWORD(lParam),HIWORD(lParam));
MessageBox(NULL,s,"Test32",MB_OK);
return TRUE;
}
if(uMsg==WM_PAINT) //窗口绘图消息
{
PAINTSTRUCT ps;
HDC hdc = ::BeginPaint(hwndDlg,&ps);
RECT rect;
GetClientRect(hwndDlg,&rect);
Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom);
::EndPaint(hwndDlg,&ps);
return TRUE;
}
return FALSE;
}
11)编译并运行;
第5节 MFC消息映射机制
第6节 使用“MFC应用程序向导”建立MFC工程
第7节 消息传递
第8节 解码消息映射机制
第4章 对话框程序
讲解模式对话框和非模式对话框的区别和调用方法,介绍对话框的关闭过程和常用回调函数。总体介绍MFC类库内容,讲解其中最重要的两大类库内部成员函数的功能。
第1节 模式对话框和非模式对话框
对话框主要分为两个类型,即模式对话框和非模式对话框。当模式对话框弹出后,无法操作它的父窗口或者上一级窗口,直到关闭该对话框为止。
使用MFC应用程序向导,建立Test,基于对话框的工程。
1)在资源试图中,MFC程序向导生成了一个主对话框模版,在其中添加2个按钮;
2)按钮属性;
ID | Caption | 用于 |
---|---|---|
IDC_ABOUT | 关于 | 测试 模式对话框 |
IDC_CHAT | 聊天 | 测试 非模式对话框3)在资源视图中,Dialog单击右键,Insert Dialog,插入一个对话框; |
4)修改属性;ID:ABOUTDLG,Caption:关于本软件;
5)选中对话框资源,执行View->Wizard,打开类向导。
类向导自动弹出创建新类的对话框,提示内容:IDD_ABOUTDLG是一个新的对话框资源,需要创建一个与它关联的类。选中Create a new class单击,OK,创建新对话框的关联类;
6)填写类名CAboutDlg后OK;完成对话框类的创建;
7)双击主对话框中的“关于”按钮建立消息映射函数;
8)编写OnAbout函数代码;
#include "AboutDlg.h"
void CTestDlg::OnAbout()
{
CAboutDlg dlg;
dlg.DoModal();
}
第2节 调用非模式对话框
非模式对话框的调用过程;
1)输入一个对话框
2)修改对话框属性ID:IDD_CHATDLG
3)执行View->ClassWizard命令,创建一个CDialog派生类CChatDlg;
4)双击聊天按钮,建立消息映射函数;
5)编写OnChat函数代码;
#include "ChatDlg.h"
void CTestDlg::OnChat()
{
CChatDlg* pDlg = new CChatDlg;
pDlg->Create(IDD_CHATDLG);
pDlg->ShowWindow(SW_SHOW);
}
6)编译运行,反复单机聊天按钮,弹出多个聊天对话框;
7)非模式对话框特点:
(1)在关闭模式对话框前,不阻挡对背景窗口的操作;
(2)使用CDialog::Creat函数创建非模式对话框,该函数是非阻塞函数,因此,必须在堆空间内New申请对话框类对象,不能在栈内申请对象。因为临时对象在离开函数体时会自动销毁;
例如,以下代码是错误的,因为申请的dlg就是临时栈内变量;
CChatDlg dlg;
dlg.Creat(IDD_CHATDLG);
8)模式对话框特点;
(1)在关闭模式对话框前,无法操作背景窗口,除非本身就是主窗口;
(2)使用CDialog::DoModal函数弹出模式对话框,该函数执行过程为阻塞函数;因此在栈内或者堆内申请对话框的对象都可以,申请栈内变量开发和运行效率高一些;
第3节 对话框的常用回调函数
无论是模式对话框还是非模式对话框,都可以直接申请基类CDialog对象来创建了创建CDialog派生类的原因是,在派生类中可以接收对话框的消息映射和虚函数回调,响应来自用户的操作和系统事件;
1)类视图主对话框右键,选择Add WindowsMessage Handle;
2)选中WM_CREATE单击Add Handle;
3)WM_CREATE移到右边,已添加消息映射函数;
4)单击Edit Existing,修改OnCreate函数代码;
5)编译运行,对话框弹出之前标题被修改;
6)修改OnInitDialog函数代码;
7)编译运行,可以判断,OnCreate函数的回调时间早于OnInitDialog;
8)修改代码;测试;
OnInitDialog函数中子窗口都已创建完成,可以对任意子窗口进行初始化操作;
OnCreate函数中,对话框中的控件子窗口还未创建,不可以操作控件窗口;
第4节 对话框程序的关闭过程
可以实现单击系统关闭按钮时最小化或者隐藏窗口的功能。详见书籍P90。
第5节 MFC类库介绍
第6节 CWnd类
第7节 CWinApp类
调用MFC全局函数AfxMessageBox弹出消息框的标题文字,就是采用CWinApp类成员变量m_pszAppName中记录的文字。
。。。
第8节 CWinApp类的应用
使对话框的背景为白色,静态文本控件中的文字为红色;
。。。
加载系统图标
。。。
第9节 读/写配置文件(.ini)
配置文件一般扩展名是“.ini”,是一种用于简易存储数据的文本文件。
1)“Tp”工程,为主对话框类添加一个WM_DESTROY的消息映射函数;
2)修改该消息映射函数代码;
3)再修改主对话框的初始化函数OnInitDialog;
4)编译运行;把主窗口拖放到一个位置上之后再退出程序,关闭对话框时的位置已经被记录在配置文件中了;重新启动对话框程序后位置和关闭时是相同的。默认是保存在Windows目录下和可执行文件名相同的INI文件;
5)打开文件,可根据需要修改其中的配置;可以把中括号内的文字比作是C++的一个类名,每个等号左边的文字就是一个类内的成员变量;
6)CWinApp类封装的API函数包括GetPrivateProfileInt、GetPrivateProfileString、WritePrivateProfileInt、WritePrivateProfileString;
7)在InitInstance函数中添加一行代码,即可使配置数据保存到注册表中,而不再保存在INI文件中;
8)编译运行;执行 开始-运行-regedit-确定,打开注册表编辑器;
9)配置数据已经保存在注册表中了。
第5章 对话框组合
讲解登录对话框与主对话框的组合、权限管理与登录对话框的组合以及数据录入对话框的组合等。介绍对话框的分类,以及CFile类和CDialog类的功能。
第1节 登录对话框与主对话框组合
使用MFC应用程序向导,创建工程名为QQ的对话框程序。
1)在资源试图中的Dialog上右键,选择Insert Dialog,添加一个新的对话框资源作为登录对话框;
2)修改对话框ID为IDD_LOGIN_DLG,标题为登录;
3)添加控件修改属性;
控件类型 | ID | Captioon | Style |
---|---|---|---|
Static Text | IDC_STATIC | 账号: | |
Edit Box | IDC_NAME | ||
Static Text | IDC_STATIC | 密码: | |
Edit Box | IDC_PASS | Password | |
Button | IDOK | 登录 | |
Button | IDCANCEL | 退出 |
4)在登录对话框上单击右键,选择ClassWizard;
5)通过类向导为IDD_LOGIN_DLG建立一个对话框类CLoginDlg;
6)在登录对话框资源中双击登录按钮,建立消息映射函数OnOK并编写代码;
void CLoginDlg::OnOK()
{
CString szName,szPass;
GetDlgItemText(IDC_NAME,szName);
GetDlgItemText(IDC_PASS,szPass);
szName.MakeLower(); //用户名不区分大小写
if(szName=="admin" && szPass=="123456")
CDialog::OnOK(); //关闭对话框
else
{
AfxMessageBox("账号或密码错误,请重新输入");
SetDlgItemText(IDC_NAME,"");
SetDlgItemText(IDC_PASS,"");
GetDlgItem(IDC_NAME)->SetFocus(); //设置焦点,或者,thi)->SetFocus();
}
}
7)修改App类中的进程启动函数InitInstance的代码;
#include "LoginDlg.h"
BOOL CQQApp::InitInstance()
{
CLoginDlg ldlg;
if(ldlg.DoModal()==IDCANCEL)
return FALSE; //单击退出按钮结束进程;
CQQDlg dlg;
dlg.DoModal();
return FALSE;
}
8)在主对话框资源内添加一个按钮,ID为IDC_LOGIN,标题重新登录;
9双击重新登录的登录,建立消息映射函数,并编写代码;
#include "LoginDlg.h"
void CQQDlg::OnLogin()
{
ShowWindow(SW_HIDE);
CLoginDlg dlg;
if(dlg.DoModal()==IDCANCEL)
OnCancel(); //关闭主对话框并推出程序
else
ShowWindow(SW_SHOW);
}
10)编译运行,测试代码;
在CLoginDlg::OnOK函数中,只有输入正确的用户名和密码才能执行基类函数CDialog::OnOK,结束阻塞函数ldlg.Modal,如果单机确定按钮,按钮执行的基类函数CDialog::OnCancel,则结束阻塞函数Idlg.DoModal并返回IDCANCEL。在InitInstance函数中根据返回值判断,如果是IDCANCEL则程序不进入主对话框而直接退出。
第2节 权限管理与登录对话框组合
高级权限可以添加盒删除账号,普通权限则不能。
1)在类视图中双击CQQApp类,修改头文件代码;
添加一个结构体SUser用于存储权限管理数据,并在CQQApp类中添加一个类成员变量;
struct SUser
{ //登录用户信息结构体
char sName[20];
char sPass[20];
int sPrior[8];
};
class CQQApp : public CWinApp
{
public:
SUser m_user; //用于保存当前登录用户信息
CQQApp();
DECLARE_MESSAGE_MAP()
};
2)添加一个新的对户口资源,ID为IDD_PRIOR_DLG,标题为权限管理;
3)添加控件,修改控件属性;
控件类型 | ID | Caption | Styles |
---|---|---|---|
Static Text | IDC_STATIC | 账号: | |
Edit | IDC_NAME | ||
Static Text | IDC_STATIC | 密码: | |
Edit | IDC_PASS | ||
Static Text | IDC_STATIC | 权限: | |
Combo Box | IDC_PRIOR | Type:Drop List | |
Button | IDC_ADD | 添加 | |
Button | IDC_DEL | 删除 | |
Button | IDC_MOD | 修改 | |
List Control | IDC_LIST | View_Report |
4)通过类向导为IDD_PRIOR_DLG建立一个对话框类CPriorDlg;
5)在类视图中的CPriorDlg对话框类上单击鼠标右键,在弹出的快捷菜单中,选择Add Member Function命令,添加一个普通成员函数;
6)填写函数名为ReadUsers,返回值是void,然后单击OK按钮完成添加函数;
7)修改新添加的类成员函数的代码;
void CPriorDlg::ReadUsers(CListCtrl *pList)
{ //从文件中读取用户列表
CFile file;
if(!file.Open("./Users.dat",CFile::modeRead))
return;
SUser u;
int i = 0;
while(file.Read(&u,sizeof(u))>0 )
{
pList ->InsertItem(i,u.sName);
pList ->SetItemText(i,1,u.sPass);
pList ->SetItemText(i,2,u.sPrior);
++i;
}
file.Close();
}
8)在权限管理对话框中,添加WM_INITDIALOG的消息映射函数OnInitDialog;
9)按Edit Code,修改函数代码;
BOOL CPriorDlg::OnInitDialog()
{
CDialog::OnInitDialog();
CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST);
pList ->InsertColumn(0,"账户",0,100);
pList ->InsertColumn(1,"密码",0,100);
pList ->InsertColumn(2,"权限",0,100);
CComboBox* pComb = (CComboBox*)GetDlgItem(IDC_PRIOR);
pComb ->AddString("普通");
pComb ->AddString("高级");
pComb ->SetCurSel(0);
ReadUsers(pList);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
10)为CPriorDlg类添加WM_DESTROY的消息映射函数;
11)修改消息映射函数CPriorDlg::OnDestroy() 的代码;
void CPriorDlg::OnDestroy()
{ //在权限对话框关闭时自动保存用户列表
CDialog::OnDestroy();
CFile file;
if(!file.Open("./Users.dat",CFile::modeCreate|CFile::modeWrite))
return;
CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST);
int i = 0,nCount = pList ->GetItemCount();
SUser u;
while(i<nCount)
{
pList ->GetItemText(i,0,u.sName,sizeof(u.sName));
pList ->GetItemText(i,1,u.sPass,sizeof(u.sPass));
pList ->GetItemText(i,2,u.sPrior,sizeof(u.sPrior));
file.Write(&u,sizeof(u));
++i;
}
file.Close();
}
void CPriorDlg::OnDestroy()
{ //在权限对话框关闭时自动保存用户列表
CDialog::OnDestroy();
CFile file;
if(!file.Open("./Users.dat",CFile::modeCreate|CFile::modeWrite))
{
AfxMessageBox("保存文件时失败!");
return;
}
CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST);
int i = 0,nCount = pList ->GetItemCount();
SUser u;
while(i<nCount)
{
pList ->GetItemText(i,0,u.sName,sizeof(u.sName));
pList ->GetItemText(i,1,u.sPass,sizeof(u.sPass));
u.sPrior = pList ->GetItemText(i,2)=="高级";
file.Write(&u,sizeof(u));
++i;
}
file.Close();
}
12)在权限管理对话框中,分别双击增、删、改3个按钮,建立消息映射函数;
void CPriorDlg::OnAdd()
{
SUser u;
GetDlgItemText(IDC_NAME,u.sName,sizeof(u.sName));
//账号登录时不区分大小写
strlwr(u.sName);
CListCtrl*pList=(CListCtrl*)GetDlgItem(IDC_LIST);
int i=0,nCount=pList->GetItemCount();
while(i<nCount)
{
if(pList->GetItemText(i,0)==u.sName)
{
CString str;
str.Format("用户\"%s\"已经存在!",u.sName);
AfxMessageBox(str);
return;
}
i++;
}
GetDlgItemText(IDC_PASS,u.sPass,sizeof(u.sPass));
GetDlgItemText(IDC_PRIOR,u.sPrior,sizeof(u.sPrior));
pList->InsertItem(nCount,u.sName);
pList->SetItemText(nCount,1,u.sPass);
pList->SetItemText(nCount,2,u.sPrior);
}
void CPriorDlg::OnDel()
{
CListCtrl*pList=(CListCtrl*)GetDlgItem(IDC_LIST);
POSITION pos=pList->GetFirstSelectedItemPosition();
int nSel=pList->GetNextSelectedItem(pos);
if(nSel<0)
{
AfxMessageBox("请选择一个用户再删除!");
return;
}
//保留一个超级管理用户名
CString szName=pList->GetItemText(nSel,0);
if(szName=="admin")
return;
CString str;
str.Format("确定删除账号\"%s\"吗?",szName);
if(AfxMessageBox(str,MB_YESNO)==IDYES)
pList->DeleteItem(nSel);
}
void CPrirDlg::OnMod()
{
CListCtrl*pList=(CListCtrl*)GetDlgItem(IDC_LIST);
POSITION pos=pList->GetFirstSelectedItemPosition();
int nSel=pList ->GetNextSelectedItem(pos);
if(nSel<0)
{
AfxMessageBox("请选择一个用户再修改!");
return;
}
SUser u:
GetDlgItemText(IDC_PASS,u.sPass,sizeof(u.sPass));
pList->SetItemText(nSel,1,u.sPass);
CString szName=pList->GetItemText(nSel,0);
//如果是admin账户则只允许修改密码
if(szName=="admin")
return;
GetDlgItemText(IDC_PRIOR,u.sPrior,sizeof(u.sPrior));
pList->SetItemText(nSel,2,u.sPrior);
}
13)在主对话框中添加一个按钮,ID为IDC_PRIOR,标题为权限管理;
14)双击权限管理按钮建立消息映射函数,并编写代码;
#include "PriorDlg.h"
void CQQDlg::OnPrior()
{
CPriorDlg dlg;
dlg.DoModal();
}
15)在类视图中的CLoginDlg对话框类上单击鼠标右键,选择Add Member Function命令,添加一个成员函数;
16)单击OK,添加CheckUser函数并修改代码;
extern CQQApp theApp
BOOL CLoginDlg::CheckUser(CString szName, CString szPass)
{
CFile file;
if(!file.Open("./Users.dat",CFile::modeRead))
{
CreateUser(); //如果用户不存在则创建文件,再重新打开
if(!file.Open("./Users.dat",CFile::modeRead))
return FALSE;
}
//把登录用户记录在theApp.m_user;
Suser &u=theApp.m_user;
while(file.read(&u,sizeof(u))>0)
{
if(szName==u.sName)
return szPass==u.sPass;
}
file.Close();
return FALSE;
}
17)在视图中的CLoginDlg对话框上单击鼠标右键,选择Add Member Function命令,添加一个成员函数;
18)单击OK按钮添加普通成员函数并修改代码;
void CLoginDlg::CreateUser()
{
CFile file;
if(!file.Open("./Users.dat",CFile::modeCreate|CFile::modeWrite))
return;
SUser u={"admin","高级"};
file.Write(&u,sizeof(u));
file.Close();
}
19)修改CLoginDlg中的OnOK代码;
void CLoginDlg::OnOK()
{
CString szName,szPass;
GetDlgItemText(IDC_NAME,szName);
GetDlgItemText(IDC_PASS,szPass);
szName.MakeLower(); //用户名不区分大小写
if(CheckUser(szName,szPass))
{ //如果密码检查通过则关闭登录对话框
CDialog::OnOK();
return;
}
AfxMessageBox("账号或密码错误,请重新输入");
SetDlgItemText(IDC_NAME,"");
SetDlgItemText(IDC_PASS,"");
GetDlgItem(IDC_NAME)->SetFocus(); //设置焦点,或者,thi)->SetFocus();
}
20)修改主对话框初始化函数OnInitDialog的代码;
extern CQQApp theApp
BOOL CQQDlg::OnInitDialog()
{
CDialog::OnInitDialog();
CString str=theApp.m_user.sPrior
GetDlgItem(IDC_PRIOR)->EnableWindow(str!="普通");
str="-"+str;
str=theApp.m_user.sName+str;
SetWindowText(str);
……
21)修改主对话框重新登库按钮的消息映射函数代码;
#include "LoginDlg.h"
void CQQDlg::OnLogin()
{
ShowWindow(SW_HIDE);
CLoginDlg dlg;
if(dlg.DoModal()==IDCANCEL)
OnCancel(); //关闭主对话框并推出程序
else
{
CString str=theApp.m_user.sPrior;
GetDlgItem(IDC_PRIOR)->EnableWindow(str!="普通");
str="-"+str;
str=theApp.m_user.sName+str;
SetWindowText(str);
ShowWindow(SW_SHOW);
}
}
22)编译并运行,测试代码;;
第3节 数据录入与对话框组合
第4节 系统对话框
第5节 CFile类
第6节 CDialog类
第6章 基础控件
介绍Windows基础控件和常用的控件CListCtrl常用成员,包括CComboBox类、CEdit类和CButton类等。讲解通过类向导建立控件型关联变量、通过函数建立控件型关联变量以及常用控件的数值型关联变量的方法。
第1节 Windows基础控件
在Visusal C++对话框资源编辑框界面中,Controls工具栏列出的就是所有Windows基础控件。
Picturn图片控件:CStatic类,显示图片
Static Text静态文本控件:CStatic类显示不可以编辑的文字
Edit Box编辑控件:CEdit类提供文字编辑、复制、粘贴等功能
Group Box分组控件:CButton类将功能相近的控件圈括在一个区域内
Button命令按钮:CButton类单击按钮发出命令
Check Box复选控件:CButton类提供多选的选项功能
Radio Button:CButton类提供单选功能
Combo Box组合控件:CComboBox类提供可以隐藏下拉列表控件
List Box列表框:CListBox类以列表方式显示多行数据,支持增删改以及单选或多选功能
Horizontal scrollbar横向滚动栏:CScrollBar类
Vertical scrollbar纵向滚动栏:CScrollBar类
Spin旋转按钮:CSpin Button Ctrl类,和编辑框结合,辅助增减控件内的数字
Progress进度栏:CprogressCtrl类提供耗时操作的进度显示
Slider滑块控件:CSliderCtrl类提供通过拖动滑块跳跃选取数字或者进度
Hot Key热键控件:CHotKeyCtrl类用于显示和修改热键设置
List Control列表视图控件:CList Ctrl类提供按列显示数据集合的功能,支持增删改查等功能
Tree Control树形控件:CTreeCtrl类显示和处理多层次数据结构
Tab Control标签控件:CTabCtrl类用于管理多个分页窗口,每一个分页窗口都包含不同的控件和信息
Animate动画控件:CAnimateCtrl类提供简单AVI动画播放功能
Rich Edit高级编辑控件:CRich Edit Ctrl类不仅提供文本编辑功能,还提供不同段落的字体和颜色
Date Time Picker日期和时间控件:CDateTimeCtrl类编辑和显示日期和时间信息功能
Month Calendar月历控件:Cmonth CalCtrl类提供一个简易月历界面,用户可以非常方便的选择日期
IP Address IP地址控件:CIPAddressCtrl类提供IP 地址的显示和编辑
ComboBoxEx扩展组合框:CcomboBoxEx类支持存储图形列表中的图像
每一个控件都可以与一个MFC控件类的对象关联,通过控件类的成员函数操作控件。
第2节 通过类向导建立控件类型关联变量
通过类向导Class Wizard可以建立一个控件类型的成员变量,并且这个成员变量与一个指定ID的控件关联。通过这个成员变量调用控件类的成员函数可以操作与之关联的控件,这个成员变量就是控件型关联变量。
通过MFC应用程序向导,创建一个工程名为vc的对话框程序。
1)修改对话框的外观和字体并添加一些控件;
2)添加控件并修改控件属性;
控件类型 | ID | Caption | Styles |
---|---|---|---|
Static Text | IDC_STATIC | 工号: | |
Edit Box | IDC_NUMB | ||
Static Text | IDC_STATIC | 姓名: | |
Edit Box | IDC_NAME | ||
Static Text | IDC_STATIC | 部门: | |
Combo Box | IDC_DEPT | Type:Drop List去掉Sort属性 | |
Button | IDC_ADD | 添加 | |
Button | IDC_DEL | 删除 | |
Button | IDC_MOD | 修改 | |
List Control | IDC_LIST | View:Report |
3)在对话框上单击鼠标右键,选择ClassWizard命令;
4)在Member Variables 分页中,选中IDC_LIST列表项再单击Add Variables按钮;其中,在Category下拉列表框中选择Control,表示要添加的是控件型关联变量,在Variable type下拉列表框中选择CList Ctrl,表示要建立的关联变量的类型。在Member variable name文本框中填写变量名称m_list后单击OK按钮,完成添加控件型关联变量。
5)添加关联变量的结果
添加关联变量的结果。在对话框类的头文件vcDlg.h中增加一行代码CListCtrl m_list,就是刚才添加的CListCtrl类型成员变量,在源文件vcDlg.cpp中也增加了一行代码DDX_Control(pDX,IDC_LIST,m_list),表示将ID为IDC_LIST的列表控件与成员变量m_list关联。
6)按照以上方法,通过类向导为组合控件IDC_DEPT也建立一个控件型关联变量;
7)创建多个关联变量,若不对可单击DeleteVariable按钮删除。
8)在头文件和源文件中处理变量;
9)修改对话框初始化函数代码;
默认为
修改为
BOOL CVcDlg::OnInitDialog()
{
CDialog::OnInitDialog();
m_list.InsertColumn(0,"工号",0,100);
m_list.InsertColumn(1,"姓名",0,100);
m_list.InsertColumn(2,"部门",0,100);
m_combo.AddString("请选择部门");
m_combo.AddString("行政部");
m_combo.AddString("财务部");
m_combo.AddString("市场部");
m_combo.AddString("测试部");
m_combo.AddString("开发部");
m_combo.SetCurSel(0);
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
return TRUE; // return TRUE unless you set the focus to a control
}
问题现象:使用CComboBox 控件下拉列表中本应该会显示3个或者更多的对象,但是程序编译后一个都不显示或者显示不全;
解决方法:在画界面设计处,选中 CComboBox
,鼠标移到的下拉箭头上,当光标为双向箭头时。再点击,出现八个小方框围成的矩形区域,将其拉到期望的大小。重新编译程序即可。
10为增删改建立消息映射函数;
添加
void CVcDlg::OnAdd()
{
int nSel = m_combo.GetCurSel();
if(!nSel)
{
AfxMessageBox("请先选择部门再添加!");
return;
}
CString str;
GetDlgItemText(IDC_NUMB,str);
int nCount = m_list.GetItemCount();
m_list.InsertItem(nCount,str);
GetDlgItemText(IDC_NAME,str);
m_list.SetItemText(nCount,1,str);
m_combo.GetLBText(nSel,str);
m_list.SetItemText(nCount,2,str);
}
删除
void CVcDlg::OnDel()
{
POSITION pos = m_list.GetFirstSelectedItemPosition();
int nSel = m_list.GetNextSelectedItem(pos);
if(nSel<0)
{
AfxMessageBox("请选择列表中的一条信息再删除!");
return;
}
CString str = m_list.GetItemText(nSel,0);
str = "确定要删除" + str;
str +="号信息吗?";
if(AfxMessageBox(str,MB_YESNO)==IDYES)
m_list.DeleteItem(nSel);
}
修改
void CVcDlg::OnMod()
{
POSITION pos = m_list.GetFirstSelectedItemPosition();
int nSel = m_list.GetNextSelectedItem(pos);
if(nSel<0)
{
AfxMessageBox("请选择列表中的一条信息再修改!");
return;
}
CString str = m_list.GetItemText(nSel,0);
str = "确定要修改" + str;
str +="号信息吗?";
if(AfxMessageBox(str,MB_YESNO)==IDNO)
return;
GetDlgItemText(IDC_NAME,str);
m_list.SetItemText(nSel,1,str);
m_combo.GetWindowText(str);
m_list.SetItemText(nSel,2,str);
}
11)编译运行,测试代码;
本示例演示通过类向导,对多种控件建立控件关联变量。控件关联变量比GetDlgItem函数在使用上方便很多,关联变量建立完之后就可以长期使用,每次调用前不需要再重新关联。
第3节 通过函数建立控件型关联变量
在CWnd类中包含一系列将窗口与变量关联的函数,如下:
1)CWnd::Attach,将一个窗口句柄嫁接到一个CWnd类型变量中;
2)CWnd::Detach,移除嫁接到CWnd变量中的句柄;
3)CWnd::SubclassWindow,子类化一个窗口句柄到CWnd派生类变量中(但不要把句柄关联进入CWnd对象中,而且还要将窗口的消息映射到CWnd的子类中);
4)CWnd::UnsubclassWindow,解除子类化;
5)CWnd::SubclassDlgItem,根据窗口ID子类化对应的窗口。
使用MFC应用程序向导,建立一个工程名为vs的对话框程序。
1)修改外观字体,添加控件;
2)控件属性;
控件类型 | ID | Caption | Styles |
---|---|---|---|
Static Text | IDC_STATIC | 工号: | |
Edit Box | IDC_NUMB | ||
Static Text | IDC_STATIC | 姓名: | |
Edit Box | IDC_NAME | ||
Static Text | IDC_STATIC | 部门: | |
Combo Box | IDC_DEPT | Type:Drop List去掉Sort属性 | |
Button | IDC_ADD | 添加 | |
Button | IDC_DEL | 删除 | |
Button | IDC_MOD | 修改 | |
List Control | IDC_LIST | View:Report |
3)在对话框类的头文件中,加入4个控件类型的变量;
class CVsDlg : public CDialog
{
CEdit m_numb;
CEdit m_name;
CComboBox m_combo;
CListCtrl m_list;
4)修改对话框初始化函数,通过调用函数建立控件窗口和变量的关联;
BOOL CVsDlg::OnInitDialog()
{
CDialog::OnInitDialog();
m_numb.SubclassDlgItem(IDC_NUMB,this);
m_name.SubclassDlgItem(IDC_NAME,this);
HWND hCombo,hList;
GetDlgItem(IDC_DEPT,&hCombo);
m_combo.SubclassWindow(hCombo);
GetDlgItem(IDC_LIST,&hList);
m_list.Attach(hList);
m_list.SetExtendedStyle(LVS_EX_GRIDLINES|LVS_EX_FULLROWSELECT);
m_list.InsertColumn(0,"工号",0,100);
m_list.InsertColumn(1,"姓名",0,100);
m_list.InsertColumn(2,"部门",0,100);
m_combo.AddString("请选择部门");
m_combo.AddString("行政部");
m_combo.AddString("财务部");
m_combo.AddString("市场部");
m_combo.AddString("测试部");
m_combo.AddString("开发部");
m_combo.SetCurSel(0);
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
}
5)添加WM_DESTROY消息映射函数并修改代码;
void CVsDlg::OnDestroy()
{
CDialog::OnDestroy();
m_list.Detach();
m_combo.UnsubclassWindow();
}
6)为增删改3个按钮建立消息映射函数并修改代码;
添加
void CVsDlg::OnAdd()
{
int nSel = m_combo.GetCurSel();
if(!nSel)
{
AfxMessageBox("请先选择部门再添加!");
return;
}
CString str;
m_numb.GetWindowText(str);
int nCount=m_list.GetItemCount();
m_list.InsertItem(nCount,str);
m_name.GetWindowText(str);
m_list.SetItemText(nCount,1,str);
m_combo.GetLBText(nSel,str);
m_list.SetItemText(nCount,2,str);
}
删除
void CVsDlg::OnDel()
{
POSITION pos = m_list.GetFirstSelectedItemPosition();
int nSel = m_list.GetNextSelectedItem(pos);
if(nSel<0)
{
AfxMessageBox("请选择列表中的一条信息再删除!");
return;
}
CString str = m_list.GetItemText(nSel,0);
str = "确定要删除" + str;
str +="号信息吗?";
if(AfxMessageBox(str,MB_YESNO)==IDYES)
m_list.DeleteItem(nSel);
}
修改
void CVsDlg::OnMod()
{
POSITION pos = m_list.GetFirstSelectedItemPosition();
int nSel = m_list.GetNextSelectedItem(pos);
if(nSel<0)
{
AfxMessageBox("请选择列表中的一条信息再修改!");
return;
}
CString str = m_list.GetItemText(nSel,0);
str = "确定要修改" + str;
str +="号信息吗?";
if(AfxMessageBox(str,MB_YESNO)==IDNO)
return;
m_name.GetWindowText(str);
m_list.SetItemText(nSel,1,str);
m_combo.GetWindowText(str);
m_list.SetItemText(nSel,2,str);
}
7)编译运行,测试代码;
实际上类向导建立的关联变量,内部也是调用子类化函数实现的,但是使用向导建立关联变量更加方便一些。因此,之后主要使用类向导建立关联变量。
第4节 通过类向导建立数值型关联变量
所有Windows基础控件都可以建立控件型关联变量,有一些控件不但可以建立控件型变量,而且可以建立数值型关联变量;
在第3节中,当通过类向导建立组合控件的关联变量(m_combo)时,在Category下拉列表框中包含Value和Control两项。选择Control表示建立控件型关联变量,选择Value表示建立数值型关联变量。大部分基础控件不支持数值型关联变量,只支持控件型关联变量,比如按钮、列表控件、树形控件;只有少部分控件才支持建立数值型关联变量,这些控件包括编辑框、下拉列表框、单选按钮、多选按钮、列表框等。
使用MFC应用程序向导,创建一个工程名为cal的对话框程序。
1)修改对话框标题为计算器,并添加一些控件;
2)修改主对话框的控件属性;
控件类型 | ID | Caption | Styles |
---|---|---|---|
Edit Box | IDC_LEFT | ||
Combo Box | IDC_OPER | Type:Drop List去掉Sort属性 | |
Edit Box | IDC_RIGHT | ||
Static Text | IDC_STATIC | ||
Edit Box | IDC_RESULT | ||
Button | IDOK | 计算 |
3)在下拉列表控件IDC_OPER的Data属性中输入默认数据;
4)执行View ClassWizard命令。在Member Variable分页中,选中IDC_LEFT控件ID再单击Add Variable按钮;
5)在类向导中为一个编辑控件建立数值型关联变量;在Category下拉列表框中,选择Value代表建立数值型关联变量;在Variable type下拉列表框选择int类型,在变量名称中输入m_nLeft,最后单击OK按钮关闭对话框,完成编辑框IDC_LEFT关联变量的创建;
6)用同样的方法为三个编辑框和一个下拉列表控件,全部建立int类型的关联变量;
7)双击计算按钮,建立消息映射函数并修改代码;
void CCalDlg::OnOK()
{
UpdateData();
switch(m_nOper)
{
case 0:
m_nResult=m_nLeft + m_nRight;
break;
case 1:
m_nResult=m_nLeft - m_nRight;
break;
case 2:
m_nResult=m_nLeft * m_nRight;
break;
case 3:
m_nResult=m_nLeft / m_nRight;
break;
case 4:
m_nResult=m_nLeft % m_nRight;
break;
}
UpdateData(FALSE);
}
8)编译并运行;
第5节 常用控件的数值型关联变量
创建一个工程名为vd的对话框程序,演示常用控件的数值型关联变量;
1)标题:员工信息;添加控件;
2)控件属性;
控件类型 | ID | Caption | Styles |
---|---|---|---|
Static Text | IDC_STATIC | 工号: | |
Edit Box | IDC_NUMB | ||
Static Text | IDC_STATIC | 姓名: | |
Edit Box | IDC_NAME | ||
Static Text | IDC_STATIC | 性别: | |
Radio Button | IDC_SEX | 男 | Group Tab Stop |
Radio Button | ID随机 | 女 | |
Group Box | IDC_STATIC | 学历 | |
Radio Button | IDC_EDUCA | 高中 | Group Tab Stop |
Radio Button | 随机ID | 大学 | |
Radio Button | 随机ID | 硕士 | |
Radio Button | 随机ID | 博士 | |
Group Box | IDC_STATIC | 外语 | |
Check Box | IDC_ENGL | 英语 | |
Check Box | IDC_JAPA | 日语 | |
Check Box | IDC_KOREA | 韩语 | |
Check Box | IDC_RUSS | 俄语 | |
Static Text | IDC_STATIC | 入职日期: | |
Data Time Picker | IDC_JOIN | ||
Button | IDC_ADD | 添加 | |
Button | IDC_DEL | 删除 | |
Button | IDC_MOD | 修改 | |
List Control | IDC_LIST |
3)在类向导中为除按钮以外的全部控件都建立一个关联变量(列表控件建立的都是控件型变量,其他控件建立的是数值型变量);
4)类向导在主对话框的头文件中自动添加的代码如下;
//{{AFX_DATA(CVdDlg)
enum { IDD = IDD_VD_DIALOG };
CListCtrl m_list;
BOOL m_bEngl;
BOOL m_bJapa;
COleDateTime m_dtJoin;
BOOL m_bKorea;
CString m_szName;
CString m_szNumb;
BOOL m_bRuss;
int m_nSex;
int m_nEduca;
//}}AFX_DATA
5)类向导在数据交换函数中自动增加的代码如下;
void CVdDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CVdDlg)
DDX_Control(pDX, IDC_LIST, m_list);
DDX_Check(pDX, IDC_ENGL, m_bEngl);
DDX_Check(pDX, IDC_JAPA, m_bJapa);
DDX_DateTimeCtrl(pDX, IDC_JOIN, m_dtJoin);
DDX_Check(pDX, IDC_KOREA, m_bKorea);
DDX_Text(pDX, IDC_NAME, m_szName);
DDX_Text(pDX, IDC_NUMB, m_szNumb);
DDX_Check(pDX, IDC_RUSS, m_bRuss);
DDX_Radio(pDX, IDC_SEX, m_nSex);
DDX_Radio(pDX, IDC_EDUCA, m_nEduca);
//}}AFX_DATA_MAP
}
6)类向导还在构造函数中自动添加如下代码(稍作修改);
CVdDlg::CVdDlg(CWnd* pParent /*=NULL*/)
: CDialog(CVdDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CVdDlg)
m_bEngl = TRUE; //将默认的FALSE修改为TRUE
m_bJapa = FALSE;
m_dtJoin = COleDateTime::GetCurrentTime();
m_bKorea = FALSE;
m_szName = _T("");
m_szNumb = _T("");
m_bRuss = FALSE;
m_nSex = 0; //将默认的-1修改为0
m_nEduca = 1; //将默认的-1修改为1
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
7)修改对话框初始化函数的代码;
BOOL CVdDlg::OnInitDialog()
{
CDialog::OnInitDialog();
m_list.InsertColumn(0,"工号",0,80);
m_list.InsertColumn(1,"姓名",0,80);
m_list.InsertColumn(2,"性别",0,60);
m_list.InsertColumn(3,"学历",0,80);
m_list.InsertColumn(4,"入职",0,80);
m_list.InsertColumn(5,"外语",0,100);
m_list.SetExtendedStyle(LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT);
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
}
8)为增删改3个按钮建立消息映射函数并修改代码;
添加
void CVdDlg::OnAdd()
{
UpdateData();
int nCount = m_list.GetItemCount();
m_list.InsertItem(nCount,m_szNumb);
m_list.SetItemText(nCount,1,m_szName);
m_list.SetItemText(nCount,2,m_nSex?"女":"男");
char *p[]={"高中","大学","硕士","博士"};
m_list.SetItemText(nCount,3,p[m_nEduca]);
CString str;
if(m_bEngl)
str+="英";
if(m_bJapa)
str+="日";
if(m_bKorea)
str+="韩";
if(m_bRuss)
str+="俄";
if(str.IsEmpty())
str+="无";
m_list.SetItemText(nCount,5,str);
str.Format("%d-%d-%d",m_dtJoin.GetYear(),m_dtJoin.GetMonth(),m_dtJoin.GetDay());
m_list.SetItemText(nCount,4,str);
}
删除
void CVdDlg::OnDel()
{
POSITION pos = m_list.GetFirstSelectedItemPosition();
if(!pos)
{
AfxMessageBox("请选择列表中的一条信息再删除!");
return;
}
int nSel = m_list.GetNextSelectedItem(pos);
CString str = m_list.GetItemText(nSel,0);
str = "确定要删除" + str + "号信息吗?";
if(AfxMessageBox(str,MB_YESNO)==IDYES)
m_list.DeleteItem(nSel);
}
修改
void CVdDlg::OnMod()
{
POSITION pos = m_list.GetFirstSelectedItemPosition();
if(!pos)
{
AfxMessageBox("请选择一行再删除!");
return;
}
int nSel = m_list.GetNextSelectedItem(pos);
UpdateData();
m_list.SetItemText(nSel,0,m_szNumb);
m_list.SetItemText(nSel,1,m_szName);
m_list.SetItemText(nSel,2,m_nSex?"女":"男");
char *p[]={"高中","大学","硕士","博士"};
m_list.SetItemText(nSel,3,p[m_nEduca]);
CString str;
if(m_bEngl)
str+="英";
if(m_bJapa)
str+="日";
if(m_bKorea)
str+="韩";
if(m_bRuss)
str+="俄";
if(str.IsEmpty())
str+="无";
m_list.SetItemText(nSel,5,str);
str.Format("%d-%d-%d",m_dtJoin.GetYear(),m_dtJoin.GetMonth(),m_dtJoin.GetDay());
m_list.SetItemText(nSel,4,str);
}
9)编译并运行,测试代码;
10)单选按钮的使用方法比较特殊,总结其特点如下;
(1)每一组单选按钮都必须而且只能有一个“组长”;
(2)“组长”就是指选择了“Group”属性的控件,一组单选按钮的“组长”必须是该组中ID最小的;
(3)通常在一组单选按钮中,第一个拖放到对话框的为“组长”,随后一次加入的是单选按钮作为“组员”;
(4)一组单选按钮中只有“组长”的ID是有用的,其他“组员”的ID随机生成即可;
(5)通过类向导建立关联变量时,只要对“组长”ID建立一个数值型关联变量即可。
第7章 GDI绘图技术
介绍绘图专用句柄HDC、CDC类及其派生类,讲解多种窗口绘图方式,GDI对象的使用方法,包括CPen类(画笔)、CBrush类(画刷)、CFont类(字体)、CBitmap类(位图)和CRgn类(区域)等。
第一节 绘图专用句柄HDC
HWND(Handle of Window,窗口句柄)专门用于窗口操作,被MFC封装于CWnd类中;
HDC(Handle of DC,设备环境句柄)是专门用于绘图的句柄,被MFC封装于CDC类中。
DC(Device Context)一般被称为设备上下文、设备环境或设备描述表。
通过HDC句柄绘图有三种方式,即标准客户区绘图、临时客户区绘图和非客户区绘图。
创建一个WIn32程序“DC32”。
1)File->New命令,选中Win32 Application;
2)选中A simple Win32 application,finish安成;
3)File->New,在Files分页中选择Resource Scrip列表项;
4)在资源视图中(Resource View)中,添加资源;
5)插入资源对话框,选中Dialog,单击New;
6)修改对话框ID为IDD_PAINT_DLG后,再修改WinMain和消息回掉函数代码。
第8章 图形软件开发
介绍内存设备环境CMemDC类的封装原理,讲解多种图形软件开发实用技术,包括图像透明技术、动画技术、透明动画、不规则窗口和双缓冲防闪烁技术等,讲解图层软件架构,介绍CDC类成员函数。
第9章 高级控件应用
介绍高级控件的开发方法,包括旋转按钮(CSpinButtonCtrl类)、标签控件(CTabCtrl类)、高级编辑控件(CRichEditCtrl类)以及树形控件(CTreeCtrl类)等。讲解分页技术以及属性表(CPropertySheet类)和属性页(CPropertyPage类)的开发方法,介绍控件的消息反射。
第10章界面装饰
讲解列表控件的列表项和标头图标设置、列表项选项、窗口颜色控制、自绘按钮、自绘组合控件等界面装饰相关技术。
第11章 自定义窗口
介绍手动创建控件、注册和创建自定义窗口,以及界面提示(CToolTipCtrl类)等。讲解自定义控件开发,包括按钮标签控件,以及滑块控件的二次开发。
第12章 视图与框架
讲解视图与框架的创建方法,以及多种由视图、分隔栏和框架结合生成的界面模型,包括Frame-View、Frame-Splitter、Frame-Splitter-Splitter以及MDIFrameWnd-MDIChildWnd等结构模型。
第13章 文档模版架构
介绍CFile、CArchive和CDocument等存储模型的进化过程,以及基于文档模版架构的序列化存储模式。介绍MFC六大关键技术,讲解动态创建、运行时类型识别以及命令传递技术内幕。
第14章 菜单与控制栏
介绍窗口菜单、上下文菜单以及自绘菜单的开发,讲解悬浮工具和文字工具栏、IE工具栏以及对话框栏和状态栏等。
第15章 MFC网络通信
介绍TCP/IP的层次,讲解简单UDP通信的和TCP通信的方式。讲解TCP和UDP通信协议的开发,以及TCP的短连接模式等。