最终方案:是在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
}