数据窗口揭秘:未公开的数据窗口事件
数据窗口揭秘:未公开的数据窗口事件
(作者:Mark Brown)
到目前为止,PB的数据窗口控件仍是PB众多控件中功能最强大,最复杂的控件。
数据窗口固有的行为
大家对数据窗口固有的行为都很熟悉。不知道你发现没有,几乎不可能覆盖数据窗口固有的行为。它有自己的一套!
事实确实如此,数据窗口控件确实有自己的固有行为。数据窗口控件是一个完全独立的子窗口,它可以显示信息,可以独立于父窗口处理Windows消息。然而,与别的并不复杂的控件(例如:commandbuttons,statictexts,singlelineedits)一样,数据窗口控件通过"Notification"Windows消息来与外界(父窗口)联系.由于可以通过预定义的PB事件在数据窗口控件与用户接口之间沟通,PB开发者大多数情况下不会用到这些内部消息。正是由于这些原因,SYBASE公司选择了不公开这些内部消息。显然,他们没有想到有人会使用它们。
未公开的数据窗口消息
然而,在许多场合下这些未公开的消息还是很有用的。在一个父窗口有多个数据窗口控件时会很有用的。例如,为了共享某些代码,你可以用父窗口级的全局的不同的数据窗口事件来替代创建用户对象。 最明显的例子是在数据窗口中用到DDDW显示风格,你将发现这些未公开的消息会很有用处,它们可以用来处理这些子数据窗口。 在介绍这些消息之前,需要简单描述一下PB事件与Windows3.1消息之间的区别。
PB事件与Windows3.1消息之间的区别
除了GroupBox控件和不同的绘画对象(Line,Oval,Rectangle以及RoundRectangle)以外,每一个在PB画板中可用的Windows控件都有一个或几个事件。PB预定义的事件与Windows3.1的消息并不是完全一致。实际上,大多数的PB事件只是简单的接受类似的Windows消息,了解这一点对于以后掌握还没有在PB控件中定义的Windows3.1消息很重要。
Windows消息类别
消息分为两类:传送消息及通知消息。传送消息即是传送给Windows控件去执行某个特定的动作,例如:重新设定Checkbox的选项。这种消息也可以用来确定特定控件的状态信息,如:检测命令按钮是否是高亮显示。另一方面,通知消息用来在应用和Window控件之间交互或通报。例如:当用户在数据窗口控件中点击鼠标时,控件通知它的父窗口它将得到输出焦点(假定它还没有得到焦点)。这个通知消息对应于数据窗口的Getfocus事件。
PB消息的内部执行
为了便于举例说明这些通知消息是如何在PB内部执行的,我们用命令按钮控件来检测消息是如何运转的。
当用户点击命令按钮控件时,Windows依次发送一个wm_command消息。这个通知消息发送给用户选中的子窗口的父窗口(命令按钮,多行编辑框,等等,实际上都是子窗口)。
在内部处理中,每一个PB窗口都有一个负责正确把Windows消息传送到PB事件中的Windows消息处理函数(对于有经验的Windows编程人员来说是WndProc())。也就是PB的内部消息处理函数负责解释wm_command消息的意义。对于命令按钮控件来说,控件的句柄(hWnd),以及通知代码bn_clicked作为wm_command的参数被传送。通知代码bn_clicked由名字就可知道这是一个显示鼠标点击按钮控件按钮的通知代码。接受到wm_command消息后,PB由Windows句柄可知用户选中的是哪一个命令按钮。在接受到bn_clicked通知代码后,PB触发选中控件的Clicked事件。稍后我们将要告诉你如何自己处理命令按钮的通知消息。
为何不用PB的事件?
既然PB已经定义了许多与Windows3.1消息对应的事件,直接使用PB定义的事件不是比截取wm_command消息更简单吗?对于标准Windows控件来说,确实如此。然而,考虑一下子数据窗口(如:DDDW下拉数据窗口),这种特殊的控件只会触发父数据窗口的ItemChanged事件,并且这种情况仅仅发生在用户改变了下拉数据窗口的项目时会发生。然而,问题出来了,如果下拉数据窗口有多个重复的显示值会发生什么呢?仅当选择第一值时会触发Itemchanged事件,选择以后的任何一个都不会触发任何事件。(举个例子比较容易明白:一张产品代码表:
编码 产品名称
01001 渣油
01002 氢气
01003 硫酸
02001 氢气
02002 汽油
02003 柴油
上表中有两个“氢气”,”编码“的前两个数值:01:原料;02:半成品;可以看到”产品名称“中有两个”氢气“,一个是原料,一个是半成品。用下拉数据窗口,显示值是”产品名称“,数据值是”编码“,那么你将看到你将无法选中半成品的”氢气“。
这种情况下修改编码表肯定不是好办法。唯一的办法是自己截获数据窗口控件的通知消息。
截获wm_command消息
你可以从本站电子书籍的one.pdf中看到wm_command消息对应PB的pbm_command事件,为了截获不同的数据窗口通知消息,你必须首先为含有数据窗口的父窗口定义一用户自定义事件”ue_command",wm_command消息的参数会通过PB的全局变量message对象来传递。
当我们引用的Windows的”消息“时,我们实际指的是一种用来传给不同的消息处理函数(还记得WndProc()吗?)的小的数据结构。PB的message对象的前四个属性与Windows的内部消息结构的前四个属性对应。我们需要注意的是属性:message.longparm.
如果想要了解wm_command消息的详细描叙及用途,可以参考WindowsSDK文档(软件开发包)。我们想要了解的是如何用这个消息来通知应用用户选择了一个子窗口控件。
当这个消息传给应用时,消息的long型参数包含两部分信息:低字节是子控件的窗口句柄,高字节是通知代码(如:bn_clicked).这里有一个例子:
// Script for ue_command event (WM_COMMAND)
uint hWndChildControl, nNotifyCode
// Get the child control's window handle
hWndChildControl = IntLow(Message.LongParm)
// Was the message sent from the OK Button?
IF hWndChildControl = Handle(cb_ok) THEN
// Get the notification code sent by the control
nNotifyCode = IntHigh(Message.LongParm)
// Is it a BN_CLICKED message?
IF nNotifyCode = 0 THEN MessageBox('WM_COMMAND', 'BN_CLICKED')
END IF
END IF
当你使用这种方法处理Windows消息时需要注意的是:是PB的内部消息处理函数接受并处理Windows消息,然后触发相关的PB事件并执行脚本。这也适用于wm_command消息本身。也就是说,如果在与你截获的通知消息对应的PB事件中有脚本,那么脚本会最先执行(在执行ue_command的脚本之前)。
未公开的数据窗口消息
将近有四十个未公开的数据窗口控件通知代码,对学者,好象有点多。然而,这其中的许多对应你平时已经很熟悉的PB事件。与我们已经讨论的别的Window控件一样,数据窗口控件发送通知代码给wm_command消息。
下面是这些未公开的数据窗口的通知代码,对应的PB事件,IDs;如果代码没有给PB的对象及控件占有,本文给出了关于何种情况导致发送通知代码的简要解释。
ID Code Name Definition
pbm_dwnitemchange 256 ItemChanged See Objects
pbm_dwnitemvalidationerror 512 ItemError ''
pbm_dwnretrievestart 768 RetrieveStart ''
pbm_dwnretrieveend 769 RetrieveEnd ''
pbm_dwnretrieverow 770 RetrieveRow ''
pbm_dwnupdatestart 1024 UpdateStart ''
pbm_dwnupdateend 1025 UpdateEnd ''
pbm_dwnlbuttondblclk 1280 DoubleClicked ''
pbm_dwnlbuttonclk 1281 Clicked ''
pbm_dwnrbuttondblclk 1282 RightDoubleClicked
* Same as DoubleClicked event, except the right
mouse button was used.
pbm_dwndberror 1536 DBError See Objects and Controls.
pbm_dwnitemchangefocus 1792 ItemFocusChanged ''
pbm_dwnrowchange 2048 RowFocusChanged ''
undefined 2049 SelectionChanged
* Unique to DropDownDataWindows; similar to the
RowFocusChanged event, but sent only after the
mouse button has been released.
pbm_dwntabout 2304 TabOut
* When user presses the Tab key to leave the last editable
field on a Data-Window.
pbm_dwnbacktabout 2305 BackTabOut
* When user presses Ctrl-Tab to leave the first editable field
on the DataWindow.
pbm_dwntabupout 2306 TabUpOut
* When the user presses the up-arrow in an edit control of a
Freeform style DataWindow.
pbm_dwntabdownout 2307 TabDownOut
* When the user presses the down-arrow in an edit control
of a Freeform style DataWindow.
pbm_dwnprocessenter 2308 ProcessEnter
* When the user presses the Enter key.
pbm_dwnkillfocus 2309 LoseFocus
(See Objects and Controls)
pbm_dwnsetfocus 2310 GetFocus ''
pbm_dwnmousemove 2311 MouseMove
* When the user moves the mouse within the Data-Window control.
pbm_dwnlbuttonup 2313 LeftButtonUp
* When the user releases the left mouse button.
pbm_dwnrbuttonclk 2314 RightClicked
* When the right mouse button is clicked.
pbm_dwnrbuttonup 2315 RightButtonUp
* When the right mouse button is released.
pbm_dwnchanging 2316 EditChanged
(See Objects and Controls)
pbm_dwnhscroll 2317 ScrollHorizontal ''
pbm_dwnvscroll 2318 ScrollVertical ''
pbm_dwnsql 2319 SQLPreview ''
pbm_dwnresize 2320 Resize ''
undefined 2321 PosChanged
* When the user or a script moves a DataWindow (i.e., changes
its position).
pbm_dwnprintstart 2323 PrintStart
(See Objects and Controls)
pbm_dwnprintpage 2324 PrintPage ''
pbm_dwnprintend 2325 PrintEnd ''
pbm_dwnkey 2326 Key ''
pbm_dwndropdown 2327 DropDown
* When the drop-down portion of a field with the
DropDownDataWindow edit-style becomes visible.
pbm_dwndragdrop 2560 DragDrop
(See Objects and Controls)
pbm_dwndragenter 2561 DragEnter ''
pbm_dwndragleave 2562 DragLeave ''
pbm_dwndragwithin 2563 DragWithin ''
如何使用这些消息
这里有一个例子教你如何在脚本中使用这些未公开的通知代码。
有一个地址表的数据窗口,对city,zip字段用DDDW显示方式。city字段的DDDW会给用户一个城市列表,每一个又有分别的州及邮政编码,用户从下拉城市列表中选中一个城市时,希望可以同时确定州及邮政编码。
同时要注意到:许多城市有多个邮政编码(如:西雅图就有几百个!),当用户从如:西雅图,WA 98001...西雅图,WA 98019 列表中选择一个时,仅仅只会触发Itemchanged事件一次。如果不管用户可能选择一个完全不同的邮编的情况,将不会触发别的PB事件。如果有的话,只可能是子数据窗口的RowFocusChanged事件。(记住,此事件仅当用户用鼠标或箭头键在数据窗口中的行间移动时触发)。
好了,我们要作的既是截获子数据窗口发送的wm_command消息。我们可以用上面处理命令按钮的方法来处理,不过有一点不同。首先,DDDW实际上是个子数据窗口,所有的通知消息会传给它的父数据窗口(不是父窗口)。所以你必须在数据窗口控件中定义自定义事件“ue_command". 下面是ue_command事件中的脚本:
// Script for ue_command event (WM_COMMAND)
int iRet, iDWRow, iDDRow
string sDWCol
DataWindowChild dwc
// Is the notification message from our DDDW?
sDWCol = This.GetColumnName()
IF sDWCol = 'city' THEN
// Is it the RowFocusChanged Notification Code?
IF IntHigh(Message.LongParm) = 2048 THEN
// Get child DataWindow control
iRet = This.dwGetChild(sDWCol, dwc)
// Get the DataWindow's current row
iDWRow = This.GetRow()
// Get the DropDown's current row
iDDRow = dwc.GetRow()
IF iDDRow > 0 THEN
iRet = This.SetItem(iDWRow, 'state', dwc.GetItemString(iDDRow, 'state'))
iRet = This.SetItem(iDWRow, 'zip', dwc.GetItemString(iDDRow, 'zip'))
ELSE
iRet = This.SetItem(iDWRow, 'state', '')
iRet = This.SetItem(iDWRow, 'zip', '')
END IF
END IF
END IF
潜在的风险
当在你的应用中使用了以上所说的数据窗口通知代码后,你应该知道这种潜在的风险。事实是Sybase公司故意决定使这些信息不能轻易得到,至少,给我的印象是如此。风险在于Sybase公司可能在将来改变通知代码的定义,但是对我来说,冒这点风险得到的好处是显而易见的。
[翻译的不好,请谅解,谢谢!!!]
作者:段江
(欢迎转载,但请注明出处:PB档案)