3

我正在开发一个 C++ winapi 应用程序,我正在努力SetFocus()为我工作。我创建主窗口,WS_OVERLAPPEDWINDOW | WS_VISIBLE然后在其中创建它的子窗口(几个按钮和我自己的编辑控件版本的开头)WS_CHILD | WS_VISIBLE,除了焦点问题之外,它们都工作正常。

在我的搜索中,我一直在努力寻找有关如何处理 Focus 的内容。当所有窗口都被创建时,它们会单独接收WM_SETFOCUS消息,我通过创建插入符号在我的编辑控件中处理此消息,但是似乎孩子们从未收到WM_KILLFOCUS消息,因此插入符号永远不会被破坏。

这就是我的问题所在:我希望主父窗口最初具有焦点,并且在我的编辑控件中没有插入符号,然后当单击子编辑控件时它具有焦点,然后当主窗口单击它应该然后再次获得焦点,依此类推。

所以我最初的想法是SetFocus()在处理消息时将焦点设置到主窗口,WM_CREATE但这似乎不起作用:孩子没有收到WM_KILLFOCUS消息。

我的下一个想法是,也许父母必须处理传递WM_KILLFOCUS给适当的孩子,所以我写了一个方法来为我做这件事,但孩子们仍然没有收到WM_KILLFOCUS消息。

所以我最好的猜测是我没有在我的 WndProc 中正确处理消息。

我创建了自己的 Window 类,并通过以下 WndProc 将消息分发到适当的类:

LRESULT CALLBACK CBaseWindow::stWinMsgHandler(
    HWND hwnd, 
    UINT uMsg, 
    WPARAM wParam,  
    LPARAM lParam)
{
    CBaseWindow* pWnd;

    if (uMsg == WM_NCCREATE)
    {       
        SetWindowLong(hwnd, GWL_USERDATA, (long)((LPCREATESTRUCT(lParam))->lpCreateParams));
    }

    pWnd = GetObjectFromWindow(hwnd);

    if (pWnd)
        return pWnd->WinMsgHandler(hwnd, uMsg, wParam, lParam);
    else
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
 }

然后每个类都有自己的 WndProc,当消息进来时我会在其中处理它们。

那么有人对我有什么想法吗?

如果我打算这样做是完全错误的方式,或者如果我没有遵循最佳实践,请说出来,我这样做是为了学习,所以开枪。

[更新]

好的,这里有一些代码来演示这个问题:

主文件

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "MainWnd.h"

int WINAPI WinMain(
    HINSTANCE hInstance, 
    HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, 
    int nCmdShow)
{       
    MainWnd wnd(hInstance);

    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0 ) > 0)
    {       
        TranslateMessage(&msg); 
        DispatchMessage(&msg);
    }   
    return 0;
}

BaseWindow.cpp

 #include "BaseWindow.h"

 //...

 LRESULT CALLBACK CBaseWindow::stWinMsgHandler(
       HWND hwnd, 
       UINT uMsg, 
       WPARAM wParam, 
       LPARAM lParam)
 {
       CBaseWindow* pWnd;

       if (uMsg == WM_NCCREATE)
       {        
             SetWindowLong(hwnd, 
                  GWL_USERDATA, 
                  (long)((LPCREATESTRUCT(lParam))->lpCreateParams));
   }

        pWnd = GetObjectFromWindow(hwnd);

       if (pWnd)
             return pWnd->WinMsgHandler(hwnd, uMsg, wParam, lParam);
       else
             return DefWindowProc(hwnd, uMsg, wParam, lParam);
 }

 BOOL CBaseWindow::Create(DWORD dwStyles, RECT* rect)
 {  
      m_hwnd = CreateWindow(        
           szClassName, 
           szWindowTitle, 
           dwStyles, 
           rect->left, 
           rect->top, 
           rect->right - rect->left, 
           rect->bottom - rect->top, 
           NULL, 
           NULL, 
           hInstance, 
           (void *)this);   

     return (m_hwnd != NULL);
 }

MainWnd.cpp

#include "MainWnd.h"
#define WIDTH 400
#define HEIGHT 400

