MFC基础

    技术2024-12-29  27

    文章目录

    CString类的使用对象初始化对象基本操作对象拼接字符串比较字符串查找字符串替换与删除字符串提取字符串格式化 模态对话框 vs 非模态对话框内存分配的三种方式对话框之间的数据传递控件变量的绑定定时器的使用对话框响应响应鼠标及键盘消息全局热键注册控件自定义消息的响应

    CString类的使用

    对象初始化

    // 使用字符串常量进行初始化 CString str1; // 采用默认的构造函数进行初始化 CString str2(_T("hello")); // 带一个参数的构造函数 CString str3 = _T("hello"); // 带一个参数的构造函数 // 使用字符数组进行初始化 TCHAR szBuf[] = _T("ABC"); CString str4(szBuf); CString str5 = szBuf; // 使用字符指针进行初始化 TCHAR *pChar = _T("CHINA"); CString str6(pChar); CString str7 = pChar; // 使用CString对象进行初始化:调用的是拷贝构造函数 CString str8(str7); CString str9 = str7;

    对象基本操作

    // 获取字符串的长度: CString#GetLength()获取的是字符个数 CString str(_T("hello,kitty--中国")); int len = str.GetLength(); // UNICODE下字符个数是15; 多字节字符集: 17 // 是否为空 bool flag1 = str.IsEmpty(); // false bool flag2 = CString().IsEmpty(); // 无名对象: true // 大小写转换 str.MakeUpper(); // 将str对象中的字符全部转换为大写: 只能对英文字母 MessageBox(str); str.MakeLower(); // 将str对象中的字符全部转换为小写: 只能对英文字母 MessageBox(str); // 逆序 str.MakeReverse(); // 将字符串中的字符逆序:包括中文 MessageBox(str);

    对象拼接

    // 字符串的连接: +、+= CString str1 = TEXT("hello"); CString str2 = TEXT("kitty"); CString str3 = str1 + TEXT(",") + str2; // CString对象 + 字符串常量 + 字符串对象 MessageBox(str3); str3 += CString(TEXT(": china")); // 匿名对象CString("...") MessageBox(str3); TCHAR szBuf[] = TEXT("AAA"); // CString对象拼接字符数组: char类型或wchar_t类型 str3 += szBuf; MessageBox(str3); TCHAR *pTChar = TEXT("BBB"); // CString对象拼接字符指针: char *或 wchar_t *类型 str3 += pTChar; MessageBox(str3);

    字符串比较

    // 字符串比较 // 常用的:==、!=、Compare():区分大小写、CompareNoCase():不区分大小写 // 不常用的:>、<、>=、<= CString s1(TEXT("ABC")); CString s2 = TEXT("BBB"); TCHAR szBuf[] = TEXT("ABC"); TCHAR *pTChar = TEXT("abc"); bool flag = s1 == s2; // false flag = s1 == szBuf; // true:运算符重载 flag = s1 == pTChar; // false:==区分大小小 int i = 0; i = s1.Compare(szBuf); // 0:返回0表示字符相等 i = s1.Compare(pTChar); // -1:返回非0表示字符串不相等: 正大负小 i = s1.CompareNoCase(szBuf); // 0 i = s1.CompareNoCase(pTChar);// 0 // Compare()、CompareNoCase()是比较大小,返回值能够表示:大于(>0)、等于(=0)、小于(<0)

    字符串查找

    // 字符串查找 // Find()、ReverseFind()、FindOneOf()三个CString对象方法来实现字符串查找操作 // Find()从指定位置开始查找指定的字符或者字符串,找打返回其位置; 找不到返回-1 CString str = TEXT("ABDFCEDF0"); int index = str.Find(TEXT("DF"), 0); // 从索引为0的位置开始查找目标字符串: 2 // ReverseFind():从字符串的末尾开始查找目标字符,查找的是字符不是字符串: // 匹配到返回其位置,找不到返回-1 // 虽然是从后向前查找,但是位置是开始算起 index = str.ReverseFind(TEXT('D')); // 6 // FindOneOf(): 查找字符中的任意字符,返回第一次出现的位置,找不到就返回-1 CString str2 = TEXT("abcdabc"); index = str2.FindOneOf(TEXT("cda")); // 返回0 // 字符"cda"中的'a'出现在"abcdabc"的0位置上

    字符串替换与删除

    // 字符串替换与删除: Replace()、Remove()、Delete() // 字符替换 CString str1 = TEXT("abcdabc"); str1.Replace(TEXT('b'), TEXT('k')); // 使用字符k替换str1中所有的字符b MessageBox(str1); // akcdakc // 字符串替换 CString str2 = TEXT("abcdabc"); str2.Replace(TEXT("bc"), TEXT("KF")); MessageBox(str2); // aKFdaKF // 单个字符的修改 CString str = TEXT("abcdabc"); TCHAR xch = str.GetAt(0); // 得到索引为0的字符 str.SetAt(0, TEXT('K')); // 修改索引为0的字符为'K' // str[1] = 'G'; // 语法报错:[]只能获取指定索引位置的字符: []只读的不能修改 MessageBox(str); // Remove(): 删除对象中指定的字符:只能删除字符,返回删除的字符个数 CString str3 = TEXT("abcdabc"); int count = str3.Remove(TEXT('b')); // 2 MessageBox(str3); // Delete(): 删除CString对象中指定位置的字符,返回的是删除后的字符串的长度 CString str4 = TEXT("abcdabc"); count = str4.Delete(1, 3); // 第一个参数是索引位置,第二参数是个数 MessageBox(str4); // 删除的是bcd:结果为aabc // count = 4: 删除后的字符串为"aabc":字符串长度为4

    字符串提取

    // 字符串的提取:Left()、Mid()、Right()分别从左中右对字符串对象进行提取 // 这三个函数并不会改变字符串对象本身 CString str = TEXT("abcd"); MessageBox(str.Left(2)); // 从左侧开始提取2个字符: "ab" MessageBox(str.Mid(2)); // 提取的索引为2:"cd" MessageBox(str.Right(3)); // 从右侧开始提取3个字符:"bcd"

    字符串格式化

    // 字符串的格式化 // 格式化字符串:Format()方法:实现从int、long等类型、TCHAR、TCHAR *类型转换 int num = 6; CString str1; str1.Format(TEXT("%d"), num); MessageBox(str1); // 将字符串转换为int类型 CString str2 = TEXT("101"); int i2 = _ttoi(str2); // #define _ttoi _wtoi // 将字符串转换为TCHAR *类型 // 方法1 TCHAR *p1 = str2.GetBuffer(); MessageBox(p1); str2.ReleaseBuffer(); MessageBox(p1); // 这里也可以使用 // 方法2:经过两次强制类型转换 TCHAR *p2 = (LPTSTR)(LPCTSTR)str2; MessageBox(p2); // 这里也可以使用 // 计算CString对象所占用的字节数 // 错误: sizeof(CString)或sizeof(str) // 正确: str.GetLength() * sizeof(TCHAR)

    模态对话框 vs 非模态对话框

    创建模态对话框:Dialog#DoModal() #include "MyDialog.h" void CDemo02Dlg::OnBnClickedOk() { MyDialog dlg; // MFC类的命名规则: 以大写C开头:CMyDialog dlg.DoModal(); // 创建模态对话框 } 创建非模态对话框:Dialog#Create() void CDemo02Dlg::OnBnClickedCancel() { MyDialog dlg; dlg.Create(MyDialog::IDD, this);// 对话框资源ID, 父窗口指针 dlg.ShowWindow(SW_SHOW); // 必须显示调用CWnd#ShowWindow()方法 } 但是这样的创建非模态对话框:对话框闪一下就会退出:因为dlg对象是自动变量,且非模态对话框不会阻塞程序流程:自动变量离开作用域就被销毁:对象都被销毁了,对话框肯定不存在了:解决办法之一:使用全局变量或静态变量 MyDialog dlg; // 全局变量 void CDemo02Dlg::OnBnClickedCancel() { dlg.Create(MyDialog::IDD, this);// 对话框资源ID, 父窗口指针 dlg.ShowWindow(SW_SHOW); // 必须显示调用CWnd#ShowWindow()方法 } 虽然解决了一闪而过的问题:但是不能多次执行:第二次执行此段代码的时候,应用程序会崩溃:因为dlg是全局变量,每次执行{…}中的代码的时候都在重复创建对话框:所以导致程序崩溃:解决方案之一:就是在MyDialog对话框退出的代码中添加销毁非模态对话框的代码:DestroyWindow() // MyDialog对话框退出时的代码 void MyDialog::OnBnClickedCancel() { DestroyWindow(); //OnCancel(); // 这句代码必须屏蔽掉了 } 非模态对话框的创建: (1)使用全局变量,即非模态对话框对象是全局变量 (2)使用成员变量,即非模态对话框对象是其他对话框类的成员变量,在OnInitDialog方法中创建非模态对话框,在需要的时候进行显示 (3)使用全局或成员指针变量 MyDialog *pDlg = NULL; // 一般定义为成员的指针变量 void CDemo02Dlg::OnBnClickedCancel() { if (pDlg == NULL) { pDlg = new MyDialog; // 在合适的地址释放指针申请的内存 } pDlg->Create(MyDialog::IDD, this);// 对话框资源ID, 父窗口指针 pDlg->ShowWindow(SW_SHOW); // 必须显示调用CWnd#ShowWindow()方法 }

    内存分配的三种方式

    从静态存储区分配:内存的程序编译的时候就已经分配好,这块内存在程序的整个运行器件都存在:例如全局变量,static变量:全局static变量、局部static变量、static成员变量从栈区分配:在执行函数时,函数内局部变量的存储单元可以在栈上创建,函数执行结束时这些存储单元自动释放。栈内存分配运行内置处理器的指令集中:即从汇编代码中可以看出!效率很高,但是分配的内存容量有限从堆区分配:也称之为动态内存分配。程序运行的时候使用库函数malloc或关键字new申请任意数量的内存,程序员自己负责在何时使用库函数free或关键字delete释放内存:动态内存的生存期由程序员决定:使用非常灵活,但问题也最多

    对话框之间的数据传递

    全局变量法 主对话框法:AfxGetMainWnd() 父窗口法:GetParent 成员变量、成员函数法

    控件变量的绑定

    对话框控件不同类型成员变量的绑定DDX:Dialog Data Exchange:对话框数据交换DDV:Dialog Date Validate:对话框数据校验一个控件可以绑定多个不同类型的变量:控件类型与值类型 设置以后发生了什么? (1)在类定义文件中有了相关变量的声明:即对话框的头文件中 (2)在对话框实现文件中的DoDataExchange()方法中,进行了相关的绑定 那么知道了原理后就可以手动的添加代码:即不需要借助工具的条件下完成控件变量的绑定工作开源控件的使用:导入开源代码到目标工程中–>控件绑定变量(如果是控件类型,使用开源控件类定义变量即可:DoDataExchange()方法中代码不需要改变)–>使用开源控件:绑定控件类型变量,进行子类化操作

    定时器的使用

    定时器Timer在windows程序设计中很多地方都能用到:它主要用途就是按程序设定间隔时间,间歇性的产生WM_TIMER消息:发送到指定窗口之后,在窗口中对WM_TIMER消息进行处理,完成指定任务定时器相关的API函数 SetTimer(定时器id, 产生WM_TIMER消息时间间隔, 回调函数: 消息响应函数):启动定时器 OnTimer(定时器ID):定时器响应函数 KillTimer(定时器ID):关闭定时器 SetTimer(1, 1000, NULL); // 消息响应函数是OnTimer: 因为参数为NULL // WM_TIMER消息的响应函数 void CDemo02Dlg::OnTimer(UINT_PTR nIDEvent) { CTime tm = CTime::GetCurrentTime(); // 将CTime对象转换为CString对象 CString str = tm.Format(TEXT("%Y-%m-%d %H:%M:%S")); SetDlgItemText(IDC_STATIC, str); // 在文本框中显示当前时间 CDialog::OnTimer(nIDEvent); }

    对话框响应响应鼠标及键盘消息

    常见的鼠标消息 WM_MOUSEMOVE:鼠标移动 WM_LBUTTONDOWN:鼠标左键按下 WM_LBUTTONUP:鼠标左键抬起 WM_LBUTTONDBLCLK:鼠标左键双击 WM_RBUTTONDOWN:鼠标右键按下 WM_RBUTTONUP:鼠标右键抬起 WM_RBUTTONDBLCLK:鼠标右键双击 WM_MBUTTONDOWN:鼠标中键按下 WM_MBUTTONUP:鼠标中键抬起 WM_MBUTTONDBLCLK:鼠标中键双击 WM_MOUSEWHEEL:鼠标滚轮滚动 创建的键盘消息 WM_KEYDOWN:按键按下 WM_CHAR:可以打印的字符 WM_KEYUP:按键抬起 自定义一个按钮:可以响应鼠标左键按下与鼠标左键抬起的消息:在自动化软件中会经常使用此功能 在CMyButton类中添加WM_LBUTTONDOWN与WM_LBUTTONUP消息响应函数 给对话框中的按钮绑定控件类型变量 CMyBuuton m_btn; DDX_Control(pDX, IDC_BUTTON1, m_btn); 对话框响应键盘消息:在虚函数PreTranslateMessage中进行安检的处理 virtual BOOL PreTranslateMessage(MSG* pMsg); BOOL CDemo02Dlg::PreTranslateMessage(MSG* pMsg) { switch (pMsg->message) { case WM_KEYDOWN: SetDlgItemText(IDC_EDIT1, TEXT("WM_KEYDOWN")); break; case WM_KEYUP: SetDlgItemText(IDC_EDIT1, TEXT("WM_KEYUP")); break; default: break; } return CDialog::PreTranslateMessage(pMsg); } WM_KEYDOWN消息中的wParam参数,即pMsg->wParam字符都是大写的:如果要判断按下的字符是大写还是小写,可以在WM_CHAR消息中使用ASCII码对pMsg->wParam进行判断:WM_KEYDOWN/WM_KEYUP与WM_CHAR他们的用途是不同的:前者是用来判断按键被按下与抬起:后者怎是检测哪个可见字符被按下了对话框程序响应组合键、快捷键:利用GetKeyState函数获得某一按键的按下与抬起的状态:该函数的返回值的最高位为1表示按键按下:为0表示按键抬起状态 #define IsKeyPressed(nVirtKey) ((GetKeyState(nVirtKey) & (1 << (sizeof(SHORT) * 8 - 1))) != 0) #define IsKetToggled(nVirtKey) ((GetKeyState(nVirtKey) & 1) != 0) BOOL CDemo02Dlg::PreTranslateMessage(MSG* pMsg) { // 检测是否同时按下了Ctrl+Q // 因为WM_KEYDOWN消息中的wParam参数都是大写的,即使按下小写的q也是得到大写的Q // Ctrl+Q或Ctrl+q都会触发代码执行 if (pMsg->message == WM_KEYDOWN && pMsg->wParam == 'Q' && IsKeyPressed(VK_CONTROL)) { MessageBox(TEXT("Ctrl + Q")); } return CDialog::PreTranslateMessage(pMsg); } 通过载入快捷键(加速键)资源,并建立快捷键与消息映射的方法来实现 1. 添加快捷键资源 2. 修改快捷键资源中的组合键配置 3. 添加响应函数声明:格式:afx_msg void OnCtrlW() 4. 添加消息映射:ON_BTN_CLICKED(ID_ACC1, onOnCtrlW)或ON_BTN_CLICKED(ID_ACC1, &XXXDlg::onOnCtrlW) 5. 添加响应代码 6. 初始化快捷键资源:

    HACCEL hAcc = LoadAccelerators(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR1)); // 得到的hAcc始终无效 在MFC中测试没有通过???在对话框中使用组合键:方法1就是在虚函数中PreTranslateMessage中实现;方法2使用资源:方法1比较直接,使用方便,但是一般只应用于快捷键比较少的应用程序;方法2比较使用,虽然实现起来代码量稍微大一些,但是适用于快捷键比较多的应用程序中:但是这两种方式在窗口隐藏或最小化的时候都是不能响应快捷键的

    全局热键注册

    全局热键的优点:即使窗口最小化或隐藏或窗口不是当前的活动窗口:也可以响应热键:例如QQ软件的默认呼出热键:Ctrl+Alt+Z就是注册了全局系统热键给窗口注册全局热键 BOOL suc = RegisterHotKey(m_hWnd, 100, MOD_CONTROL, 'W'); 第一个参数:窗口句柄 第二个参数:热键标识 第三个参数:组合键,可以是MOD_ALT、MOD_CONTROL、MOD_SHIFT、MOD_WIN任意组合 第三个参数:指定热键的虚拟键盘码,例如'A' UnregisterHotKey(m_hWnd, 100); // 只能取消一次RegisterHotKey注册的热键 第一个参数:窗口句柄 第二个参数:热键标识 给工作线程注册全局热键:没有窗口 static UINT __cdecl ThreadProc(LPVOID lpParam); // 声明成员函数作为线程函数:必须static UINT __cdecl CDemo03Dlg::ThreadProc(LPVOID lpParam) { RegisterHotKey(NULL, 100, MOD_CONTROL, 'W'); // 注册热键 MSG msg = {0}; while (GetMessage(&msg, NULL, 0, 0)) { switch (msg.message) { case WM_HOTKEY: { int idHotKey = (int) msg.wParam; // 热键id int fuModifiers = (UINT) LOWORD(msg.lParam); // 组合键 int uVirtKey = (UINT) HIWORD(msg.lParam); // 虚拟键码 if (idHotKey == 100 && fuModifiers == MOD_CONTROL && uVirtKey == 'W') AfxMessageBox(TEXT("--------")); } break; default: break; } } UnregisterHotKey(NULL, 100); return 0; } AfxBeginThread(ThreadProc, this); // 开启线程 GetMessage在没有消息的时候会阻塞线程,改为PeekMessage UINT __cdecl CDemo03Dlg::ThreadProc(LPVOID lpParam) { RegisterHotKey(NULL, 100, MOD_CONTROL, 'W'); // 注册热键 MSG msg = {0}; for (;;) { while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // 没有消息返回FALSE switch (msg.message) { case WM_HOTKEY: { int idHotKey = (int) msg.wParam; int fuModifiers = (UINT) LOWORD(msg.lParam); int uVirtKey = (UINT) HIWORD(msg.lParam); if (idHotKey == 100 && fuModifiers == MOD_CONTROL && uVirtKey == 'W') AfxMessageBox(TEXT("--------")); } break; default: break; } } } Sleep(50); // 睡觉100ms:防止CPU过高 UnregisterHotKey(NULL, 100); return 0; }

    控件自定义消息的响应

    按钮控件的右键消息 1. 向工程中添加一个派生自CButton类的子类CMyButton 2. 为按钮绑定一个CMyButton类型的空间类型变量 3. 响应CMyButton类的你想响应的消息
    Processed: 0.010, SQL: 9