||
对话框窗体的创建有两种方式,一种是基于FRAMEWIN的对话框窗体的创建,另一种是基于WINDOW的对话框窗体的创建。这两种方式创建的对话框返回的对话框句柄当然也是不同的,即:基于FRAMEWIN的对话框返回的句柄为FRAMEWIN控件的句柄;而基于WINDOW的对话框返回的是WINDOW控件的句柄。
具体的创建过程我们来看一下对话框的创建函数GUI_CreateDialogbox()。
/********************************************************************
*
* GUI_CreateDialogbox
********************************************************************/
WM_HWIN GUI_CreateDialogBox(const GUI_WIDGET_CREATE_INFO* paWidget, int
NumWidgets, WM_CALLBACK* cb, WM_HWIN hParent, int x0, int y0)
{
/* 首先创建对话框的窗体,paWidget此时指向对话框创建资源列表中的第一个控件,即:作为对话框的中子控件载体的
WINDOW或者是FRAMEWIN。根据你所创建的对话框资源列表的第一个元素来确定。返回值hDialog
=WINDOW或者FRAMEWIN的句柄*/
WM_HWIN hDialog = paWidget->pfCreateIndirect(paWidget, hParent, x0, y0,
cb);
/* 下面这条语句比较关键。通过WM_GetClientWindow (hDialog)来获得对话框窗体的客户区控件的句柄。
1 基于FRAMEWIN的对话框:FRAMEWIN控件创建时会同时创建一个他的子窗口,即客户区控件。对话框所有其他的空间以这个客户区窗口为父窗口,将所有控件显示在他的上面。所以,此处hDialogClient
=FRAMEWIN客户区的句柄。
2 基于WINDOW的对话框:WINDOW控件创建时没有创建一个所谓的客户区的窗口,所有其他的对话框控件的载体也就还是WINDOW本身。所以hDialogClient
= hDialog */
WM_HWIN hDialogClient = WM_GetClientWindow(hDialog);
WIDGET_OrState(hDialog, paWidget->Flags);
WM_ShowWindow(hDialog);
WM_ShowWindow(hDialogClient);
/* 接下来创建对话框其他的控件*/
while (--NumWidgets > 0) {
WM_HWIN hChild;
/* paWidget++,指向下一个控件资源*/
paWidget++;
/* 看清了,对话框其他控件资源都是客户区hDialogClient 的子窗口*/
hChild = paWidget->pfCreateIndirect(paWidget,
hDialogClient, 0, 0, 0); /* Create child window */
WM_ShowWindow(hChild);
}
/*焦点设置在对话框资源列表中窗体控件的下一个控件 */
WM_SetFocusOnNextChild(hDialog);
WM_SendMessageNoPara(hDialogClient, WM_INIT_DIALOG);
/*返回对话框句柄,说白了就是WINDOW或者FRAMEWIN的句柄*/
return hDialog;
}
解释到这,你只要明白一件事:对话框创建完成后返回的对话框的句柄是WINDOW或者FRAMEWIN的句柄,而不是客户区的句柄(FRAMEWIN才有客户区这一说)。
再来说明一个事情,对于所有控件如:BUTTON、CHECKBOX、FRAMEWIN等等,都有一个原始的回调函数,这个回调函数位于uc/GUI内核中。但是为了扩展控件本身的消息处理能力,uc/GUI允许我们在创建某个控件实体时再给这个对象实体增加一个回调函数。那么这两个回调函数之间存在什么关系呢?笼统的来说,当某个对象实体上发生事件后,事件以消息的形式首先到达控件的原始回调函数。在原始的回调函数中进行一般的消息处理,再通过某种机制来调用对象实体的回调函数进行特殊的消息功能处理。这种机制这里不再细说。接下来进入正题。
1 消息发送
我们在应用uc/GUI做一些具体应用时,uc/GUI中的一般性的消息不会考虑用户的特殊应用。所以,有时候我们需要向对话框发送一些自定义的消息。而向对某个窗体发送我们自定义的消息,可以用以下两个通用的函数来完成:
1 void WM_SendMessage(WM_HWIN
hWin, WM_MESSAGE* pMsg);
2 int WM_BroadcastMessage(
WM_MESSAGE* pMsg);
应用第一个函数函数我们可以向一个特定的窗体发送一条消息,让这个窗体的回调函数来处理我们自定义的消息。
第二个函数是一个以广播的形式向当前所存在的所有窗体句柄发送我们自定义的消息。哈哈,注意了啊!!不是对话框才叫窗体。TEXT、BUTTON、DIALOG、 EDIT等等……所创建的、具有实体句柄的窗体都会被发送这样一条消息。个人感觉可能会引起资源的小小浪费。
2 基于FRAMEWIN对话框的消息处理
为了详细的了解基于FRAMEWIN对话框的消息处理,首先就必须看一下FRAMEWIN的原始回调函数。
下面就是uc/GUI3.91的FRAMEWIN原始回调函数的完整版。
/********************************************************************
*
* Framewin Callback
********************************************************************/
static void _FRAMEWIN_Callback (WM_MESSAGE *pMsg) {
FRAMEWIN_Handle hWin = (FRAMEWIN_Handle)(pMsg->hWin);
FRAMEWIN_Obj* pObj = FRAMEWIN_H2P(hWin);
GUI_RECT* pRect = (GUI_RECT*)(pMsg->Data.p);
POSITIONS Pos;
GUI_HOOK* pHook;
/* Call hook functions */
for (pHook = pObj->pFirstHook; pHook; pHook = pHook->pNext) {
int r;
r = (*pHook->pHookFunc)(pMsg);
if (r) {
return; /* Message handled */
}
}
switch (pMsg->MsgId) {
case WM_HANDLE_DIALOG_STATUS:
if (pMsg->Data.p)
{
/* set pointer to Dialog status */
pObj->pDialogStatus =
(WM_DIALOG_STATUS*)pMsg->Data.p;
} else
{
/* return pointer to Dialog status */
pMsg->Data.p =
pObj->pDialogStatus;
}
return;
case WM_PAINT:
_Paint(pObj);
break;
case WM_TOUCH:
_OnTouch(hWin, pObj, pMsg);
return;
/*
Return here ... Message handled */
case WM_GET_INSIDE_RECT:
FRAMEWIN__CalcPositions(pObj, &Pos);
*pRect = Pos.rClient;
return;
/*
Return here ... Message handled */
case WM_GET_CLIENT_WINDOW: /* return
handle to client window. For most windows, there is no seperate client window,
so it is the same handle */
pMsg->Data.v = (int)pObj->hClient;
return;
/*
Return here ... Message handled */
case WM_NOTIFY_PARENT:
if (pMsg->Data.v == WM_NOTIFICATION_RELEASED) {
WM_MESSAGE Msg;
Msg.hWinSrc = hWin;
Msg.Data = pMsg->Data;
Msg.MsgId = WM_NOTIFY_PARENT_REFLECTION;
WM_SendMessage(pMsg->hWinSrc, &Msg);
}
return;
case
WM_SET_FOCUS:
/* We have received or lost focus */
if (pMsg->Data.v == 1) {
if (WM_IsWindow(pObj->hFocussedChild)) {
WM_SetFocus(pObj->hFocussedChild);
} else {
pObj->hFocussedChild =
WM_SetFocusOnNextChild(pObj->hClient);
}
FRAMEWIN_SetActive(hWin, 1);
pMsg->Data.v =
0;
/* Focus could be accepted */
} else {
FRAMEWIN_SetActive(hWin, 0);
}
return;
case WM_TOUCH_CHILD:
/* If a child of this framewindow has been touched and
the frame window was not active,
the framewindow will receive the
focus.
*/
if (!(pObj->Flags & FRAMEWIN_SF_ACTIVE)) {
const WM_MESSAGE * pMsgOrg;
const GUI_PID_STATE * pState;
pMsgOrg = (const
WM_MESSAGE*)pMsg->Data.p; /* The original
touch message */
pState = (const
GUI_PID_STATE*)pMsgOrg->Data.p;
if (pState)
{ /* Message may not have
a valid pointer (moved out) ! */
if (pState->Pressed) {
WM_SetFocus(hWin);
}
}
}
break;
case WM_NOTIFY_CHILD_HAS_FOCUS:
_OnChildHasFocus(hWin, pObj, pMsg);
break;
case WM_DELETE:
GUI_DEBUG_LOG("FRAMEWIN:
_FRAMEWIN_Callback(WM_DELETE)\n");
GUI_ALLOC_FreePtr(&pObj->hText);
break;
}
/* Let widget handle the standard messages */
if (WIDGET_HandleActive(hWin, pMsg) == 0) {
return;
}
WM_DefaultProc(pMsg);
}
好了,我们主要来看一下其中的关于消息处理部分,即switch
(pMsg->MsgId) {}部分。其中,case的结尾有的是直接的函数返回,有的却是跳出swtch。二者有什么区别呢?本人理解的是以return结尾的消息,对话框接收到这类消息后,经FRAMEWIN
的原始回调函数_FRAMEWIN_Callback处理后,这个消息就已经划上了句号,这个消息不再产生任何其他行为。而以break结尾的消息,经过swtch中的处理过程后,这个消息还没有完,接下来看函数末尾绿色部分语句。这里WIDGET_HandleActive(hWin,
pMsg),又将这个消息进行了一般消息的处理,如果一个消息在WIDGET_HandleActive中得到处理则这个函数返回0。返回0后,_FRAMEWIN_Callback关于消息的处理也就告一段落,也就是说这个消息的处理过程划上了句号。如果一个消息没有在WIDGET_HandleActive中得到处理,则程序往下执行,这就到了WM_DefaultProc(pMsg)。
WM_DefaultProc(pMsg)函数也是对于一些GUI中的特定消息进行了一般处理。但是有一个特殊的消息,那就是按键消息的处理比较特别,按键消息会通过这个函数将按键消息通知给他的父窗口来处理。处理过程如下:
case WM_KEY:
WM_SendToParent(hWin, pMsg);
return;
/* Message handled */
其它的消息到此为止,湮灭在整个GUI中了。
说到这里,你会发现一个问题。这个函数好像跟我们对话框实体对象的回调函数没有任何关系。也就是说,如果一个我们自定义的消息发送个我们的对话框的实体对象后,实际上首先是由_FRAMEWIN_Callback来处理的。经过_FRAMEWIN_Callback处理后,我们的实体对象回调函数不会得到任何机会去处理它。最后的结果就是我们自定义的消息会湮灭在整个GUI中。
得到这样一个结论后,我们再来看一个回调函数,也就是FRAMEWIN的客户区句柄的回调函数。
/*********************************************************************
*
* Client Callback
********************************************************************/
static void FRAMEWIN__cbClient(WM_MESSAGE* pMsg) {
WM_HWIN hWin = pMsg->hWin;
WM_HWIN hParent = WM_GetParent(pMsg->hWin);
FRAMEWIN_Obj* pObj = FRAMEWIN_H2P(hParent);
WM_CALLBACK* cb = pObj->cb;
switch (pMsg->MsgId) {
case WM_PAINT:
if (pObj->Props.ClientColor != GUI_INVALID_COLOR) {
LCD_SetBkColor(pObj->Props.ClientColor);
GUI_Clear();
}
/* Give the user callback a chance to draw.
* Note that we can not run into the bottom part,
as this passes the parents handle
*/
if (cb) {
WM_MESSAGE Msg;
Msg = *pMsg;
Msg.hWin = hWin;
(*cb)(&Msg);
}
return;
case WM_SET_FOCUS:
if (pMsg->Data.v) { /* Focus
received */
if (pObj->hFocussedChild &&
(pObj->hFocussedChild != hWin)) {
WM_SetFocus(pObj->hFocussedChild);
} else {
pObj->hFocussedChild =
WM_SetFocusOnNextChild(hWin);
}
pMsg->Data.v =
0; /* Focus change accepted */
}
return;
case WM_GET_ACCEPT_FOCUS:
WIDGET_HandleActive(hParent, pMsg);
return;
case WM_KEY:
if (((const
WM_KEY_INFO*)(pMsg->Data.p))->PressedCnt > 0) {
int Key = ((const
WM_KEY_INFO*)(pMsg->Data.p))->Key;
switch (Key) {
case GUI_KEY_TAB:
pObj->hFocussedChild =
WM_SetFocusOnNextChild(hWin);
return;
}
}
break;
/*
Send to parent by not doing anything */
case WM_GET_BKCOLOR:
pMsg->Data.Color = pObj->Props.ClientColor;
return;
/* Message handled */
case WM_GET_INSIDE_RECT:
/* This should not be passed to parent ... (We do not want parents
coordinates)*/
case
WM_GET_ID:
/* This should not be passed to parent ... (Possible recursion problem)*/
case WM_GET_CLIENT_WINDOW: /* return
handle to client window. For most windows, there is no seperate client window,
so it is the same handle */
WM_DefaultProc(pMsg);
return; /*
We are done ! */
}
/* Call user callback. Note that the user callback gets the handle
of the Framewindow itself, NOT the Client. */
if (cb) {
pMsg->hWin = hParent;
(*cb)(pMsg);
} else {
WM_DefaultProc(pMsg);
}
}
看一下上面的程序,我来问一个问题。如果一个消息在switch中没有得到匹配,直截了当的说,如果客户区句柄收到了一个我们自定义的消息后,这个Framewin的客户区句柄会做什么?看函数最后部分的绿色语句,你会不会恍然大悟啊。呵呵,我们终于找到答案了。最后的答案就是,自定义消息不会在switch中得到任何处理。if
(cb)会判断Framewin的回调函数是否存在,如果存在则会调用这个回调函数来处理这则消息。哈哈!!我们的消息终于有人管了。
是不是觉得结果来的太突然,那好,我再来对其中的细节进行一下解释。
cb------
看程序开头部分的绿色语句WM_CALLBACK*
cb,我们可以知道:
Cb是个回调函数的指针。指向哪个回调函数呢?看下面这条语句,这条语句是得到当前收到消息的客户区句柄的父窗口的句柄,客户区的父窗口是什么啊?很简单,就是他所对应的FRAMEWIN的实体对象的句柄,进一步说,就是对话框的句柄。
WM_HWIN hParent = WM_GetParent(pMsg->hWin);
再来看cb的赋值过程:这个指针最后指向了我们所创建的对话框实体对象的回调函数,(不是FRAMEWIN的原始回调函数,看清了。)
WM_CALLBACK* cb = pObj->cb;
hParent------
将这个消息发送个句柄为hParent的回调函数,而不是客户区的句柄。这点很关键。
就这样,我们自定义的消息回到了我们在创建对话框时所赋予的那个回调函数中去了。在定义的回调函数中,对于自定义的消息就可以任你处置了。
你是不是还有疑问,那我们该怎么样向一个实体对话框发送自定义的消息呢?往下看。
回过头来看一下这两个发送消息的函数:
1 void WM_SendMessage(WM_HWIN hWin, WM_MESSAGE* pMsg);
2 int WM_BroadcastMessage( WM_MESSAGE* pMsg);
如果我们用第一个函数向一个对话框实体发送自定一消息,参数WM_HWIN
hWin我们应该传递对话框的句柄;而参数WM_MESSAGE* pMsg应该传递我们自定义消息的地址。但是,我们前面说过,对话框的句柄其实就是FRAMEWIN的句柄,如果这样发送一个自定义消息给对话框,我们自定义的实体对话框回调函数是收不到的啊。怎么办,在发送个特定对话框句柄一个消息之前要简单处理一下程序如下:
/* hWin 就是要想这个特定的对话框实体发送一个消息,通过它我们首先得到他的客户区句柄。但是如果我们的对话时基于WINDOW创建的话,这样也没关系,因为他返回的依然是WINDOW的句柄,也就是依然是对话框的句柄*/
WM_HWIN hDialogClient = WM_GetClientWindow(hWin);
/*将这个消息发送个给对话框客户区的句柄,最终会在我们自定义的实体对话框的回调函数中收到它。 */
WM_SendMessage(hDialogClient, pMsg);
如果我们用第二个函数向一个对话框实体句柄发送一条自定义的消息时,这个函数会向所有当前存在的有效句柄发送自定义的消息,当然也会包括对话框客户区的原始回调函数。所以我们同样也可以收到我们的自定义消息。
好了,关于基于FRAMEWIN创建对话框的消息处理就讲到这里了,希望能对大家在uc/GUI的应用开发中有所帮助。