天天看點

VC對話框焦點轉換問題

建立一個基于對話框的應用程式,可以看到在CXxxApp的InitInstance()函數中:

    CDlgaDlg dlg;

    m_pMainWnd = &dlg;

應用程式啟動時,必須對CXxxApp的成員變量m_pMainWnd進行指派,否則無法運作。在單文檔的工程中,我們看不到這種指派操作,它是在

   if (!ProcessShellCommand(cmdInfo))

       return FALSE;

在ProcessShellCommand函數中對m_pMainWnd賦了值。

一、return false和return ture的差別:

1. 在CXxxApp的InitInstance()函數中:

return false:退出應用程式,不進入消息循環。

return ture:應用程式進入消息循環。

2. 在對話框類中的OnInitDialog()中:

return false:如果在OnInitDialog()函數中設定了某控件得到焦點,如:

      GetDlgItem(IDC_EDIT1))->SetFocus();

      應return false,否則上一句代碼不會生效。

return ture:在OnInitDialog()函數中沒有設定了某控件得到焦點,應return ture。

說明:要使對話框上的某個控件在一顯示對話框,就具有焦點,還可以将該控件的

      Table Order設為1。

二、在對話框中響應Enter鍵:

在對話框中回車,會執行預設按鈕(Default Button)的函數,預設的預設按鈕是IDOK。如果沒有預設按鈕,會執行對話框中的OnOK()函數。是以,在一個對話框中要控制Enter鍵,可以采用下面的方法:

1. 在OnOK()函數中添加代碼:

在對話框中添加四個文本框,我們想使用者按Enter鍵時,四個文本框依次循環得到焦點:

if(GetFocus()==GetDlgItem(IDC_EDIT4))    //如果第四個文本框得到焦點

        GetDlgItem(IDC_EDIT1)->SetFocus();   //使第一個文本框得到焦點

else if (GetFocus()->GetDlgCtrlID()==IDOK)   //如果“确定”按鈕得到焦點

        CDialog::OnOK();            //對話框傳回

else

        GetFocus()->GetNextWindow()->SetFocus(); 

//使目前具有焦點視窗的下一個(按照Table Order順序)視窗得到焦點

2. 添加按鈕,将其設定為預設按鈕,Visible屬性設為false,為它添加響應函數,在其中程式設計(代碼類似1)。

3. 更換回調函數:

我們更換第一個文本框的回調函數,讓它不響應Enter鍵。

i.                     定義一個全局變量WNDPROC  oldProc; 用于儲存原來的回調函數的指針。

ii.                   在對話框類中的OnInitDialog()中用SetWindowLong函數更換第一個文本框的回調函數為newProc

oldProc=(WNDPROC)SetWindowLong(GetDlgItem(IDC_EDIT1)->m_hWnd,GWL_WNDPROC,(long) newProc);

iii.                  定義newProc函數:

  LRESULT CALLBACK newProc (

HWND hwnd,UINT uMsg, WPARAM wParam, LPARAM lParam)

{

       if(uMsg==WM_CHAR)     //如果是字元消息

       {

              if(0xd==wParam)   //如果是Enter鍵

                     return 1;     //不處理就傳回,即不響應Enter鍵

       }

       return oldProc (hwnd,uMsg,wParam,lParam);

              //其它消息仍由原來的回調函數處理

}

說明:此種方法要文本設定文本框的MultiLine 和Want return屬性設為有效。

三、制作一個可以響應WM_MOUSEMOVE消息的按鈕:

1. 建立一個類CHide ,從CButton派生,增加成員對象CHide *m_btnFriend,在類中響應WM_MOUSEMOVE消息,在OnMouseMove中添加代碼:

    ShowWindow(SW_HIDE);

    m_pmyFriend->ShowWindow(SW_NORMAL);

2. 在對話框的頭檔案中加入#include “hide.h”

3. 為對話框上的“确定”和“取消”按鈕添加捆綁變量

i.                     靜态綁定:

