孙鑫VC++深入详解:Lesson7 Part4 ---对话框上各个控件之间循环接受回车键获得焦点


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






你可能感兴趣的:(孙鑫VC++深入详解:Lesson7 Part4 ---对话框上各个控件之间循环接受回车键获得焦点)