Powerbuilder 所提供的 MDIHelp! 窗口类型带有一个 Sybase 自制的类似状态条的控件(严格说它并非是控件),用户可以通过调用 SetMicroHelp( ) 函数修改该控件的显示文本,即该窗口的标题。但与 Windows 的状态条控件相比,它还是缺少了些重要的功能,因此本文将就如何在 Powerbuilder 中使用 Windows 状态条控件展开讨论。在讨论前笔者假设读者对如何在 Powerbuilder 中使用 Windows 公共控件已有所了解,如果读者不了解的话,可以参考笔者的《用 Windows 公共控件增强 PB 应用的界面》。
初始化控件
为演示 Windows 状态条控件的功能,笔者先创建一个类型为 Main 的窗口对象,并命名为 w_frame。然后创建一个外部可视用户对象,将
属性 LibraryName 设为:%System%/ COMCTL32.DLL;
属性 ClassName 设为:msctls_statusbar32;
属性 Style 设为:256。
这里 Style 属性有宏 CCS_TOP 和 SBARS_SIZEGRIP 供组合,CCS_TOP(1) 表示控件将显示在父窗口的顶部而非底部,SBARS_SIZEGRIP(256) 表示控件将显示 Sizing Grip,如图1和图2所示。
图1
图2
正如笔者在《用 Windows 公共控件增强 PB 应用的界面》中所说的,状态条控件不必调用 InitCommonControls( )、CreateWindowsEx( ) 和 ShowWindow( ) 函数。因此修改上述的属性后,状态条控件即显示出来了。将该用户对象命名为 ctrl_status_bar,并将继承自该用户对象的用户对象 ctrl_sbar 布置于 w_frame 上。
动态调整大小
当调整父窗口的大小时,需要相应调整状态条和 MDIClient 的大小,在父窗口的 resize 事件中,修改控件的宽及 MDIClient 的坐标和长宽:
stc_rect lstc_rect
ctrl_sbar.width = newWidth
GetClientRect(Handle(this), lstc_rect)
lstc_rect.bottom = PixelsToUnits(lstc_rect.bottom, YPixelsToUnits!)
mdi_1.y = lstc_rect.bottom - WorkSpaceHeight()
mdi_1.width = WorkSpaceWidth()
mdi_1.height = WorkSpaceHeight() – 80
同样工具栏的移动会修改 WorkSpace 的大小,故需要进行调整:
stc_rect lstc_rect
GetClientRect(Handle(this), lstc_rect)
lstc_rect.bottom = PixelsToUnits(lstc_rect.bottom, YPixelsToUnits!)
mdi_1.y = lstc_rect.bottom - WorkSpaceHeight()
mdi_1.width = WorkSpaceWidth()
mdi_1.height = WorkSpaceHeight() – 80
简单模式状态条
如图1和图2所示的由单一部分构成的状态条称为简单模式状态条,形式与 Powerbuilder 的 MDIHelp! 的状态条极相似,只要修改 Text 属性就可改变状态条的文字。
多部分状态条
多部分状态条由多个部分构成,如图3所示。要实现从缺省的简单模式向多部分状态条的转换,需要向控件发生 SB_SETPARTS 消息。
图3
一般在 Powerbuilder 中发送消息要用 Send( ) 函数,但 SB_SETPARTS 消息的参数为:
wParam
=
(WPARAM) nParts;
//
部分数
lParam
=
(LPARAM) (LPINT) aWidths;
//
各部分宽度
aWidths 是一个长整型数组,在此要说明的是 32 位 C 编译器所用的 int 是 32 位的,而 Powerbuilder 中的 int 是 16 位,long 是 32 位,所以说 aWidths 是长整型的数组。
由上可知 aWidths 不能作为 Send( ) 的参数,只能用 SendMessageA( ) Win32 API 函数来实现,考虑函数的多态性,可以声明 Powersoft 原型为:
Function ULong SendMessageA(ULong hWnd,ULong Msg,UInt wParam,ref ULong lParam[]) Library "User32.DLL"
需要注意的是 wParam 不可以被 Ref 修饰,lParam 的类型必须是 Ulong 或 Long,否则控件将视所发 SB_SETPARTS 为无效消息。
基于面向对象的封装原则,定义对象方法 SetParts( ) 为:
ctrl_status_bar.Function SetParts(Uint nParts, Ulong aWidths[]) returns Boolean
return
(
0
<>
SendMessageA(Handle(
this
),
1028
, nParts, aWidths))
其中,1028 是 SB_SETPARTS 消息的标识号。
在生成多部分的状态条后,我们需要实现类似 SetMicroHelp( ) 函数的功能,即修改状态条各部分的显示文本。对于第一部分往往只要修改用户对象的 Text 属性,就可修改它的文本;对于其他部分,我们可以通过发送 SB_SETTEXT 消息修改各部分的文本,当然第一部分也可以这样修改。但 SB_SETTEXT 消息的参数为:
wParam
=
(WPARAM) iPart
|
uType;
//
部分索引号 | 类型
lParam
=
(LPARAM) (LPSTR) szText;
//
待显示的文本
由于 lParam 是字符串,不能利用 Send( ) 和先前声明的 SendMessageA( ),但 Powerbuilder 支持多态性原则,所以可以再声明一个 SendMessageA( ) 函数。其 Powersoft 原型为:
Function ULong SendMessageA(ULong hWnd,ULong Msg,UInt wParam,String szText) Library "User32.DLL"
同样定义方法 SetText( ) 为:
ctrl_status_bar.Function SetText(Uint iPart, Uint uType, String szText) returns Boolean
return
(
0
<>
SendMessageA(Handle(
this
),
1025
, iPart
+
uType, szText))
其中,1025 是 SB_SETTEXT 消息的标识号,iPart 是基于 0 的索引值,iPart + uType 表示逻辑或,uType 的取值可以参考 MSDN Library 和 commctrl.h。
模式间的切换
状态条控件可以在 SB_SIMPLE 消息的控制下进行简单模式与多部分间的切换,定义方法 Simple( ) 为:
ctrl_status_bar.Function Simple(Boolean fSimple)
int
iSimple
=
0
if
fSimple then iSimple
=
1
Send(Handle(
this
),
1033
, iSimple,
0
)
如果 fSimple = True,状态条切换为简单模式;反之亦然。
Owner-Drawn窗口部分
到目前为止所讨论的只限于在状态条上显示文本,那么如何绘制图形或显示不同字体的文本呢?我们可以通过定义指定的窗口部分为 Owner-Drawn 窗口部分来实现。为定义一个窗口部分为 Owner-Drawn 的,要发送 SB_SETTEXT 消息给状态条,并指定 iPart 为该窗口部分对应的值及 uType 设为 SBT_OWNERDRAW (4096)。此时参数 lParam 可以是一个字体句柄、位图句柄或字符串地址等。
当状态条需要绘制一个 Owner-Drawn 部分时,会发送 WM_DRAWITME 消息给父窗口。消息的 wParam 参数是状态条的控件标识号,lParam 参数是 DRAWITEMSTRUCT 结构的地址。父窗口利用该结构绘制 wParam 指定的窗口部分。但由于 pbm_drawitem 事件所传递的 lParam 参数是长整型的,所以我们不能直接利用该结构,只能通过状态条控件的句柄来间接获得结构成员所带的信息。
现举一在状态条的第二部分 (iPart = 1) 显示一个小图标的例子:
定义全局结构 strc_rect 为:
Long left
Long top
Long right
Long bottom
依据多态性原则,定义 ctrl_status_bar 的方法 SetText( ) 为:
return ( 0 <> Send (Handle( this ), 1025 , iPart + uType, handle))
获取指定的窗口部分的坐标值,向状态条控件发送 SB_GETRECT 消息,同样是 lParam 参数类型的关系,需利用多态性声明 SendMessageA( ) 为:
Function Ulong SendMessageA(Ulong hWnd, Ulong Msg, Uint wParam,ref strc_rect lParam) Library "User32.DLL"
并定义 ctrl_status_bar 的方法 GetRect( ) 为:
ctrl_status_bar.Function GetRect(Uint iPart,
ref
strc_rect rc) returns boolean
return
(
0
<>
SendMessageA(Handle(
this
),
1034
, iPart, rc))
声明 w_frame 的 Local External Function 的 Powersoft 原型为:
Function ULong LoadImageA(Ulong hinst, String lpszName, UInt uType, int cxDesired, int cyDesired, UInt fuLoad) Library "User32.DLL"
Function Boolean DestroyIcon(ULong hIcon) Library "User32.DLL"
Function Int GetDlgCtrlID(Ulong hwndCtl) Library "User32.DLL"
Function ULong GetDC(ULong hWnd) Library "User32.DLL"
Function Int ReleaseDC(ULong hWnd,ULong hDC) Library "User32.DLL"
Function Boolean DrawIconEx(Ulong DC,int xLeft,int yTop,ULong Icon,int cxWidth,int cyWidth,UINT istepIfAniCur,ULong hbrFlickerFreeDraw,UINT diFlags) Library "User32.DLL"
声明 w_frame 的实例变量
Ulong hIcon, hDC
定义 w_frame 的 Open 事件脚本为:
ULong aWidths[] = { 350 , - 1 }
ctrl_sbar.SetParts( 2 , aWidths) // 显示 2个部分
hDC = GetDC(Handle(ctrl_sbar)) // 获得 Device Context
hIcon = LoadImageA( 0 , " .STATBAR.ICO " , 1 , 16 , 16 , 16 ) // 加载图标
ctrl_sbar.SetText( 1 , 4096 , hIcon)
定义 w_frame 的 Close 事件脚本为:
w_frame.Event Close( ) returns
long
DestroyIcon(hIcon)
ReleaseDC(Handle(ctrl_sbar), hDC)
定义 w_frame 的自定义事件 DrawItem 事件脚本为:
w_frame.Event DrawItem(Ulong ChildID, Long DrawItemStruct) returns
long
[pbm_drawitem]
if
childid
<>
GetDlgCtrlID(Handle(ctrl_sbar)) then
return
//
标识号是否一致
ctrl_sbar.GetRect(
1
,rc)
//
获得窗口部分的坐标值
DrawIconEx(hDC, rc.Left, rc.Top, hIcon,
0
,
16
,
16
,
0
,
3
)
//
绘制图标
图4
图4 所示即为调试结果。
处理通知消息
如果单击状态条上的打印机图标能打开“打印队列管理器”,对最终用户来说是很有吸引力的。如何实现呢?理论上,当单击状态条时,状态条控件会向父窗口发送 WM_NOTIFY 消息,只要处理该消息即可;但实际上,状态条的 NM_CLICK、NM_DBLCLK、NM_RCLICK 和 NM_RDBLCLK 消息的 lParam 参数是 NMMOUSE 结构变量,很难处理,所以得用 ctrl_status_bar 的 WM_LBUTTONDOWN 消息来代替它。下面以 Clicked 事件为例加以说明:
首先定义 ctrl_status_bar 的 Clicked 事件为:
ctrl_status_bar.Event Clicked(Ulong flags,
int
xpos,
int
ypos) returns
long
[pbm_lbuttondown]
然后定义 ctrl_sbar 的 Clicked 事件脚本 (继承自 ctrl_status_bar) 为:
ctrl_sbar.Event Clicked(Ulong flags,
int
xpos,
int
ypos) returns
long
strc_rect rc
GetRect(
1
, rc)
if
UnitsToPixels(xpos, XUnitsToPixels
!
)
>=
rc.left
&
and UnitsToPixels(xpos, XUnitsToPixels
!
)
<=
rc.left
+
16
&
and UnitsToPixels(ypos, YUnitsToPixels
!
)
>=
rc.top
&
and UnitsToPixels(ypos, YUnitsToPixels
!
)
<=
rc.top
+
16
then
MessageBox(
"
Clicked
"
,
"
Event Triggered!
"
)
end
if
同理处理其他鼠标消息也是如此。
显示菜单的 MicroHelp 信息
使用 MDI with Help 类型窗口的 Powerbuilder 应用程序,在用户选择菜单项或对应的工具条图标时,会在状态条上显示菜单项的 MicroHelp 属性值。如图5 所示。但我们所使用的外部控件是无此功能的,不过我们可以利用 WM_MENUSELECT 消息来实现该功能。
图5
首先定义与菜单有关的 Local External Function 为:
Function ULong GetMenu(ULong hWnd) Library "User32.DLL"
Function ULong GetSubMenu(ULong hMenu,int nPos) Library "User32.DLL"
Function UINT GetMenuItemID(Ulong hMenu,int nPos) Library "User32.dll"
定义菜单 m_frame 的用户函数 GetMicroHelp 为:
m_frame.Function GetMicroHelp(Long hMenu, Uint ItemID) returns String
int
li_Count
int
li_SubCount
for
li_Count
=
1
to UpperBound(Item)
if
GetSubMenu(GetMenu(Handle(ParentWindow)), li_Count
-
1
)
=
hMenu then
//
判断 hMenu 是否是当前菜单的句柄
for
li_SubCount
=
1
to UpperBound(Item[li_Count].Item)
if
GetMenuItemID(hMenu, li_SubCount
-
1
)
=
ItemID then
//
判别当前菜单项是否是所期望的菜单项
return
Item[li_Count].Item[li_SubCount].MicroHelp
end
if
next
end
if
next
return
GetApplication().MicroHelpDefault
//
返回缺省的 MicroHelp
定义窗口 w_frame 的自定义事件 MenuSelected 为:
w_frame.Event MenuSelected(Uint ItemID, Uint Flags, Long hMenu) returns
long
[pbm_menuselect]
ctrl_status_bar.Text
=
m_frame.GetMicroHelp(hMenu,ItemID)
这样就实现了所要求的 MicroHelp 功能。
本文介绍了如何在Powerbuilder中利用Windows的msctls_statusbar32控件,通过设置LibraryName、ClassName和Style属性来创建状态条,并详细讲解了初始化、动态调整大小、简单和多部分状态条的实现、模式切换以及Owner-Drawn窗口部分的绘制。此外,还涉及了处理鼠标点击事件和模拟MicroHelp信息显示的方法。
631

被折叠的 条评论
为什么被折叠?