a.       用ClassWizard為對話框上的“确定”和“取消”按鈕添加捆綁變量,

     CHide             m_btnOK;

       CHide             m_btnCancel;

并将其中一個按鈕的Visible屬性設為false。

b.       此種方法會在對話框的DoDataExchange函數中添加

       DDX_Control(pDX, IDCANCEL, m_btnCancel);

       DDX_Control(pDX, IDOK, m_btnOK);

c.       在對話框類中的OnInitDialog()中添加代碼:

     m_btnOK.m_pmyFriend=&m_btnCancel;

       m_btnCancel.m_pmyFriend=&m_btnOK;

ii.                   動态綁定:

a.       定義兩個成員變量:

     CHide             m_btnOK;

       CHide             m_btnCancel;

b.       在對話框類中的OnInitDialog()中添加代碼:

     m_btnOK.m_pmyFriend=&m_btnCancel;

m_btnCancel.m_pmyFriend=&m_btnOK;

m_btnOK.SubclassDlgItem(IDOK,this);

m_btnCancel.SubclassDlgItem(IDCANCEL,this);

=======================================================================================================

MFC中如何将焦點設定到指定控件上

我們常常會有這樣的需求,想在視窗打開的時候就将焦點(光标)設定到某個指定的控件上去。

我們都知道設定焦點需要用到setFocus這個方法(幾乎任何程式設計環境都這樣VC,C#,VB,VF都這樣)。

但在VC中我們使用了setfocus并不一定會達到我們需要的效果。

如果我們僅僅設定了setfocus那是不夠的,我們需要将對話框中的OnInitDialog的最後那句return設定為FALSE;

我曾經在對話框中對各個控件的輸入進行校驗檢測,當檢測到不滿足要求的輸入則會彈出對話框提示。

我本來的目的是彈出提示框,但确定後回到窗體中後就将焦點和光标設定到對應的控件上,結果死都上不去。

一開始我以為是Setfocus的設定無效,後面發現是邏輯上錯了,因為當提示框顯示的時候,在提示框彈出前通過SetFocus設定的焦點都将廢棄。

發現這一點後,我将彈出提示框前需要設定獲得焦點的目标控件存儲起來,等提示框顯示完了我仍然可以對該控件進行設定焦點的操作,問題總算解決了。

這裡寫出來,希望大家不要走我這樣的彎路哦!

順便說一下如何擷取到焦點所在的控件的ID号,也許這對大家操作焦點所在控件有作用;

MFC::

CWnd *pWnd = GetFocus(); 

if(pWnd == NULL)

;//no focus

else

UINT nID = pWnd->GetDlgCtrlID();

SDK:

HWND hWnd = ::GetFocus();

if(hWnd == NULL)

;//no focus 

else

UINT nID = ::GetDlgCtrlID(hWnd);

轉載注明出處哦,謝了!

======================================================================================== mfc解決Enter鍵預設關閉視窗的一般方法

在一般情況下編寫的對話框程式,使用者在運作的時候,如果不注意按下了 ENTER 或者 ESC 鍵,程式就會立刻退出,之是以會這樣,是因為按下 Enter鍵時,Windows就會自動去找輸入焦點落在了哪一個按鈕上,當獲得焦點的按鈕的四周将被點線矩形包圍。如果所有按鈕都沒有獲得輸入焦點,Windows 就會自動去尋找程式或資源所指定的預設按鈕(預設按鈕邊框較粗) 。 如果對話框沒有預設按鈕,那麼即使對話框中沒有OK按鈕,OnOK函數也會自動被調用 ,對于一個普通的對話框程式來說, OnOK 函數的調用,以為着程式會立刻退出。為了使 Enter 鍵無效,最簡單的辦法就是将 CExDlg 的 OnOK 函數寫成空函數,然後針對 OK 按鈕寫一個新的函數來響應。 ESC 鍵的原理也是如此,它是預設和 OnCancel 函數映射在一起的。對于 ESC 鍵,需要自己重載   CDialog 類的 PreTranslateMessage 函數,當發現是 ESC 鍵的時候,過濾掉這個消息或者是替換掉這個消息。  

一下是簡單的代碼示例:  

【方法1】 

可以先重載 OnOK 函數  

void CTestDlg::OnOK()

{ // 裡面什麼也不寫 }

然後重載 PreTranslateMessage函數 

把 ESC 鍵的消息,用 RETURN 鍵的消息替換,這樣,按 ESC 的時候,也會執行剛才的 OnOK 函數,這樣問題就可以解決了。  

BOOL CxxxDlg::PreTranslateMessage(MSG* pMsg)

{

if(pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_ESCAPE)

{

pMsg->wParam=VK_RETURN; // 将ESC鍵的消息替換為Enter鍵的消息,這樣,按ESC的時候 

//也會去調用OnOK函數,而OnOK什麼也不做,這樣ESC也被屏蔽 

}

return CDialog::PreTranslateMessage(pMsg);

}

【方法2】 

直接在重載的 PreTranslateMessage 函數中屏蔽回車和 ESC 的消息,和以上方法大同小異:  

BOOL CxxxDlg::PreTranslateMessage(MSG* pMsg)

{

if(pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_ESCAPE) return TRUE;

if(pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_RETURN) return TRUE;

else

return CDialog::PreTranslateMessage(pMsg);

} ==============================================================================

用Enter鍵實作MFC對話框中TAB鍵控件輸入焦點在控件中跳轉的效果

  • 版權聲明:轉載時請以超連結形式标明文章原始出處和作者資訊及本聲明

    http://hcq11.blogbus.com/logs/54217707.html

    近日在為一個資料應用寫資料輸入界面,大量的編輯框要想實作快速的輸入就是有設計良好的符合工作流程的TAB鍵序。相信,不少的人在使用具有大量編輯框的程式時都有這樣的想法和感概。而我這個對話框界面主要輸入的是數字,是以如果使用TAB鍵作編輯框之間的跳轉會在使用數字鍵盤時不友善。是以,就考慮了用Enter鍵來實作TAB鍵的功能。因為MFC對話框的特點,基于MFC對話框的對話框按下Enter鍵會調用架構内的的OnOk成員函數導至退出。即使你去掉IDC_OK按鍵的BS_DEFPUSHBUTTON 屬性也是不行的。

    必須重載WM_GETDEFID,因為當使用者按下Enter鍵時,Windows發送WM_GETDEFID消息來獲得預設的指令ID,Windows再将它作為WM_COMMAND發送,是以重載這個消息必須在高位字中傳回DC_HASDEFID。 如下所例:

BEGIN_MESSAGE_MAP(CMyDlg, CDialog)
    ON_MESSAGE(DM_GETDEFID, OnGetDefID)
    ...
END_MESSAGE_MAP()

LRESULT CMyDlg::OnGetDefID(WPARAM wp, LPARAM lp) 
{
    return MAKELONG(0,DC_HASDEFID); 
} 
           
  • 這是一種方法,另外一種方法是在消息循環處着手處理。重載PreTranslateMessage這個虛函數,将消息隊列中有關鍵盤按下的消息給攔載下來,在架構之前處理WM_KEYDOWN消息。判斷是按下Enter鍵後,我們可以用GetNextDlgTabItem 函數獲得TAB鍵序中下一個或上一個接受TAB鍵的控件句柄。示例代碼如下:
BOOL CKeydownDlg::PreTranslateMessage(MSG* pMsg) 
{
     if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)
     {
           CWnd *mwnd = GetNextDlgTabItem (GetFocus());        //取得目前焦點控件的下一個控件的句柄
           if (mwnd)
           {
                mwnd->SetFocus();        //設定下一件控件得到輸入焦點
                return TRUE;
           }
     }
   return CDialog::PreTranslateMessage(pMsg);
}
           
  •   但是,各位有沒有注意到。這樣處理之後,雖然可以在各個控件之進行輸入焦點的轉移。可是當一個按鈕控件獲得了焦點之後,也是按下Enter鍵。以前我們習慣中或者說預料中按下該按鈕的後應該執行的功能沒有被執行,而焦點又跑到按鈕之後的控件上去了。這就是上面代碼的不足之處,雖然實作了焦點由Enter鍵控件轉移,但是按鈕卻不能用Enter鍵來操作了。隻以用滑鼠,這和大家習慣不合。而且,在快速的資料輸入中不能用Enter鍵來按下這個按鈕卻要用滑鼠來點選也有違當初我們要快速這樣一個目的。是以,還得對以上代碼做适當的修改。代碼如下。

            在這裡增加了對目前焦點控件類的判斷,即如果目前控件是按鈕(Button)那麼就不執行焦點跳轉而是構造一條WM_COMMAND消息發送給程式,讓程式以為是滑鼠點選了該按鈕。進而執行了這個按鈕具有的功能而不是什麼也不做的焦點轉移。

    BOOL CKeydownDlg::PreTranslateMessage(MSG* pMsg) 
    {
          if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)
          {
              CWnd *wnd = GetFocus ();
              if (wnd != NULL)
              {
                   char str[256];
                   CString ClassName = _T("Button");
                   GetClassName (wnd->m_hWnd, str, 256);
                     if (ClassName == str)
                     {
                            UINT i = wnd->GetDlgCtrlID ();
                            SendMessage (WM_COMMAND, i, (LPARAM)wnd->m_hWnd);
                            return TRUE;
                       }
             }
          CWnd *mwnd = GetNextDlgTabItem (wnd);
          if (mwnd)
          {
                mwnd->SetFocus();
               return TRUE;
           }
    }
    
    return CDialog::PreTranslateMessage(pMsg);
    }
    
               
    • TAB ORDER按Enter鍵使下個控件自動獲得焦點

      1.選中主對話框:Layout菜單=>Tab Order(或者直接按Ctrl+D):按你需要的TAB順序依次單擊控件.

      2.按Ctrl+W打開MFC ClassWizard=>Message Maps頁籤:給你的主DLG映射PreTranslateMessage消息處理函數:

    BOOL CYourDlg::PreTranslateMessage(MSG* pMsg) 
    {
        // 順次傳遞焦點,除非目前焦點在确定按鈕上時才響應觸發消息
        if((pMsg->message == WM_KEYDOWN) && (VK_RETURN == (int) pMsg->wParam))
        {
            if(GetFocus()->GetDlgCtrlID() != IDOK)
            {
                pMsg->wParam = VK_TAB;
            }
        }
    
        return CDialog::PreTranslateMessage(pMsg);
    }
    
               