MainWnd::MainWnd(HINSTANCE hInst): CBaseWindow(hInst), hInstance(hInst)
{
     SetWindowTitle(_T("Main Window"));

     WNDCLASSEX wcx;
     FillWindowClass(&wcx);                 

     if(RegisterWindow(&wcx))
     {      
         RECT rc;
     BuildRect(&rc);

         if(Create(WS_OVERLAPPEDWINDOW | WS_VISIBLE, &rc))
         {          
               customTextBox = new CustomTextBox(hInst, m_hwnd);            
         }
     }  
 }

 void MainWnd::FillWindowClass(WNDCLASSEX *wcx)
 {
      wcx->cbSize = sizeof(WNDCLASSEX);
      wcx->style = CS_HREDRAW | CS_VREDRAW | CS_DROPSHADOW;                     
      wcx->lpfnWndProc = CBaseWindow::stWinMsgHandler;          
      wcx->cbClsExtra = 0;                                      
      wcx->cbWndExtra = 0;                                      
      wcx->hInstance = hInstance;                                   
      wcx->hIcon = LoadIcon(NULL, IDI_APPLICATION);             
      wcx->hCursor = LoadCursor(NULL, IDC_ARROW);
      wcx->hbrBackground = CreateSolidBrush(RGB(255,255,255));      
      wcx->lpszMenuName = NULL;                                 
      wcx->lpszClassName = _T("MainWindow");
      wcx->hIconSm = LoadIcon(NULL, IDI_APPLICATION);
 }

 LRESULT CALLBACK MainWnd::WinMsgHandler(
       HWND hwnd, 
       UINT uMsg, 
       WPARAM wParam, 
       LPARAM lParam)
 {          
       switch (uMsg)
       {
       case WM_DESTROY:
            delete customTextBox;
            PostQuitMessage(0);     
            break;
       case WM_LBUTTONUP:
            SetFocus(hwnd);
            break;
       default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
       }
       return 0;
 }

自定义文本框.cpp

 #include "CustomTextBox.h"

 CustomTextBox::CustomTextBox(
      HINSTANCE hInst, 
      HWND hParent): CBaseWindow(hInst), 
                 hParent(hParent),
 {  
      WNDCLASSEX wcx;
      CreateWndClassEX(wcx);
      if(RegisterWindow(&wcx))
      {
           RECT clientRect;
           CreateClientRect(clientRect);
           CreateChild(WS_VISIBLE | WS_CHILD | WS_BORDER | WS_TABSTOP, &clientRect, hParent);       
      }
 }

 void CustomTextBox::CreateWndClassEX(WNDCLASSEX& wcx)
 {
      wcx.cbSize = sizeof(WNDCLASSEX);
      wcx.style = CS_HREDRAW | CS_VREDRAW;    
      wcx.lpfnWndProc = CBaseWindow::stWinMsgHandler;
      wcx.cbClsExtra = 0;                                           
      wcx.cbWndExtra = 0;
      wcx.hInstance = hInstance;
      wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
      wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
      wcx.hbrBackground = CreateSolidBrush(RGB(255,255,255));
      wcx.lpszMenuName = NULL;
      wcx.lpszClassName = _T("Edit Control");
      wcx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
 }

 LRESULT CALLBACK CustomTextBox::WinMsgHandler(
      HWND hwnd, 
      UINT uMsg, 
      WPARAM wParam, 
      LPARAM lParam)
 {
      switch(uMsg)
      {
      /* Handling the caret */
      case WM_SETFOCUS:
           CreateCaret(hwnd, NULL, 0, nWindowY); 
           SetCaretPos(GetEndOfLinePoint(), nCaretPosY * nCharY); 
           ShowCaret(hwnd); 
           return 0;    
      case WM_MOUSEACTIVATE:
           SetFocus(hwnd);
           return MA_ACTIVATE;
      case WM_KILLFOCUS:
           DestroyCaret();
           return 0;
      default:
           return DefWindowProc(hwnd, uMsg, wParam, lParam);
      }         
 }

发现

在编写此代码时,我遇到了问题的原因之一:在我的实际应用程序中,我没有标题栏,因此要将发送WM_NCLBUTTONDOWN到主窗口的窗口移动到WM_LBUTTONDOWN

SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL);

然后我SetFocus()在这之后有了我的,这不起作用,但如果我切换它们并处理第SetFocus()一个,那么点击确实会改变焦点。

但是,最初设置焦点仍然存在问题。在启动应用程序后的那一刻,自定义编辑控件仍然显示插入符号,即使它没有焦点,您需要单击它以使其获得焦点,从而它将接收键盘输入。之后,焦点按需要工作:如果我点击主窗口,它就有焦点;如果我点击自定义编辑,它有焦点等。

4

1 回答 1

1

好的,事实证明我的整个问题都在于那条线SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL);一旦我切换了:

case WM_LBUTTONDOWN:                        
    SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL);       
    SetFocus(hwnd);
break;

到:

case WM_LBUTTONDOWN:                        
    SetFocus(hwnd);
    SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL);
break;

一切都走到了一起。

于 2012-11-16T07:53:19.910 回答