65.9K
CodeProject 正在变化。 阅读更多。
Home

Tab 顺序辅助类

starIconstarIconstarIconemptyStarIconemptyStarIcon

3.00/5 (2投票s)

2009年10月13日

CPOL

3分钟阅读

viewsIcon

67360

downloadIcon

348

用于在子对话框中操作 tab 顺序的类

引言

在 Windows 应用程序中,您可以通过按 Tab 键来更改控件的焦点。Tab 顺序是指按 Tab 键时焦点的序列。

从 Visual C++ 6.0 的资源编辑器中,您可以通过按 Ctrl+D 键来查看和更改 Tab 顺序。

在简单的对话框中,Tab 顺序很容易使用。但是,在复杂对话框(例如多子对话框环境)中,Tab 顺序不能正常工作。焦点在一个对话框中移动,永远不会移动到子对话框。

复杂对话框

总而言之,Tab 顺序工作正常,而且非常简单。但是,当您创建子对话框时,问题就会发生。让我们想象一个对话框,它有三个按钮和一个子对话框。子对话框有两个按钮。在这种情况下,预期的 Tab 序列如下

1. Main Dialog Button 1
2. Main Dialog Button 2
3. Main Dialog Button 3
4. Sub Dialog Button 1
5. Sub Dialog Button 2

但是,如果没有额外的 Tab 键处理,我们无法通过 Tab 键将焦点设置到子对话框,它的工作方式是 1 > 2 > 3 > 1 > 2 > 3...

CFocusEx 类解决了这个问题。它将错误的焦点序列(1 > 2 > 3 > 1 > 2 > 3...)更改为预期的序列(1 > 2 > 3 > 4 > 5 > 1 > 2 > 3...)。

工作原理

CFocusEx 简单而灵活。此外,它支持大多数具有多个子对话框的应用程序。当用户按下 Tab 键时,CFocusEx 会比较注册窗口和焦点窗口。CFocusEx 类按照注册的顺序移动控件的焦点。如果下一个焦点控件是对话框,它会将焦点设置在该对话框的第一个控件上。

Using the Code

  1. 重写父对话框的 PreTranslateMessage 函数来拦截 Tab 键的事件。
  2. 声明 CFocusEx 对象作为类成员变量,并声明 GetFocusableWindow 函数以管理 Tab 顺序
    class CFocusDlg : public CDialog
    {
    // Construction
    public:
    	CFocusDlg();
    	.
    	.
    	.
    private:
    	static HWND GetFocusableWindow(int nPosition, LPVOID lParam);
    	CFocusEx m_objFocus;
    };
  3. 初始化 CFocusEx 对象并调用 ProcessKeyPressMessage 来处理键盘事件。CFocusEx::InitFocusEx 的第一个参数是 GetFocusableWindow 函数。这是一个非常重要的函数,因为它决定了下一个焦点。
    BOOL CFocusDlg::PreTranslateMessage(MSG* pMsg)
    {
    	m_objFocus.InitFocusEx( GetFocusableWindow, this );
    
    	if( m_objFocus.ProcessKeyPressMessage( this, pMsg ) ) {
    		return TRUE;
    	}
    
    	return CDialog::PreTranslateMessage(pMsg);
    }
  4. 编写 GetFocusableWindow 函数以确定下一个焦点。您可以根据 nPosition 的数据分配适当的句柄来接收焦点。
    通常,您可以像以下代码一样编写父对话框的 GetFocusableWindowm_dlgSub 是在最后一个控件之后获得焦点的子对话框。
    HWND CFocusDlg::GetFocusableWindow(int nPosition, LPVOID lParam)
    {
    	CFocusDlg* pThis = (CFocusDlg*)lParam;
    	switch( nPosition )
    	{
    	case FOCUSABLEWINDOW_POSITION_FIRST:
    		{
    			return CFocusEx::GetFirstFocusableWindow
    					( pThis->GetSafeHwnd() );
    		}
    		break;
    	case FOCUSABLEWINDOW_POSITION_FOCUSABLE:
    		{
    			return pThis->m_dlgSub.GetSafeHwnd();
    		}
    		break;
    	case FOCUSABLEWINDOW_POSITION_LAST:
    		{
    			return CFocusEx::GetLastFocusableWindow
    					( pThis->GetSafeHwnd() );
    		}
    		break;
    
    	}
    	return NULL;
    }

    该函数有三种 switch 状态:FOCUSABLEWINDOW_POSITION_FIRSTFOCUSABLEWINDOW_POSITION_LAST 返回第一个或最后一个控件的句柄。FOCUSABLEWINDOW_POSITION_FOCUSABLE 是函数最重要的部分。
    当用户在控件的末尾按下 Tab 按钮时,将使用 FOCUSABLEWINDOW_POSITION_FOCUSABLE 调用该函数。因此,有两种情况需要考虑。

    1. 首先,将焦点从父窗口设置到子窗口。返回父窗口的句柄,将焦点设置到父窗口。

    2. 其次,将焦点从子窗口设置到父窗口。返回子窗口的句柄,将焦点设置到子窗口。

  5. 重写子对话框的 PreTranslateMessage 函数来拦截 Tab 键的事件。
  6. 声明 CFocusEx 对象作为类成员变量,并声明 GetFocusableWindow 函数以管理 Tab 顺序
    class CFocusSubDlg : public CDialog
    {
    // Construction
    public:
    	CFocusSubDlg();
    	.
    	.
    	.
    private:
    	static HWND GetFocusableWindow(int nPosition, LPVOID lParam);
    	CFocusEx m_objFocus;
    };
  7. 初始化 CFocusEx 对象
    BOOL CFocusSubDlg::PreTranslateMessage(MSG* pMsg)
    {
    	m_objFocus.InitFocusEx( GetFocusableWindow, this );
    
    	if( m_objFocus.ProcessKeyPressMessage( this, pMsg ) ) {
    		return TRUE;
    	}
    
    	return CDialog::PreTranslateMessage(pMsg);
    }
  8. 编写 GetFocusableWindow 函数以确定下一个焦点。
    通常,您可以像以下代码一样编写子对话框的 GetFocusableWindow
    HWND CFocusDlg::GetFocusableWindow(int nPosition, LPVOID lParam)
    {
    	CFocusDlg* pThis = (CFocusDlg*)lParam;
    	switch( nPosition )
    	{
    	case FOCUSABLEWINDOW_POSITION_FIRST:
    		{
    			return CFocusEx::GetFirstFocusableWindow
    					( pThis->GetSafeHwnd() );
    		}
    		break;
    	case FOCUSABLEWINDOW_POSITION_FOCUSABLE:
    		{
    			if( pThis->GetParent() ) {
    				if( pThis->GetParent()->GetParent() ) {
    					return pThis->GetParent()->
    						GetParent()->GetSafeHwnd();
    				}
    			}
    		}
    		break;
    	case FOCUSABLEWINDOW_POSITION_LAST:
    		{
    			return CFocusEx::GetLastFocusableWindow
    					( pThis->GetSafeHwnd() );
    		}
    		break;
    
    	}
    	return NULL;
    }

就这样。如果您不理解本文中的代码,则附加项目的代码将更容易理解。

历史

  • 2009 年 10 月 13 日:初始版本
© . All rights reserved.