三、解決在無視窗的程式中采用MessageBox彈出的對話框具有模态特性

1、MessageBox第一個參數介紹: hWnd 

[in] Handle to the owner window of the message box to be created. If this parameter is NULL, the message box has no owner window. 

這個hwnd,就給出了meessagebox這個類似或者說就是模态對話框的父視窗,對于模态對話框,樓主應該知道什麼意思了吧, 其他視窗将不會擁有焦點,若hwnd == NULL,  has no owner window當然就有其他視窗會得到焦點, 這跟domodel(NULL),一樣的道理,就是這個,但是不能說他阻塞了其他線程,沒這個說法!

可是我測試了一下,若我第一個參數傳::GetDesktopWindow()

發現他和傳NULL的效果一樣(當然這個效果是指我們表面上看上去的效果)

那這個時候MessageBox是模态還是非模态?模态的話,主視窗就是桌面了嗎?? 2、解決MessageBox(NULL彈出對話框後,按回車預設不能關閉彈出框的方法:

if ( !m_hwndParent )
	{
		m_hwndParent = ::GetForegroundWindow();
	}
	if ( IDOK != m_fnMessageBox(m_hwndParent, TEXT_CONN_FAILE, CAPTION_DISCONN, MB_OK, MB_ICONERROR|MB_SYSTEMMODAL))
	{
		bRet = FALSE;
	}
	else
	{
		bRet = TRUE;
	}