文章目录
- 控件反射消息机制
- 文本框控件
- EN_CHANGE消息
- EN_UPDATE消息
- EN_SETFOCUS消息
- EN_KILLFOCUS消息
- EN_MAXTEXT消息
- EN_ERRSPACE消息
- EN_HSCROLL消息
- 按钮控件
- BN_CLICKED消息
- BN_DOUBLECLICKED消息
- BN_SETFOCUS消息
- BN_KILLFOCUS消息
- 单选按钮
- BN_CLICKED 消息
- 复选框
- BN_CLICKED
- BN_DOUBLECLICKED消息
- 组合框
- CBN_SELCHANGE 消息
- CBN_EDITCHANGE 消息
- CBN_DROPDOWN消息
- CBN_CLOSEUP消息
- CBN_SELENDOK消息
- CBN_SELENDCANCEL消息
- WM_NOTIFY消息
- 1.当控件向父窗口发送消息
- 2.标准的解决方案
- 还可以对NMHDR这个结构体进行拓展
- 微调控件
- UDN_DELTAPOS消息
- 滑动控件
- TRBN_THUBPOSCHANGING
- 进度条
- 列表
- LBN_SELCHANGE 消息
- LBN_DBLCLK 消息
- LBN_SELCANCEL 消息
- LBN_SETFOCUS 消息
- LVN_ITEMCHANGED 消息
- LVN_COLUMCLICK 消息
- 实现简单的列表排序
- 树控件
- TVNSELCHANGED 消息
- TVN_ITEMEXPANDING消息
- TVN_KEYDOWN 消息
- Tab控件
- TCN_SELCHANGED 消息
- TCN_KEYDOWN 消息
控件反射消息机制
当控件产生一个消息时,MFC首先会尝试将该消息反射回控件自身进行处理,这就是控件反射消息机制。这种机制允许控件类自己处理与自身相关的特定消息,而不需要父窗口介入。
如果控件没有处理反射消息,或者没有设置反射消息处理机制,那么消息会传递给父窗口进行处理。父窗口可以使用ON_BN_CLICKED等宏来处理控件的消息。
如果父窗口也没有处理该消息,消息会继续向上传递到更高级别的窗口(如主框架窗口),直到消息被处理或者到达消息链的末端。
创建消息步骤如下
- 对话框里创建一个控件,这里是edit control
- 点开类视图,点击类向导
- 搜索对象ID,右边就会弹出很多消息供选择,选择需要使用的消息
文本框控件
EN_CHANGE消息
文本改变消息
void C控件消息Dlg::OnChangeEdit1()
{CString str;GetDlgItemText(IDC_EDIT1, str);//获取文本框内容SetWindowText(str);//设置窗口标题
}
EN_UPDATE消息
跟上面的差不多,只不过在未完成绘制更新时触发
void C控件消息Dlg::OnUpdateEdit1()
{CString str;GetDlgItemText(IDC_EDIT1, str);//获取文本框内容SetWindowText(str);//设置窗口标题
}
EN_SETFOCUS消息
获取焦点时的消息
void C控件消息Dlg::OnSetfocusEdit1()
{static int i = 0;CString str;str.Format(_T("第%d次获得焦点"), ++i);SetWindowText(str);//设置窗口标题
}
EN_KILLFOCUS消息
与上面相反,失去焦点时获得消息
EN_MAXTEXT消息
这里要先设置一下最大值,才能获取这个消息
BOOL C控件消息Dlg::OnInitDialog()
{CDialogEx::OnInitDialog();// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动// 执行此操作SetIcon(m_hIcon, TRUE); // 设置大图标SetIcon(m_hIcon, FALSE); // 设置小图标// TODO: 在此添加额外的初始化代码auto pEdit = (CEdit*)GetDlgItem(IDC_EDIT1);//获取文本框控件pEdit->SetLimitText(10);//设置文本框最大字符数return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void C控件消息Dlg::OnMaxtextEdit1()
{MessageBox(_T("文本框已达到最大字符数"));
}
EN_ERRSPACE消息
当 Edit 控件在执行某些操作时,由于系统内存不足,无法完成分配所需内存空间的任务,就会触发 EN_ERRSPACE 消息。这是一个系统发出的警示信号,告知程序当前 Edit 控件遇到了内存分配困境,可能影响正常功能的运行,例如无法存储新输入的文本、无法动态扩展以显示完整内容等。比如在文本文档里输入的内容超过限制数量,弹出一个提示
EN_HSCROLL消息
要接收到消息,先要设置一下滚动条
这里水平和竖直滚动条都开了
void C控件消息Dlg::OnHscrollEdit1()
{//MessageBox(_T("水平滚动条被拖动"));auto pEdit = (CEdit*)GetDlgItem(IDC_EDIT1);//获取文本框控件int pos = pEdit->GetScrollPos(SB_HORZ);//获取水平滚动条位置CString str;str.Format(_T("水平滚动条位置:%d"), pos);SetWindowText(str);//设置窗口标题
}
按钮控件
BN_CLICKED消息
和以前的双击一样,都是关联到OnBnClickedButton()
BN_DOUBLECLICKED消息
要获取双击事件,要开启notify
void C控件消息Dlg::OnDoubleclickedButton1()
{MessageBox(_T("按钮被双击"));
}
后面很多消息需要开启notify
BN_SETFOCUS消息
获得焦点
BN_KILLFOCUS消息
失去焦点
单选按钮
BN_CLICKED 消息
需要的消息可以在类向导里一起选中双击,后确定
这里双击同样要开启notify
void C控件消息Dlg::OnClickedRadio1()
{SetWindowText(_T("radio1被点击"));
}void C控件消息Dlg::OnKillfocusRadio1()
{SetWindowText(_T("radio1失去焦点"));
}void C控件消息Dlg::OnSetfocusRadio1()
{SetWindowText(_T("radio1获得焦点"));
}void C控件消息Dlg::OnDoubleclickedRadio1()
{MessageBox(_T("radio1被双击"));
}
复选框
BN_CLICKED
BN_DOUBLECLICKED消息
组合框
创建了一个combobox,给它设置了一个控件变量m_combobox
CBN_SELCHANGE 消息
CBN_EDITCHANGE 消息
CBN_DROPDOWN消息
下拉箭头点击时发出的消息
void C控件消息Dlg::OnDropdownCombo1()
{MessageBeep(0xFFFFFFFF);//发出声音
}
CBN_CLOSEUP消息
CBN_SELENDOK消息
CBN_SELENDCANCEL消息
void C控件消息Dlg::OnSelendcancelCombo1()
{MessageBox(_T("下拉框选项被取消"));
}
WM_NOTIFY消息
1.当控件向父窗口发送消息
当自定义控件中发生了特殊的事件需要通知父窗口时,可以向父窗口发送消息,最简单的方法就是直接向父窗口直接发送自定义消息:
在pch.h定义一下全局的宏
#define UM_TEST WM_USER + 1//自定义消息
创建一个自定义类CMyButton,继承自CButton,下面是cpp文件
#include "pch.h"
#include "CMyButton.h"BEGIN_MESSAGE_MAP(CMyButton, CButton)ON_CONTROL_REFLECT(BN_CLICKED, &CMyButton::OnBnClicked)
END_MESSAGE_MAP()void CMyButton::OnBnClicked()
{// TODO: 在此添加控件通知处理程序代码this->GetParent()->SendMessage(UM_TEST, 1, 2);//发送自定义消息
}
然后就可以在主窗口截取消息
Dlg.cpp里添加消息映射
ON_MESSAGE(UM_TEST, &C控件消息Dlg::OnTestBtnMsg)//添加在消息映射那块
添加接收函数
LRESULT C控件消息Dlg::OnTestBtnMsg(WPARAM wParam, LPARAM lParam)
{CString str;str.Format(_T("收到自定义消息,参数1:%d,参数2:%d"), wParam, lParam);MessageBox(str);return wParam + lParam;
}
缺点:需要用到唯一ID的时候,父窗口中的控件会有很多,并不能保证所有消息ID都是唯一的。父窗口可能有一个控件和子窗口的控件同名
2.标准的解决方案
WM_NOTIFY 消息它主要用于控件向其父窗口发送详细的通知信息。
这个消息与其它的消息不同,用户可以自定义通知的内容,但传递消息的方式是统一的。
上述CMyButton.cpp中的函数改成
void CMyButton::OnBnClicked()
{// TODO: 在此添加控件通知处理程序代码NMHDR nmhdr;//一个NMHDR结构体,用于存储按钮的消息nmhdr.hwndFrom = GetSafeHwnd();//获取按钮的句柄nmhdr.idFrom = GetDlgCtrlID();//获取按钮的IDnmhdr.code = UM_TEST;//自定义消息this->GetParent()->SendMessage(WM_NOTIFY, nmhdr.idFrom, (LPARAM)&nmhdr);//发送消息//注意,这里如果用PostMessage,则按钮点击后不会立即响应,而是等到消息队列中的其他消息处理完毕后才会响应,后面如果用WM_NOTIFY接收,会接收不到,需要再添加一行代码,才能收到
}
Dlg里的消息改成
ON_NOTIFY(UM_TEST, IDC_TEST_BUTTON, &C控件消息Dlg::OnTestBtnMsg2)
Dlg的消息接收函数改成
void C控件消息Dlg::OnTestBtnMsg2(NMHDR* pNMHDR, LRESULT* pResult)
{CString str;str.Format(_T("收到自定义消息,参数1:%d,参数2:%d"), pNMHDR->idFrom, pNMHDR->code);MessageBox(str);*pResult = 0;//表示消息处理完毕
}
还可以对NMHDR这个结构体进行拓展
在pc.h里添加结构体
typedef struct MyNMHDR
{NMHDR nmhdr;int data1;int data2;
} MyNMHDR, * PMyNMHDR;
CMyButton.cpp修改
void CMyButton::OnBnClicked()
{// TODO: 在此添加控件通知处理程序代码NMHDR nmhdr;//一个NMHDR结构体,用于存储按钮的消息nmhdr.hwndFrom = GetSafeHwnd();//获取按钮的句柄nmhdr.idFrom = GetDlgCtrlID();//获取按钮的IDnmhdr.code = UM_TEST;//自定义消息MyNMHDR mynmhdr;mynmhdr.nmhdr = nmhdr;mynmhdr.data1 = 100;mynmhdr.data2 = 200;this->GetParent()->SendMessage(WM_NOTIFY, nmhdr.idFrom, (LPARAM)&mynmhdr);//发送消息
}
Dlg.cpp修改
void C控件消息Dlg::OnTestBtnMsg2(NMHDR* pNMHDR, LRESULT* pResult)
{MyNMHDR* pnmhdr = (MyNMHDR*)pNMHDR;CString str;str.Format(_T("收到自定义消息,参数1:%d,参数2:%d ,参数3:%d,参数4:%d"), pNMHDR->idFrom, pNMHDR->code, pnmhdr->data1, pnmhdr->data2);MessageBox(str);*pResult = 0;//表示消息处理完毕
}
微调控件
控件事件也可以点属性框那个闪电符号设置
选中需要添加的事件,点下拉按钮,点add
UDN_DELTAPOS消息
步长位置消息
可以看出,该结构体和我们上例自定义的MyNMHDR类似,那么用法也是类似的
void C控件消息Dlg::OnDeltaposSpin1(NMHDR* pNMHDR, LRESULT* pResult)
{LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);// TODO: 在此添加控件通知处理程序代码CString str;str.Format(_T("当前值:%d, 步长:%d"), pNMUpDown->iPos, pNMUpDown->iDelta);SetWindowText(str);//设置窗口标题CSpinButtonCtrl* pSpin = (CSpinButtonCtrl*)GetDlgItem(IDC_SPIN1);//获取控件UDACCEL uda[1];//定义一个加速器数组uda[0].nInc = 10;//每次增加的值uda[0].nSec = 1;//加速时间间隔pSpin->SetAccel(1, uda);//设置加速器*pResult = 0;
}
滑动控件
TRBN_THUBPOSCHANGING
滑块位置改变消息
该消息需要开启notify
void C控件消息Dlg::OnTRBNThumbPosChangingSlider1(NMHDR* pNMHDR, LRESULT* pResult)
{// 此功能要求 Windows Vista 或更高版本。// _WIN32_WINNT 符号必须 >= 0x0600。NMTRBTHUMBPOSCHANGING* pNMTPC = reinterpret_cast<NMTRBTHUMBPOSCHANGING*>(pNMHDR);// TODO: 在此添加控件通知处理程序代码CString str;str.Format(_T("当前值:%d,移动原因:%d"), pNMTPC->dwPos, pNMTPC->nReason);//移动原因,有点滑之类的SetWindowText(str);//设置窗口标题*pResult = 0;
}
进度条
都是通用消息,略
列表
LBN_SELCHANGE 消息
选择改变消息
LBN_DBLCLK 消息
LBN_SELCANCEL 消息
选择取消消息
这个消息要结合combobox才能触发,下面会介绍
LBN_SETFOCUS 消息
LVN_ITEMCHANGED 消息
列表选择发生改变的消息,如果是HDN_ITEMCHANGED消息,就是表头选择发生改变
void C控件消息Dlg::OnLvnItemchangedList2(NMHDR* pNMHDR, LRESULT* pResult)
{LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);// TODO: 在此添加控件通知处理程序代码if (pNMLV->uNewState & LVIS_SELECTED)//如果被选中{CString str;str.Format(_T("item:%d\r\niSubitem:%d\r\n状态:%d\r\nold状态:%d\r\n"), pNMLV->iItem, pNMLV->iSubItem, pNMLV->uNewState, pNMLV->uOldState);//把字符串放到m_edit中m_edit.SetWindowText(str);}*pResult = 0;
}
LVN_COLUMCLICK 消息
列的点击,一般可以用来排序之类的
实现简单的列表排序
.h文件定义一个排序函数
static int CALLBACK CompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);//排序函数
.cpp实现
DWORD dwSelColID; // 选择的列
bool bSortAsc = true; // 是否升序
void C控件消息Dlg::OnLvnColumnclickList2(NMHDR* pNMHDR, LRESULT* pResult)
{LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);// TODO: 在此添加控件通知处理程序代码int nCol = pNMLV->iSubItem;//获取列for (int i = 0; i < m_list.GetHeaderCtrl()->GetItemCount(); i++)m_list.SetItemData(i, i);//设置列的排序数据dwSelColID = nCol;//保存列 bSortAsc = !bSortAsc;//改变排序方式m_list.SortItems(CompareProc, (DWORD_PTR)&m_list);//排序*pResult = 0;
}int C控件消息Dlg::CompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{CListCtrl* pList = (CListCtrl*)lParamSort;CString str1 = pList->GetItemText(lParam1, dwSelColID);CString str2 = pList->GetItemText(lParam2, dwSelColID);if (bSortAsc)return str1.Compare(str2);elsereturn str2.Compare(str1);
}
树控件
TVNSELCHANGED 消息
选择发生改变消息
void C控件消息Dlg::OnTvnSelchangedTree1(NMHDR* pNMHDR, LRESULT* pResult)
{LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);// TODO: 在此添加控件通知处理程序代码HTREEITEM hItem = pNMTreeView->itemNew.hItem;CString str;str.Format(_T("选中项:%s"), m_tree.GetItemText(hItem));SetWindowText(str);*pResult = 0;
}
TVN_ITEMEXPANDING消息
展开消息
TVN_KEYDOWN 消息
按键消息
void C控件消息Dlg::OnTvnKeydownTree1(NMHDR* pNMHDR, LRESULT* pResult)
{LPNMTVKEYDOWN pTVKeyDown = reinterpret_cast<LPNMTVKEYDOWN>(pNMHDR);// TODO: 在此添加控件通知处理程序代码if (pTVKeyDown->wVKey == 0x46 && (GetKeyState(VK_CONTROL) & 0x8000))//按下了回车键和Ctrl{MessageBox(_T("按下了F和Ctrl"));}*pResult = 0;
}
Tab控件
TCN_SELCHANGED 消息
选择改变消息
void C控件消息Dlg::OnTcnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult)
{// TODO: 在此添加控件通知处理程序代码int nTab = ((CTabCtrl*)GetDlgItem(IDC_TAB1))->GetCurSel();//获取当前选项卡CString str;str.Format(_T("当前选项卡:%d"), nTab);SetWindowText(str);*pResult = 0;
}
TCN_KEYDOWN 消息
按键消息
void C控件消息Dlg::OnTcnKeydownTab1(NMHDR* pNMHDR, LRESULT* pResult)
{NMTCKEYDOWN* pTCKeyDown = reinterpret_cast<NMTCKEYDOWN*>(pNMHDR);// TODO: 在此添加控件通知处理程序代码if (pTCKeyDown->wVKey == 0x30 && (GetKeyState(VK_CONTROL) & 0x8000))//按下了0和CtrlMessageBox(_T("按下了0和Alt"));if (pTCKeyDown->wVKey == 0x10)//按下了回车键MessageBox(_T("按下了shift"));*pResult = 0;
}