本文共 7622 字,大约阅读时间需要 25 分钟。
所有的窗口均继承自wxTopLevelWindows
:
WXDLLIMPEXP_DATA_CORE(wxWindowList) wxTopLevelWindows;wxTopLevelWindows
在使用Frame窗口的时候,我们一般从wxFrame继承,创建时通过调用Create
方法进行创建:
debugWXFrame::debugWXFrame(wxWindow* parent,wxWindowID id){ ... Create(parent, id, wxEmptyString, wxDefaultPosition, ...); ...}
我们先看下wxFrame
类的继承关系:
wxFrame -> wxFrameBase -> wxTopLevelWindow -> -> wxTopLevelWindowMSW(wxTopLevelWindowNative) -> wxTopLevelWindowBase -> wxNonOwnedWindow -> wxNonOwnedWindowBase -> wxWindow
注:wxTopLevelWindow继承自wxTopLevelWindowNative,但是wxTopLevelWindowNative是宏定义,在include/wx/toplevel.h
文件中有如下定义:
#if defined(__WXMSW__) #include "wx/msw/toplevel.h" #define wxTopLevelWindowNative wxTopLevelWindowMSW
接着我们看看窗口创建的过程,wxFrame
调用wxTopLevelWindow::Create
实现窗口的创建:
wxTopLevelWindows
队列中,这个全局变量保存了当前所有topWindow的指针;CreateBase
用于设定基础参数;CreateDialog
或者CreateFrame
执行创建。bool wxFrame::Create(wxWindow *parent, ...){ if ( !wxTopLevelWindow::Create(parent, id, title, pos, size, style, name) ) return false; ...}bool wxTopLevelWindowMSW::Create(wxWindow *parent, ...){ // notice that we should append this window to wxTopLevelWindows list // before calling CreateBase() as it behaves differently for TLW and // non-TLW windows wxTopLevelWindows.Append(this); bool ret = CreateBase(parent, id, pos, sizeReal, style, name); if ( !ret ) return false; if ( parent ) parent->AddChild(this); if ( GetExtraStyle() & wxTOPLEVEL_EX_DIALOG ) { ... ret = CreateDialog(dlgTemplate, title, pos, sizeReal); free(dlgTemplate); } else // !dialog { ret = CreateFrame(title, pos, sizeReal); } return ret;}
继续看CreateFrame
,根据继承关系我们可以找到实际调用的是wxTopLevelWindowMSW::CreateFrame
,这个函数调用MSWCreate
实现窗口的创建:
bool wxTopLevelWindowMSW::CreateFrame(const wxString& title, const wxPoint& pos, const wxSize& size){ WXDWORD exflags; WXDWORD flags = MSWGetCreateWindowFlags(&exflags); const wxSize sz = IsAlwaysMaximized() ? wxDefaultSize : size; return MSWCreate(MSWGetRegisteredClassName(), title.t_str(), pos, sz, flags, exflags);}
我们看下这个函数的参数MSWGetRegisteredClassName
,这个函数用于注册窗口类
注:Windows程序在创建自定义窗口时,需要先调用::RegisterClass注册该窗口类,创建自定义的窗口类时,在使用该窗口类前必须注册该窗口类,使用RegisterClass注册窗口类。
MSWGetRegisteredClassName
通过调用wxApp::GetRegisteredClassName
进行注册:
wxWindow
,的消息处理函数是wxWndProc
;gs_regClassesInfo
中注:这里注册了两种窗口类型,一个是带有
CS_HREDRAW | CS_VREDRAW
标记的,另外一种不带此标记,当窗口创建时携带wxFULL_REPAINT_ON_RESIZE
标记时,会使用不带标记窗口类型,否则使用带有标记的窗口类型:
实现代码:
/* static */const wxChar *wxWindowMSW::MSWGetRegisteredClassName(){ return wxApp::GetRegisteredClassName(wxT("wxWindow"), COLOR_BTNFACE);}const wxChar *wxApp::GetRegisteredClassName(const wxChar *name, ...){ const size_t count = gs_regClassesInfo.size(); for ( size_t n = 0; n < count; n++ ) { if ( gs_regClassesInfo[n].regname == name ) return gs_regClassesInfo[n].regname.c_str(); } // we need to register this class WNDCLASS wndclass; wndclass.lpfnWndProc = (WNDPROC)wxWndProc; wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | extraStyles; ClassRegInfo regClass(name); wndclass.lpszClassName = regClass.regname.t_str(); if ( !::RegisterClass(&wndclass) ) ... wndclass.style &= ~(CS_HREDRAW | CS_VREDRAW); wndclass.lpszClassName = regClass.regnameNR.t_str(); if ( !::RegisterClass(&wndclass) ) ... gs_regClassesInfo.push_back(regClass); return gs_regClassesInfo.back().regname.t_str();}
继续看窗口的实际创建MSWCreate
函数:
wxFULL_REPAINT_ON_RESIZE
决定使用那种窗口类;::CreateWindowEx
创建真正的窗口,窗口HANDLE保存在m_hWnd
中。SubclassWin(m_hWnd)
用于保存此窗口的handle到系统中,以后APP那边收到消息后,通过这个handle来查找哪个wxWindow与此Handle关联,后面会详述。bool wxWindowMSW::MSWCreate(const wxChar *wclass, ...){ int x, y, w, h; (void)MSWGetCreateWindowCoords(pos, size, x, y, w, h); int controlId = style & WS_CHILD ? GetId() : 0; wxString className(wclass); if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) ) { className += wxApp::GetNoRedrawClassSuffix(); } wxWindowCreationHook hook(this); m_hWnd = (WXHWND)::CreateWindowEx ( extendedStyle, className.t_str(), ... ); SubclassWin(m_hWnd); return true;}
另外需要关注的一个调用就是wxWindowCreationHook hook(this);
这个类用于将当前窗口的指针写到全局变量gs_winBeingCreated
中,在wxWinProc
函数中有使用,下文有讲述。
wxWindowCreationHook::wxWindowCreationHook(wxWindowMSW *winBeingCreated){ gs_winBeingCreated = winBeingCreated;}
wxWidgets是基于Windows的Win32API来实现窗口操作的,包括消息机制,有一个问题就是wxWindow类如何与Win32的窗口类关联起来呢?
其实wxWidgets的实现很简单,通过一个全局Map表来保存,索引是HWND,目标是wxWindow指针,这样就可以通过Win32的HANDLE直接找到wxWindow了。
wxGUIEventLoop::PreProcessMessage
中我们看到有如下代码:
bool wxGUIEventLoop::PreProcessMessage(WXMSG *msg){ HWND hwnd = msg->hwnd; wxWindow *wndThis = wxGetWindowFromHWND((WXHWND)hwnd);
其中wxGetWindowFromHWND
就是用于从HWND映射到wxWindow指针,这个函数实现中,通过调用wxFindWinFromHandle
获取对应的窗口,这里还有处理就是如果当前的HWND找不到窗口,则使用该窗口的父窗口再去查找。
extern wxWindow *wxGetWindowFromHWND(WXHWND hWnd){ HWND hwnd = (HWND)hWnd; wxWindow *win = NULL; if ( hwnd ) { win = wxFindWinFromHandle(hwnd); } while ( hwnd && !win ) { hwnd = ::GetParent(hwnd); win = wxFindWinFromHandle(hwnd); } return win;}
继续看wxFindWinFromHandle
,它是通过在gs_windowHandles
中查找,接着我们再看下gs_windowHandles
的定义,他是一个HashMap表,类似于std::map
的功能,具体实现可自行看代码:
wxWindow *wxFindWinFromHandle(HWND hwnd){ WindowHandles::const_iterator i = gs_windowHandles.find(hwnd); return i == gs_windowHandles.end() ? NULL : i->second;}WX_DECLARE_HASH_MAP(HWND, wxWindow *, wxPointerHash, wxPointerEqual, WindowHandles);WindowHandles gs_windowHandles;
管理机制有了,我们看看这个窗口是什么时候注册的呢?总的来说,HWND与wxWindows窗口关联的时机有两个:
wxWindowMSW::SubclassWin
关联;wxWndProc
中关联。在窗口创建完成后,调用SubclassWin
将此窗口放到全局变量gs_windowHandles
中,函数中调用wxAssociateWinWithHandle
进行HWND和wxWindow的关联:
void wxWindowMSW::SubclassWin(WXHWND hWnd){ wxAssociateWinWithHandle(hwnd, this); // we're officially created now, send the event wxWindowCreateEvent event((wxWindow *)this); (void)HandleWindowEvent(event);}
继续追踪wxAssociateWinWithHandle
,这个是一个全局函数,就是将数据关联起来:
void wxAssociateWinWithHandle(HWND hwnd, wxWindowMSW *win){ gs_windowHandles[hwnd] = (wxWindow *)win;}
窗口消息处理函数统一为wxWndProc
,这个函数在进来后首先根据HWND查找wxWindows,查找不到则直接关联此HWND到最新创建的窗口上。
关键全局变量 gs_winBeingCreated ,这个变量在 wxWindowMSW::MSWCreate 中调用,创建窗口时通过wxWindowCreationHook构造函数将自己写到gs_winBeingCreated中这样在第一次收到消息,并且找不到HWND对应的wxWindows时,在这里关联进去。
// Main window procLRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){ wxWindowMSW *wnd = wxFindWinFromHandle(hWnd); // 关联HWND和wxWindows if ( !wnd && gs_winBeingCreated ) { wxAssociateWinWithHandle(hWnd, gs_winBeingCreated); wnd = gs_winBeingCreated; gs_winBeingCreated = NULL; wnd->SetHWND((WXHWND)hWnd); } LRESULT rc; if ( wnd && wxGUIEventLoop::AllowProcessing(wnd) ) rc = wnd->MSWWindowProc(message, wParam, lParam); else rc = ::DefWindowProc(hWnd, message, wParam, lParam); return rc;}
在windows系统中,一旦调用CreateEx函数创建窗口完成后,窗口就会立刻收到消息,此时就会调用wxWndProc
进行处理,问题是,此时还没有调用wxWindowMSW::SubclassWin
进行关联,所以解决办法就是在使用的时候立刻执行关联,这个关联的窗口通过全局变量来传递。
wxWidgets提供了一套完整的窗口管理机制,有效的与Windows系统结合起来,形成了很好的中间层,彻底屏蔽了Win32API,这样用户无论在哪个平台上编写GUI代码,都无需知道平台信息。
转载地址:http://voyfz.baihongyu.com/