最终方案:是在OnOK()中循环改变对话框上的各个控件的焦点,而且这个方法不需要去定义EDIT的WndProc.
void CTestDlg::OnOK() {
// TODO: Add extra validation here
GetNextDlgTabItem(GetFocus(),false)->SetFocus();
// CDialog::OnOK(); //注释掉基类的OnOK(),这按回车键就不会关闭对话框了.
}
这里的OnOK()是MFC默认的响应回车键的函数,它是一个虚函数,由覆盖基类的虚函数OnOK()而来,它是由按钮的ID必须是IDOK所产生的函数,不是什么IDC_OK那种按钮的消息响应函数OnOk()(不是虚函数,k是小写的)
但是IDOK按钮即使删除了也没有关系,只要代码中重写了基类的 OnOK()就可以了.
也就是说,在对话框上面,每次按ENTER回车键时,都是默认的那个OnOK()来响应回车键,因此可以在OnOK()中GetNextDlgTabItem(GetFocus(),false)->SetFocus()
让不同的控件循环获得焦点,就是按照控件的Tab Order顺序来转移焦点,但是前提是必须设置控件的Tab stop属性
------Tab stop属性
//----- 下面是比较复杂的解决办法
输入焦点的传递:
第一个输入框EDIT1按下回车键后,焦点转移到第二个输入框EDIT2
思路:自己编写一个EDIT编辑框的窗口过程函数WndProc(),替换MFC的WndProc()
让编辑框的所有消息都通过自己的WndProc()来处理.
step1:调用SetWindowLong()函数
在Win32的Windows编程中的知识:
创建一个窗口之前,必须先注册一个窗口类wndclass,那个时候就指定了窗口过程WndProc.
所以,当我们看到输入框EIDIT时,其窗口过程早就确定了,现在能做的是先修改它的窗口过程函数地址(也就是名称),
要修改窗口的注册类的属性,那必须调用SetWindowLong()函数.
step2:在哪个函数中调用SetWindowLong()?
1)在处理WM_CREATE消息的函数OnCreate()中调用行吗? 不行,因为那个时候对话框的子控件还没有建立
2)在WM_INITDIALOG这个消息响应函数中调用是可以的,因为这个消息恰好在对话框及其子控件创建完成时产生的.
step3:有上述分析,为CTestDlg添加WM_INITDIALOG消息响应函数,并在其中调用SetWindowLong()来更改WndProc为自定义的WndProc.
这个思路还必须要设置EDIT的Style中的Multiline属性,也就是能接受回车键输入.
上述思路最终都有比较麻烦,最后,比较好的解决办法是在OnOK()中循环改变对话框上的各个ITEM的焦点,而且这个方法不需要去定义EDIT的WndProc
调用GetNextDlgTabItem按照对话框上的控件的Tab order顺序来搜索窗口,循环获得焦点,必须要将3个EDIT的Style中的Multiline取消
void CTestDlg::OnOK() {
// TODO: Add extra validation here
GetNextDlgTabItem(GetFocus(),false)->SetFocus();
// CDialog::OnOK(); //注释掉基类的OnOK(),这按回车键就不会关闭对话框了.
}
// TestDlg.cpp : implementation file // #include "stdafx.h" #include "Mybole.h" #include "TestDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CTestDlg dialog //构造函数,用基类CDialog构造函数传入IDD,这个IDD就是IDD_DIALOG1,IDD在TestDlg.h中定义的. CTestDlg::CTestDlg(CWnd* pParent /*=NULL*/) : CDialog(CTestDlg::IDD, pParent) { //{{AFX_DATA_INIT(CTestDlg) m_num1 = 0; m_num2 = 0; m_num3 = 0; //}}AFX_DATA_INIT m_bIsCreate = false; } void CTestDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CTestDlg) DDX_Control(pDX, IDC_EDIT3, m_edit3); DDX_Control(pDX, IDC_EDIT2, m_edit2); DDX_Control(pDX, IDC_EDIT1, m_edit1); DDX_Text(pDX, IDC_EDIT1, m_num1); DDX_Text(pDX, IDC_EDIT2, m_num2); DDX_Text(pDX, IDC_EDIT3, m_num3); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CTestDlg, CDialog) //{{AFX_MSG_MAP(CTestDlg) ON_BN_CLICKED(ID_BTN_ADD, OnBtnAdd) ON_BN_CLICKED(IDC_NUMBER1, OnNumber1) ON_BN_CLICKED(IDC_BUTTON2, OnButton2) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CTestDlg message handlers void CTestDlg::OnBtnAdd() { // TODO: Add your control notification handler code here //--- 创建按钮 /* static bool m_bIsCreate = false; if(m_bIsCreate==false) { m_btn.Create("微信",BS_DEFPUSHBUTTON|WS_VISIBLE|WS_CHILD,CRect(0,0,100,100),this,123); m_bIsCreate = true; } else { m_btn.DestroyWindow(); m_bIsCreate = false; } */ //---利用m_btn对象中的成员m_hWnd来判断按钮是否创建. /* if(m_btn.m_hWnd==NULL) m_btn.Create("微信",BS_DEFPUSHBUTTON|WS_VISIBLE|WS_CHILD,CRect(0,0,100,100),this,123); else m_btn.DestroyWindow(); */ int num1,num2,num3; char ch1[10],ch2[10],ch3[10]; //------ 方式一 GetDlgItem()->GetWindowText(); /* GetDlgItem(IDC_EDIT1)->GetWindowText(ch1,10); GetDlgItem(IDC_EDIT2)->GetWindowText(ch2,10); num1 = atoi(ch1); num2 =atoi(ch2); num3 = num1+num2; itoa(num3,ch3,10); GetDlgItem(IDC_EDIT3)->SetWindowText(ch3); */ //------ 方式二 GetDlgItemText();SetDlgItemText(); /* GetDlgItemText(IDC_EDIT1,ch1,10); GetDlgItemText(IDC_EDIT2,ch2,10); num1 = atoi(ch1); num2 = atoi(ch2); num3 = num1 + num2; itoa(num3,ch3,10); SetDlgItemText(IDC_EDIT3,ch3); */ //------ 方式三 GetDlgItemInt(); SetDlgItemInt(); /* num1 = GetDlgItemInt(IDC_EDIT1); num2 = GetDlgItemInt(IDC_EDIT2); num3 = num1 + num2; SetDlgItemInt(IDC_EDIT3,num3); */ //------ 方式四 将三个EDIT编辑框关联到当前CTestDlg类的3个成员变量,是普通的内置数据类型如int的成员变量 // DDX_TEXT函数将EDIT1的内容转换到m_num1 /* UpdateData(true); // 让UpdateData()去调用DoDataExchange(), true 就是获取对话框中输入的数据并赋予变量m_num1 m_num3 = m_num1 + m_num2; UpdateData(false); //用变量m_mum3的值去初始化对话框IDC_EDIT3 */ //------ 方式五 将三个EDIT编辑框关联到当前CTestDlg类的控件类型的成员变量,如CEdit类型的控件 /* m_edit1.GetWindowText(ch1,10); m_edit2.GetWindowText(ch2,10); num1 = atoi(ch1); num2 = atoi(ch2); num3 = num1 + num2; itoa(num3,ch3,10); m_edit3.SetWindowText(ch3); */ //------ 方式六 向控件发送SendMessage()的方式来获取或者设置控件窗口的文本 // 注意: 要在MSDN中查看WM_GETTEXT和WM_SETTEXT,才能看到SendMessage()的这种参数用法,单纯的查看SendMessage()是看不到这种参数传入wParam和lParam的用法的. // 是谁给谁发送啊? 是点击的那个ADD按钮给控件窗口发送? // 要获得窗口文本用WM_GETTEXT,设置则用WM_SETTEXT //点击按钮是向编辑框EDIT1发送消息要求获得其文本,保存到ch1 /* WM_GETTEXT An application sends a WM_GETTEXT message to copy the text that corresponds to a window into a buffer provided by the caller. To send this message, call the SendMessage function with the following parameters. SendMessage( (HWND) hWnd, // handle to destination window WM_GETTEXT, // message to send (WPARAM) wParam, // number of characters to copy (LPARAM) lParam // text buffer这时候这里居然是buffer了,这个参数真实多变.... ); WM_SETTEXT An application sends a WM_SETTEXT message to set the text of a window. To send this message, call the SendMessage function with the following parameters. SendMessage( (HWND) hWnd, // handle to destination window WM_SETTEXT, // message to send (WPARAM) wParam, // not used; must be zero (LPARAM) lParam // window-text string (LPCTSTR) ); Parameters */ //调用SDK函数,要加双冒号::,最后要强制转换,有点疑惑,ch1是char,lparam是LPARAM要转换 /* ::SendMessage(GetDlgItem(IDC_EDIT1)->m_hWnd,WM_GETTEXT,10,(LPARAM)ch1); ::SendMessage(GetDlgItem(IDC_EDIT2)->m_hWnd,WM_GETTEXT,10,(LPARAM)ch2); num1 = atoi(ch1); num2 = atoi(ch2); num3 = num1 + num2; itoa(num3,ch3,10); m_edit3.SendMessage(WM_SETTEXT,0,(LPARAM)ch3); */ /* (1)或者用IDC_EDIT1关联的成员控件变量m_edit1去获得IDC_EDIT1的窗口句柄 ::SendMessage(m_edit1.m_hWnd,WM_GETTEXT,10,(LPARAM)ch1); (2) 或者直接调用IDC_EDIT1的SendMessage(),那是自己发给自己?看MSDN解释:"An application sends a WM_GETTEXT message,.....call SendMessage() " 没有说是谁call GetDlgItem(IDC_EDIT1)->SendMessage(WM_GETTEXT,10,(LPARAM)ch1); (3) 直接用关联的控件m_edit1,控件也是窗口,调用SendMessage m_edit1.SendMessage(WM_GETTEXT,10,(LPARAM)ch1) */ //------ 方式七 SendDlgItemMessage() SendDlgItemMessage(IDC_EDIT1,WM_GETTEXT,10,(LPARAM)ch1); SendDlgItemMessage(IDC_EDIT2,WM_GETTEXT,10,(LPARAM)ch2); num1 = atoi(ch1); num2 = atoi(ch2); num3 = num1 + num2; itoa(num3,ch3,10); SendDlgItemMessage(IDC_EDIT3,WM_SETTEXT,0,(LPARAM)ch3); } // -----静态文本的访问 void CTestDlg::OnNumber1() { // TODO: Add your control notification handler code here CString str; if(GetDlgItem(IDC_NUMBER1)->GetWindowText(str),str="Number1:") { GetDlgItem(IDC_NUMBER1)->SetWindowText("数值1:"); } else { GetDlgItem(IDC_NUMBER1)->SetWindowText("Number1:"); } } // 收缩按钮实现对话框伸缩功能 void CTestDlg::OnButton2() { // TODO: Add your control notification handler code here CString str; if(GetDlgItemText(IDC_BUTTON2,str),str=="收缩<<") { SetDlgItemText(IDC_BUTTON2,"伸展>>"); } else if(str=="伸展>>") { SetDlgItemText(IDC_BUTTON2,"收缩<<"); } static CRect rectLarge,rectSmall; CRect rectSeparator; if(rectLarge.IsRectEmpty()) { GetWindowRect(&rectLarge); GetWindowRect(&rectSmall); GetDlgItem(IDC_SEPERATOR)->GetWindowRect(&rectSeparator); rectSmall.bottom = rectSeparator.bottom; } if(str=="收缩<<") { // 用了SWP_NOZORDER,就忽略第一个参数,故用NULL // 用SWP_NOMOVE,就忽略了x和y,故用0,0 SetWindowPos(NULL,0,0,rectSmall.Width(),rectSmall.Height(),SWP_NOMOVE|SWP_NOZORDER); } else { SetWindowPos(NULL,0,0,rectLarge.Width(),rectLarge.Height(),SWP_NOMOVE|SWP_NOZORDER); } } //CTestDlg::OnOK() 覆盖基类的OnOk(),但是它末尾还是调用了基类的CDialog::OnOK(),因为要注释掉它 void CTestDlg::OnOK() { // TODO: Add extra validation here //方式一:不能解决问题,因为每次回车都是把焦点设置在IDC_EDIT1的下个Tab order窗口即IDC_EDIT2 // GetDlgItem(IDC_EDIT1)->GetNextWindow(GW_HWNDNEXT)->SetFocus(); //方式二: 用GetFocus()获得当前的窗口焦点,在循环指向下一个 // GetFocus()->GetNextWindow(GW_HWNDNEXT)->SetFocus();//不能解决问题,不能在三个EDIT框中循环获得焦点,但是也没有出现孙鑫老师视频中的GetNextWindow返回空导致非法访问的弹窗. //方式三: 调用GetNextDlgTabItem按照对话框上的控件的Tab order顺序来搜索窗口,循环获得焦点,必须要将3个EDIT的Style中的Multiline取消 GetNextDlgTabItem(GetFocus(),false)->SetFocus(); // CDialog::OnOK(); //注释掉基类的OnOK(),这按回车键就不会关闭对话框了. } WNDPROC prevProc; LRESULT CALLBACK NewEditProc(HWND hwnd, UINT uMsg,WPARAM wParam, LPARAM lParam ) { //如果收到键盘输入消息,且是输入的回车键 if(uMsg==WM_CHAR && wParam==0x0d) { //::SetFocus(::GetNextWindow(hwnd,GW_HWNDNEXT) ); //获得窗口句柄方法一 // ::SetFocus(::GetWindow(hwnd,GW_HWNDNEXT)); //获得窗口句柄方法二 ::SetFocus(::GetNextDlgTabItem(::GetParent(hwnd),hwnd,false)); ////获得窗口句柄方法三 // 这三个方法都不能有效解决问题,我们知道有个默认的获取回车键消息的响应函数OnOK // 因此可以在默认的OnOK()中循环转移输入焦点 return 1; } else { return prevProc(hwnd,uMsg,wParam,lParam); } } BOOL CTestDlg::OnInitDialog() { CDialog::OnInitDialog(); // TODO: Add extra initialization here prevProc = (WNDPROC)SetWindowLong(GetDlgItem(IDC_EDIT1)->m_hWnd,GWL_WNDPROC,(LONG)NewEditProc); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE }