发信人: msnet()
整理人: cobe(2000-02-12 23:52:43), 站内信件
|
编者的话
====================================================================
大家好!
这是关于VB和ASP的免费邮件列表。
如果您有问题或想回答别人的问题,请给我写信。
如果您有好的作品或您发现了好的作品,不要忘
了让大家共享。
热情欢迎您的参与!!!
版主 冯德平
[email protected]
=============================================
VB邮件(6.6)
a VB网格中输入数据
山东 周庆斌
VISUAL BASIC提供的网格控制(Grid)主要是用来直观地输出数据,但不能 输入数据,使用起来很不方便。能不能向网格中输入数据呢?笔者提出了两种解 决方法,供大家参考:
方法一:
改写网格的KeyPress事件,在每次有合法字符输入时,把Grid的Text项和输 入字符连接。这种方法缺点是:一代码较复杂,需要处理各种ASCII字符信息;二 是通过编程只能实现很少的编辑功能,如用退格键删除前一字符,用Del键删除所 有字符;三是这种方法不能输入汉字,使用起来受到很大的限制。
方法二:
利用一文本框作为缓冲,实现编辑功能。当网格改变行列时,把网格当前行 列的内容传递给文本框;当网格中有ASCII字符产生时,把输入焦点设置为文本框 ,并把输入的ASCII字符送给文本框;当编辑完文本框的内容时,按Enter键或TA B键,把文本框的内容送给网格的当前行列,并把输入焦点设置为网格。如下程序 所示(其中Textl为文本框,Labell为标签,Gridl为网格):
Option Explicit
Const EnterAsc=13
Const TabAsc=9
Private Sub Form-Load()
Labell.Caption=”请输入”
End Sub
Private Sub Gridl-KeyPress(KeyAscii As Integer)
Text1.StFocus
Textl.SelStart=0
If KeyAscii <> EnterAsc And KeyAscii <> TabAsc Then
SendKeys Chr (KeyAscii)
End IF
End Sub
Private Sub Gridl-RowColChange()
Textl.Text=Grid1.Text
End Sub
Private Sub Text1-KeyPress(KeyAscii As Integer)
If KeyAscii=EnterAsc Then
Grid1.SetFocus
KeyAscii=0
End If
End Sub
Private Sub Text1-LostFocus()
Grid1.Text=Text1.Text
End Sub
b 问与答
编程
Q8. 如何控制Windows任务栏
广州·郏军波 (E-mail):
我在用VB 5.0编写一个屏幕保护程序时,调用ShowCursor关闭指针,但未能 找到对任务栏的关闭函数。VB 5.0能否调用有关函数来实现对任务栏的控制?如 果可以,如何实现?
李海:
可以使用FindWindow和ShowWindow来控制任务栏。下面给出一个例子程序。 首先建立一个窗体和两个按钮。在窗体声明部分输入如下定义:
Private Declare Function FindWindow Lib "user32" Alias "FindWindow A" (ByVal lpClassName As String, ByVal lpWindowName As Any) As Long
Private Declare Function ShowWindow Lib "user32" (ByVal hwnd As Lo ng, ByVal nCmdShow As Long) As Long
Const SW_HIDE = 0
Const SW_SHOWNORMAL = 1
然后在按钮的事件中输入如下代码:
Private Sub Command1_Click()
Dim hTaskBar As Integer
hTaskBar = FindWindow("Shell_TrayWnd", 0&)
ShowWindow hTaskBar, SW_HIDE
End Sub
Private Sub Command2_Click()
Dim hTaskBar As Integer
hTaskBar = FindWindow("Shell_TrayWnd", 0&)
ShowWindow hTaskBar, SW_SHOWNORMAL
End Sub
这个例子的结果是,按Command1将隐藏任务栏,而按Command2将显示任务栏 。
另外,还有一个办法可以使VB的窗体可以遮住任务栏。你可以在设计时将窗 体的WindowState属性设为2,MaxButton设为False。这样,你的程序运行时,窗 体会遮住任务栏,即使任务条被设置为“总在最前面”也一样。这本是VB的一个 Bug,但可能恰好适合于你的屏幕保护程序。
Q12. 如何清除TCanvas上的内容
华文生(E-mail):
在VB的PictureBox控件里,可以用Circle、Line、Point等方式在图像表面绘 图,然后用Cls的方式擦除先前用上述方式画上的图形。在Delphi 3.0中,采用T Canvas对象可以在图像上绘图,但却找不到类似VB的Cls方式来擦除已画上的图形 。请问有什么方法可以解决这个问题?
李海:
Delphi 3.0没有Cls方法。你可以使用画矩形的办法来擦除图像,如:
Canvas.Brush.Color := ClWhite;
Canvas.FillRect(Canvas.ClipRect);
你可以指定Canvas.Brush.Color为希望的颜色。
Q13. 如何提取可执行文件中包含的图标
qqboy (E-mail):
在Visual Basic 5.0中,怎么样才能把可执行文件(如.EXE文件、.DLL文件 等)中包含的图标显示在Picture Box中?要是该文件包含有多个图标,怎样显示 指定的一个?能举一个程序例子说明吗?
李海:
Windows API的ExtractIcon可以完成这个任务。ExtractIcon的定义如下:
Declare Function ExtractIcon Lib "shell32.dll" Alias "ExtractIconA " (ByVal hInst As Long, ByVal lpszExeFileName As String, ByVal nIconIn dex As Long) As Long
第一个参数是调用这个函数的实例句柄,在VB中可以使用App.hInstance得到 这个句柄。第二个参数是包含图标的文件名。当该文件包含有多个图标时,第三 个参数指定该图标的索引。使用时,首先通过 ExtractIcon (App.hInstance, f ilename, -1) 得到文件中图标的个数。然后,再次使用 hIcon = ExtractIcon (App.hInstance, filename, i) 来获得第i个图标的句柄。然后使用 DrawIcon (Picture1.hDC, 0, 0, hIcon) 将图标显示在Picture Box中。
Q14. VB程序如何获得命令行参数
四川·邓世学(信函):
如何在VB 5.0的应用程序中获得外来的参数?比如,对于用VB 5.0生成的应 用程序Programm.exe,在Win 95环境中可以选择“运行”,输入"Program.exe M yString";可是Programm.exe这个程序自己如何获得"My String"这个参数呢?
李海:
VB 5.0的Command函数就是用来获取命令行参数的。如果你在命令行输入"Pr ogram.exe MyString",该函数即返回"MyString"。该函数返回的字符串是区分大 小写的,如果你对命令行的处理是不区分大小写的,别忘了使用Ucase或Lcase函 数处理一下。为便于在集成环境中调试,你可以在Project Properties对话框的 Make选项卡中设置命令行参数(图2)。
图2 设置命令行参数以方便调试
Q15. VB程序如何修改注册表
四川·邓世学(信函):
如何在VB 5.0的应用程序中修改Windows 95的注册表信息?如建立一个主键 ,须在注册表中加入语句:
Hkey_Classses_Root\映象文件\Shell\打开\Command=D:\\tools\\Program. exe %1
李海:
虽然VB提供了GetSetting、SaveSetting等函数,但它们只能处理HKEY_CURR ENT_USER\Software\VB and VBA Program Settings下面的子键。所以要实现对注 册簿的修改,通常需要调用Windows API。在Windows API中有一系列函数专门处 理注册簿操作,这些函数基本上都以Reg开头。一般读写一个键值的步骤是:
(1) 先用RegCreateKeyEx或RegOpenKeyEx创建或打开一个键;
(2) 使用RegQueryValueEx读一个键的值,或使用RegSetValueEx写入一个键 的值;
(3) 最后使用RegCloseKey关闭这个键。
为便于在VB中使用,我们采用了如下的形式进行封装。
Global Const REG_SZ As Long = 1
Global Const REG_DWORD As Long = 4
Global Const HKEY_CLASSES_ROOT = &H80000000
Global Const HKEY_CURRENT_USER = &H80000001
Global Const HKEY_LOCAL_MACHINE = &H80000002
Global Const HKEY_USERS = &H80000003
Global Const KEY_ALL_ACCESS = &H3F
Declare Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Lon g) As Long
Declare Function RegCreateKeyEx Lib "advapi32.dll" Alias "RegCreat eKeyExA"
(ByVal hKey As Long, ByVal lpSubKey As String,
ByVal Reserved As Long, ByVal lpClass As String,
ByVal dwOptions As Long, ByVal samDesired As Long,
ByVal lpSecurityAttributes As Long, phkResult As Long,
lpdwDisposition As Long) As Long
Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKey ExA"
(ByVal hKey As Long, ByVal lpSubKey As String,
ByVal ulOptions As Long, ByVal samDesired As Long,
phkResult As Long) As Long
Declare Function RegSetValueExString Lib "advapi32.dll"
Alias "RegSetValueExA"
(ByVal hKey As Long, ByVal lpValueName As String,
ByVal Reserved As Long, ByVal dwType As Long,
ByVal lpValue As String, ByVal cbData As Long)
As Long
Declare Function RegSetValueExLong Lib "advapi32.dll"
Alias "RegSetValueExA"
(ByVal hKey As Long, ByVal lpValueName As String,
ByVal Reserved As Long, ByVal dwType As Long,
lpValue As Long, ByVal cbData As Long)
As Long
Public Function CreateNewKey(lPredefinedKey As Long, sNewKeyName A s String)
Dim hNewKey As Long 'handle to the new key
Dim lRetVal As Long 'result of the RegCreateKeyEx function
lRetVal = RegCreateKeyEx(lPredefinedKey, sNewKeyName, 0&, vbNullS tring, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0&, hNewKey, lRetVal)
RegCloseKey (hNewKey)
End Function
Public Function SetValueEx(ByVal hKey As Long, sValueName As Strin g, lType As Long, vValue As Variant) As Long
Dim lValue As Long
Dim sValue As String
Select Case lType
Case REG_SZ
sValue = vValue
SetValueEx = RegSetValueExString(hKey, sValueName, 0&, lType, sVa lue, Len(sValue))
Case REG_DWORD
lValue = vValue
SetValueEx = RegSetValueExLong(hKey, sValueName, 0&, lType, lValu e, 4)
End Select
End Function
Public Function SetKeyValue(lPredefinedKey As Long, sKeyName As St ring, sValueName As String, vValueSetting As Variant, lValueType As Lo ng)
Dim lRetVal As Long 'result of the SetValueEx function
Dim hKey As Long 'handle of open key
'open the specified key
lRetVal = RegOpenKeyEx(lPredefinedKey, sKeyName, 0, KEY_ALL_ACCES S, hKey)
lRetVal = SetValueEx(hKey, sValueName, lValueType, vValueSetting)
RegCloseKey (hKey)
End Function
调用时,可以使用如下的语法:
CreateNewKey HKEY_CLASSES_ROOT, "映象文件\Shell\打开\"
SetKeyValue HKEY_CLASSES_ROOT, "映象文件\Shell\打开\", "Command", "
D:\tools\Program.exe %1", REG_SZ
1999年 第03期
问与答
编程
Q11. 如何选中ListView控件的整行
暨南·区庆彬 (E-mail):
请先看看图3和图4两幅图片,注意虚线框框住的地方。图3是OutLook中的部 分界面,图4是Win 98中的资源管理器中的部分界面,在VB、VC中,都提供了一个 叫listview的控件,它的样式跟图4一样,点击时只有虚线框框住的地方为反白显 示,同一行的其他部分却不是,在正常显示处点击则不能选定该行。但在图3中, 却能实现单击一行中的任何部分都能选定该行,且能整行都反白显示。请问在VB 和VC中怎样才能实现这种功能呢?
图3 Outlook的界面
图4 Win 98资源管理器的界面
李海:
对于大多数用户来说,Outlook整行选中的处理方式更舒服一些。它实际上利 用了ListView的一个未公开的设置。只要你的Comctl32.dll的版本高于3.71,就 可以使用这一技巧。下面我们以VB为例说明。
首先,在窗体中加入如下声明:
Private Declare Function SendMessage Lib "user32" Alias _ "SendMes sageA" (ByVal hwnd As Long, ByVal wMsg As Long, _ ByVal wParam As Long , lParam As Any) As Long
Const LVS_EX_FULLROWSELECT = &H20
Const LVM_FIRST = &H1000
Const LVM_GETEXTENDEDLISTVIEWSTYLE = LVM_FIRST + &H37
Const LVM_SETEXTENDEDLISTVIEWSTYLE = LVM_FIRST + &H36
在窗体上加上一个ListView和一个按钮,并输入如下代码:
Private Sub Command1_Click()
Dim lStyle As Long
lStyle = SendMessage(ListView1.hwnd, _ LVM_GETEXTENDEDLISTVIEWSTY LE, 0, 0)
lStyle = lStyle Or LVS_EX_FULLROWSELECT
Call SendMessage(ListView1.hwnd, _ LVM_SETEXTENDEDLISTVIEWSTYLE, 0, ByVal lStyle)
End Sub
Private Sub Form_Load()
'Add two Column Headers to the ListView control
Set clmAdd = ListView1.ColumnHeaders.Add(Text:="Name")
Set clmAdd = ListView1.ColumnHeaders.Add(Text:="Date")
'Set the view property of the Listview control to Report view
ListView1.View = lvwReport
'Add data to the ListView control
Set itmAdd = ListView1.ListItems.Add(Text:="Joe")
itmAdd.SubItems(1) = "05/07/97"
Set itmAdd = ListView1.ListItems.Add(Text:="Sally")
itmAdd.SubItems(1) = "04/08/97"
Set itmAdd = ListView1.ListItems.Add(Text:="Bill")
itmAdd.SubItems(1) = "05/29/97"
Set itmAdd = ListView1.ListItems.Add(Text:="Fred")
itmAdd.SubItems(1) = "05/17/97"
Set itmAdd = ListView1.ListItems.Add(Text:="Anne")
itmAdd.SubItems(1) = "04/01/97"
End Sub
只要你按下按钮,就会发现你的ListView也可以像Outlook一样工作了。
Q12. 如何在Excel中调用VB程序和使用DriveListBox等控件
jalapeno (E-mail):
(1) 我使用EXCEL 97中的VBA编制了大量的代码,可是近来我发现VBA的速度 慢得几乎无法忍受,于是我打算使用VB 5.0编译这些代码,使之成为EXE文件,可 是我该如何将这些EXE文件集成到EXCEL 97的界面中呢?
(2) 我在使用VBA时发现“控件工具箱”中的控件太少,连DriveListBox、D irListBox、FileListBox这样的控件都没有,使用“附加控件”也没有找到这些 控件,应怎样添加这些控件?
李海:
(1) 比较好的办法是编写Automation服务器或者编写ActiveX控件。我们将在 下一个问题的回答中介绍如何编写ActiveX控件,先来说说如何编写Automation服 务器。
首先启动VB 5.0,在New Project窗口中选择ActiveX DLL或ActiveX EXE(前 者速度更快些)。这时你的工程文件中将包括一个Class1,你可以在属性窗口中 将它的名字改为MyClass。在Project菜单下选择Project1 Properties,修改工程 的名字为MyComponent。在MyClass中插入以下代码:
Option Explicit
Public Function SquareIt(lngNumber As Long)
SquareIt = lngNumber ^ 2
End Function
你可以利用类似的步骤插入其他函数。调试时,你可以在集成环境中运行程 序,在Excel中调用这个Automation服务器。调试结束后,编译成DLL或EXE就可以 了。
下面再来谈谈如何在Excel中调用Automation服务器。首先启动Excel 97,然 后打开Visual Basic编辑器,选择“工具 | 引用”菜单,在其中选择MyCompone nt。然后你可以在一个过程、函数或事件中加入以下代码:
'建立对象
Dim obj As MyComponent.MyClass
Dim lngArgument As Long
Dim lngResult As Long
'建立对象实例
Set obj = New MyClass
lngArgument = 2
'调用SquareIt方法
lngResult = obj.SquareIt(lngArgument)
MsgBox "The Square of " & lngArgument & _ " is " & lngResult
'结束
(2) 你所说的几个控件都是VB的内置控件,它们不是ActiveX控件,所以不能 在“附加控件”中找到。既然你对VB 5.0很熟悉,完全可以利用VB 5.0编写Acti veX控件,然后在VBA中加以调用。
首先启动VB 5.0,在New Project窗口中选择ActiveX Control。这时你的工 程文件中将包括一个UserControl1。在Project菜单下选择Project1 Properties ,修改工程的名字为MyFileControl。然后在UserContronl1上放置一个标准的VB 的FileListBox,然后选择Add-ins菜单中的ActiveX Control Interface Wizard 。如果你的Add-ins菜单中没有这一项,你需要在Add-ins菜单的Add-ins Manage r中加上这一项。这个Wizard是用来向你的控件中加入属性、方法和事件的。第一 步,需决定加入哪些属性、方法和事件,你可以在左边的列表框中选择所有标准 的属性、方法和事件,加入到右边列表框中。一些FileListBox的核心属性、事件 ,像Path、PathChange、Pattern当然不能缺少。第二步,你可以加入自己的属性 。如果没什么属性可以加的,就跳过这一步。第三步是建立你的控件的属性和Fi le1之间的关联(图5),这一步你只要将File1的属性、事件与你的控件的相同名 字的属性、事件关联起来就行了。然后,一直继续下去直至结束。最后,在User Control_Resize事件中加入如下代码:
Private Sub UserControl_Resize()
File1.Width = UserControl.Width
File1.Height = UserControl.Height
File1.Move 0, 0
End Sub
至此,一个FileListBox控件就制作好了,编译生成.ocx文件。在使用VBA时 在“控件工具箱”中加入MyFileControl控件,你就可以使用自己的文件框了。类 似地,你还可以处理VB的其他控件。
图5 建立用户控件同File控件属性和事件的对应关系
Q14. 在VB中如何直接对内存口地址进行数据读写
刘顺 (E-mail):
在VB中如何直接对内存口地址进行数据读写?(我需要与电脑内的一块通讯 卡进行数据交换,其地址为300H,301H。)
李海:这是我最经常被问到的三个问题之一(另两个是VB是否是面向对象的 语言和如何打印DBGrid)。VB不像Quick Basic那样,它没有提供Inp、Out这样的 语句来直接操作I/O口。在本刊去年的两期杂志中,我介绍了如何使用C语言编写 一个DLL,然后在VB中进行调用。不过很多VB的用户不熟悉C语言,在编译DLL上遇 到了一些麻烦。你可以从http://www.netease.com/~zealsoft/downc.html下载编 译好的VBIO.DLL。在这个软件中包括有VB的程序示例,你可以参考。
1999年 第04期
问与答
编程
Q10. VB中如何平铺显示图片
Huashan (E-mail):
VB中Form的Picture属性不能将图像自动平铺到整个表单中,只是以原大小显 示(而VFP中好象可以),请问如何实现图像平铺显示?
李海:
你可以在Form上放置一个PictureBox,将Visible属性设为False,AutoSize 属性设为True,将PictureBox的Picture属性设置为你需要的图片,然后在Form_ Paint事件中输入如下代码:
Private Sub Form_Paint()
Dim wid As Single
Dim hgt As Single
Dim x As Single
Dim y As Single
wid = Picture1.ScaleWidth
hgt = Picture1.ScaleHeight
y = 0
Do While y < ScaleHeight
x = 0
Do While x < ScaleWidth
PaintPicture Picture1.Picture, _
x, y, wid, hgt
x = x + wid
Loop
y = y + hgt
Loop
End Sub
Q11. 在VB Crystal Report中加入非数据库文本或程序变量值
Huashan (E-mail):
VB的Crystal Report中如何加入一个非数据库文本或程序中某个变量的值( 如出报表的人名、单位等),我是在数据表中加一个字段来实现的,但太浪费。 另外,怎样将多个报表连续输出而中间不换纸?
李海:
Crystal Reports当然可以插入非数据库文本。从Insert菜单上选择Text Fi eld就可以输入文本。插入文本的工具条按钮就在插入字段的按钮的右边。除了普 通文本,你还可以插入公式、矩形、图片、OLE对象等。
如果你想插入程序中的变量,可以在Crystal Reports插入一个公式,在图1 2所示的对话框中输入以下公式@VarC:
NumberVar c;
c
<a href="/pcc/99_04/images/12.gif">图12 在Crystal Reports中插入 公式
然后在VB程序中设置@VarC公式来显示变量,如下面的代码将VarC的值改为V B的nCount变量的值:
CrystalReport1.Formulas(0) = "VarC=" + CStr(nCount)
CrystalReport1.PrintReport
当然你还可以实现更复杂的公式,如:
CrystalReport1.Formulas(0) = "VarC=" + CStr(nCount) "*{file.SALES} "。
至于将多个报表连续输出而中间不换纸,很遗憾,没有办法。这是由Window s打印方式决定的,它只能一页一页地打。
Q12. VB中用SQL实现复合运算
Huashan (E-mail):
数据库教材中说嵌入式SQL可与宿主语言中的主变量通信,如:SELECT AVG( FIELD1),SUM(FIELD1) INTO :AVG%,:SUM% FROM TABLE(至少PL/1中是这样),但 VB中用DB.Execute "SELECT AVG(FIELD1),SUM(FIELD1) INTO :AVG%,:SUM% FROM TABLE"根本不识别,请问VB中如何用SQL实现求平均值、合计之类的运算及VB能 否与其嵌入式SQL进行变量通信?
李海:
虽说SQL (Structured Query Language)是标准化的结构化查询语言,但不同 的系统在实现的时候还是略有不同。VB的SQL语句SELECT ... INTO只能将结果输 出到表中,而不能输出到VB变量中。VB中求平均值、合计之类的运算采用另一种 办法,即先建立一个Recordset,然后从Recordset的字段中得到结果,如:
Dim Rst As Recordset
Set Rst = Db.OpenRecordset("SELECT AVG(FIELD1), SUM(FIELD1) FROM T ABLE")
Rst(0)是AVG(FIELD1),而Rst(1)是SUM(FIELD1)。
Q13. VB中如何读取INI文件
Huashan (E-mail):
VB中有没有读取INI文件的函数?同样功能的API函数是什么?
李海:
VB没有直接读写INI文件的语句,必须借助Windows API来操作INI文件。相应 的Windows API函数有两类:一类是GetProfileInt、GetProfileString、WriteP rofileInt和WriteProfileString,它们是读写Win.ini中的设置;另一类是GetP rivateProfileInt、GetPrivateProfileString、WritePrivateProfileInt和Wri tePrivateProfileString,它们可以读写任何一个.ini文件。这些函数的声明都 可在Win32api.txt中找到。应用方法是,先在VB中建立一个模块,输入以下代码 :
Declare Function GetPrivateProfileString Lib "kernel32" _
Alias "GetPrivateProfileStringA" (ByVal lpApplicationName _
As String, ByVal lpKeyName As Any, ByVal lpDefault As String, _
ByVal lpReturnedString As String, ByVal nSize As Long, _
ByVal lpFileName As String) As Long
Declare Function WritePrivateProfileString Lib "kernel32" _
Alias "WritePrivateProfileStringA" (ByVal lpApplicationName _
As String, ByVal lpKeyName As Any, ByVal lpString As Any, _
ByVal lpFileName As String) As Long
下面的例子将从njcom.ini中读取NJStar Communicator的目录。
Dim s As String * 256
GetPrivateProfileString "NJCOM Options", _
"Home Directory", "", s, 256, "njcom.ini"
Label1.Caption = s
下面的例子将利用WritePrivateProfileString删除掉njcom.ini中Home Dir ectory设置。这里我们的第三个参数不是通常的字符串,而是0&,你可以看到我 们在声明参数类型时使用As Any的好处。
WritePrivateProfileString "NJCOM Options", _
"Home Directory", 0&, "njcom.ini"
Q14. 如何打开加密的Access数据库
chlitina01 (E-mail):
我用VB连接Access数据库编程,想实现如下控制:用VB的OpenDatabase打开 加密码的Access数据库时,加上密码才能打开,否则提示出错。我该怎么做?
李海:
OpenDatabase的第4个参数就是用来打开加密数据库的。这个参数是由两个部 分组成的:第一个部分是数据库类型,如FoxPro 2.5等,对于Access数据库,这 一部分为空;第二个部分是一些参数,如pwd=password表示密码是password。两 个部分之间使用“;”号分隔。下面的例子打开一个带有密码的数据库,密码为 outlook:
Dim MyDatabase As Database
Set MyDatabase = Workspaces(0).OpenDatabase("CSMain.mdb", True, Fa lse, ";pwd=outlook")
如果是使用Data控件打开数据库,需要设置Connect属性,格式同OpenDatab ase的第4个参数。
c VB下Add-Ins的编程
华工 姜新 汪秉文
摘要 :本文从一个具体例子出发阐述了在Visual Basic 5.0环境下进行外接 程序(Add-Ins)开发的原理、关键技术和注意事项,并对其相关技术,如Activ eX、多态性与接口、事件变量等VB5新引进的编程概念也做了必要的分析和描述。
关键词:外接程序(Add-Ins),ActiveX,接口,多态,事件变量
一、 概述
Visual Basic下的Add-Ins,即外接程序,是扩展VB编程环境的非常有用的工 具。作为一个开发者,我们发现在使用Visual Basic集成开发环境(IDE)时经常 需要重复地干同一件简单的工作,如设置所有或一类控件的字体,前景及背景颜 色,改变控件的Tab次序等。这些简单、单调而重复性的劳动如果由程序自动完成 ,将变得非常方便,而Add-Ins正为实现这一功能提供了可能。与Visual Basic的 早期版本不同,Visual Basic5.0下的Add-Ins是一种模块化的ActiveX部件,可以 作为ActiveX DLL或EXE文件进行编译。另外,跟以前的版本相比,VB5下Add-Ins 无论从编程思想还是从其扩展模型的对象与结构来说,变化都比较大,因此VB5下 编写Add-Ins程序与早期版本将会有很大不同。在这篇文章中,我们将从一个具体 例子出发,阐述VB5下Add-Ins的编程。
二、 问题的提出
在Visual Basic下进行窗体设计时,我们一般希望自己的程序具有风格一致 的界面,如同类控件具有相同的外观、相同的字体等。另外,我们在设计时还应 该考虑到屏幕分辨率的变化对程序的影响,例如我们经常发现出现这样的问题: 即在某一分辨率(如800*600)下看起来比较好的字体,当在另一分辨率下(如6 40*480)运行时将变得非常难看;或者在中文环境下看起来比较舒服,但在英文 环境下却很不清晰。这种种原因都使得我们必须调整窗体控件的字体,以达到最 佳效果。但如果表单或控件比较多时,手工调整将会非常繁琐,而且容易出错, 因此编写一个Add-Ins以实现字体的自动调整将会非常实用。本文中的例子即是用 来实现此功能的。
三、 编程方法
编写Add-Ins的最简单方法是首先利用VB5的编程模板生成一个Add-Ins的框架 ,然后在此框架上进一步实现自己的代码。具体方法是在VB5的“文件”菜单下选 择“新建工程”,然后在弹出的窗口中选择“外接程序”(英文版中是“Add-In s”)即可。这时VB5将生成一个名为MyAddin的工程,该工程中包括一个窗体frm AddIn,一个模块AddIn,以及一个类模块Connect。下面我们对这些部分分别做一 简单分析。
1. 类模块Connect
整个类模块Connect的代码均是由模板自动生成的,大大方便了程序员编程, 但其源代码中有一些比较重要的概念与方法,对我们理解VB Add-Ins甚至VB面向 对象编程均有很大好处。下面我们对如下几点做一简单介绍(源代码由于是VB自 动产生,这里就没有列出了)。
(1)实现IDTExtensibility接口
所有的VB外接程序都必须实现IDTExtensibility接口,该接口包含了当外接 程序与Visual Basic连接时Visual Basic调用的一些方法,无论是通过外接程序 管理器,还是其它一些手段。接口是VB5引入的新概念,是Visual Basic提供多态 性的一种重要手段。我们知道,面向对象的语言一般都提供多态性。如C++语言, 通过类的继承关系,子类重载父类的方法以实现其不同的特性,或父类仅提供方 法框架,即无函数体的虚函数,而子类具体实现其代码。简单地说,多态意味着 许多类可以提供同样的属性或者方法,而且调用者在调用这些属性或方法之前, 不必知道某个对象属于什么类。严格来说,Visual Basic不是完整意义上的面向 对象编程语言,但它也能实现多态性,不过这种多态性不是通过继承来实现的, 而是通过多重ActiveX接口来提供的。多态性具体实现方法简单来说,首先是建立 一个类模块,然后在该模块中声明一些子程序(Sub),但不实现其代码,这样该 类模块可以说是一个抽象类,也即接口;然后建立其它的类模块,在这些类模块 的声明部分中,使用Implements语句表明该类实现了上述接口,接下来就可以实 现接口代码(也即接口的方法)了。这种用Implements语句的方法可以说与Java 语言比较相似。至于这方面的详细信息,可参看VB的帮助或联机手册。
在类模块Connect中,使用Implements IDTExtensibility语句声明该类实现 了IDTExtensibility接口。因此类模块Connect中实现了该接口包括的四个方法: OnAddInsUpdate,OnConnection,OnDisconnection,OnStartupComplete的代码,例 如一条语句、过程调用、注释等等。如果过程是空的话,它会被编译器删掉。如 果实在没有代码可往这些过程里加的话,就插入注释。这是因为既然实现了接口 ,就必须提供接口的所有方法的代码。这四种方法中,比较关键的是OnConnecti on和OnDisconnection方法,其中OnConnection方法当一个外接程序通过“外接程 序管理器”对话框或另一个个接程序与Visual BasicIDE连接时被调用。我们一般 在该方法中保存当前Visual Basic会话期实例,挂接VB菜单,工具栏,或显示初 始窗口,以便用户使用该外接程序。而OnDisconnection方法当外接程序通过编程 或“外接程序管理器”对话框与Visual BasicIDE分离时被调用。我们一般在该方 法中卸掉菜单、工具栏、程序窗体,以及做保存设置等工作。
(2)挂接Visual BasicIDE菜单
在接口IDTExtensibility的OnConnection方法代码中,一般来说大多数情况 下需要挂接Visual BasicIDE菜单(当然也可以是工具栏),以便用户可随时使用 该程序。挂接菜单的代码如下:
Set MCBmenuCommandBar = AddToAddinCommandBar ("My Addin")
'sink the event
Set Me.MenuHandler=
VBinst.Events.CommandBarEvents (mcbMenuCommandBar)
其中AddToAddInCommandBar 函数的代码如下:
Function AddToAddInCommandBar (sCaption As String)As-Office.CommandBar Control
Dim cbMenuCommandBar As Office.CommandBarControl
Dim cbMenu As Object
On Error Go To AddToAddInCommandBarErr
'see if we can find the Add-ins menu
Set cbMenu=Vbinstance.CommandBars ("Add-ins")
If cbMenu is Nothing Then
'not available so we fail
Exit Function
End if
'add it to the command bar
Set cbMenuCommandBar=cbMenu.Controls.Add(1)
'set the caption
cbMenuCommandBar.Caption =sCaption
Set AddToAddInCommandBar = cbMenuCommandBar
Exit Function
AddToAddInCommandBarErr:
End Function
写上面的代码中,mcbMenuCommandBar变量定义为:
Dim mcbMenuCommandBar As Office.CommandBarControl
MenuHandler变量定义为:
Public WithEvents MenuHandler As CommandBarEvents
这里需要指出的是,MenuHandler是WithEvents变量,凡是WithEvents变量均 表明该变量是一个用来响应由ActiveX对象触发的事件的对象变更。也就是说,该 变量是拥有事件的,因此可以编写该变量的事件代码。在上述代码中将事件对象 VBInst.Events.Comman-dBarEvents(mcbMenuCommandBar)赋予MenuHandler变量, 则表示当发生有关mcbMenuC-ommandBar菜单项的事件时,由该变量的代码进行处 理。在类模块中,我们发现,就存在MenuHandler-Click事件代码,该代码用以显 示窗体对话。
在AddToAddInCommandBar函数中,VBInstance是在OnConnection方法保存的 会话期实例,所有Add-Ins的调用都需要此实例。使用VBInstance. CommandBars (“Add-Ins”)将获得Add-Ins菜单条对象(在中文VB5下是“外接程序”菜单条 ),请注意:该“Add-Ins”是不会本地化的,因此无论是在中文还是英文VB5下 抱歉获得上菜单条对象。代码Set cbMenuCommandBar=cbMenu.Controls.Add(1)在 该菜单下添加一新的菜单项“My AddIn”,使用了cbMenu.Controls的Add方法。 关于菜单条的层次关系及相应的属性及方法,可以参看VB关于外接程序的帮助或 联机手册。但通过笔者的实践认为,最好的方法是使用F2打开VB的浏览器,其类 之间的层次关系以及对象事件,方法,属性一目了然。
关于类Connect中的其它方面的代码,一般来说比较容易理解,这里就不叙述 了。
3. 模块AddIn
模块AddIn比较简单,仅包含一个子程序:
Sub AddToINI ()
Dim ErrCode As Long
ErrCode=WritePrivateProfileString ("Add-Ins32","My AddIn.Connect","0", "vbaddin.ini")
End Sub
该程序可以在立即窗口中运行,以便将关于此外接程序的信息添加到Window s目录下的vbaddin.ini文件中去,这样VB的外接程序管理器就可以认识该程序, 并可将其挂接到VB的IDE环境中。该程序所做的工作很简单,只是在vbaddin.ini 文件的[Add-Ins32]下添加一行My AddIn.Connect=0,表明My AddIn没有挂接到ID E环境中,若My AddIn.Connect=1,则VB启动时自动挂接该程序。
4. 窗体frm AddIn
窗体frm AddIn中的代码是由用户编写的,模板仅给出了一个框架。针对我们 上面提出的具体问题,我们设计了一个简单的用户界面,包括如下控件:
(1)标签Label1:Caption="All controls In SelectedForm:"
(2)列表框List1:用于显示表单中的所有控制。
(3)命令按钮fontButton (0):Caption ="&Select Font",用于将选中的窗体 上所有控制的字体设为缺省字体。(注:缺省字体为宋体9号字,这种字体在中文 环境不同分辨率下效果都比较好)。
(4)命令按钮fontButton (1):Caption ="&Select Font",选择该按钮后将弹 出选择字体,然后程序将选中的窗体上所有控制的字体均设为用户选择的字体。
(5)命令按钮ExitButton:Caption="&Exit",用于退出此程序,实际上的工作 只是隐藏了窗体frm AddIn。外接程序的真正退出还是需要外接程序管理器中删去 。
(6)通用对话框CommonDialog1:用于弹出选择字体的通用对话框,便于用户从 中选择所需字体。
下面是窗体代码:
Public VBInstance As vbide.VBE
Public Connect As Connect
Dim mcmpCurrentForm As VBComponent
Option Explicit
Private Sub ChangeFont (NewFontName As String,NewFontSize As Integer,N ewFontBold As Boolean,NewFontltalic As Boolean)
Dim control As VBControl
On Error Resume Next
List1.Clear
Set mcmpCurrentForm =VBInstance.SelectedVBComponent
If (mcmpCurrentForm.Type<>vbext-ct-VBForm) And-
(mcmpCurrentForm.Type<>vbext-ct-UseControl) And-
(mcmpCurrentForm.Type<>vbext-ct-DocObject) And-
(mcmpCurrentForm.Type<>vbext-ct-PropPage) Then
Exit Sub
End if
For Each control in mcmpCurrentForm.Designer.VBControls
List.AddItem control.ControlObject.Name
control.ControlObject.FontName= NewFontName
control.ControlObject.FontSize= NewFontSize
control.ControlObject.FontBole= NewFontBole
control.ControlObject.FontItalic= NewFontItalic
Next
End Sub
Private Sub ExitButton-Click (Index As Integer)
Connect.Hide
End Sub
Private Sub FontButton-Click (Index As Integer)
Select Case Index
Case 0
ChangeFont "宋体",9,False,False
Case 1
With CommonDialog1
.Flags = cdlCFBoth
.ShowFont
ChangeFont.FontName,.FontSize,.FontBold,.FontItalic
End With
End Select
End Sub
上面的代码比较简单,关键的代码在于获取屏幕上的所有控制,在Visual B asic5.0下,与早期版本不同,它利用了VBComponent对象的Designer属性获取屏 幕上的设计器,它可以是窗体,也可以是ActiveX控件或ActiveX文档等,然后通 过该设计器来获得设计器上的用户控制。上述代码中,首先将mcmpCurrenForm变 量被设为当前选中的构件,然后判断它是否是窗体,用户控件,ActiveX文档或属 性页,如果是,则获取其设计器即Designer对象,然后通过Designer对象的VBCo ntrols属性获得其上所有控制的集合,这样就可以操作该集合中的每一个VBCont rols控制了。对每一个控制,通过其ControlObject 属性可以获得其相应的屏幕 上的控制对象,然后就可以设置该对象的任意属性了。在上述代码中,也可以使 用control.Properties!Fontname取代control.ControlObject.FontName,其结果 是一样的。另外,为了防止对没有Font属性的控制进行该属性设置将导致错误发 生,在程序的开头使用了On Error Resume Next语句以忽略这些错误。下面是该 程序的用户界面:
四、 进一步的改进
对上述程序还可做进一步的改进,下面列出几种改进思想及编程方案。
1、 设置控制的其它属性
上述代码主要用来设置控制的字体,但基于同样的原理,我们只要对代码稍 加改变就可以设置控制的其它属性,如前景色,背景色,可见或不可见等。
2、 为当前工程中的所有窗体设置控制属性
在上述代码中,是对当前选中的窗体里的控制设置字体,但有时我们需要将 工程中的所有窗体中的所有控制一次设置其属性,而不是每次选择一个窗体进行 设置。这时可以采用如下方法:首先用VBE(即当前会话期实例)的ActiveVBPro ject属性获得当前的工程,然后通过其VBComponents属性获得当前工程中的所有 组件集合,针对每一组件判断它是否为窗体或 ActiveX控件等,剩下的代码就和 上面基本一样了。
3、 仅为选中的控制设置属性
若只是需要为选中的控制设置属性则可以利用VBForm 对象的SelectedVBCon trols属性获得当前选中的所有控制的集合,然后对其包含的控制设置属性即可。 也可利用VBControl的InSelection属性判断该控制是否被选中,然后仅对选中的 控制设置属性即可。
4、 当控件被添加到窗体时,自动为它设置属性
要实现当控件被添加到窗体时,自动为它设置属性的功能需要用到VBE的事件 对象,即与上面提到的为菜单提供事件代码的方法一样。首先声明一个事件变量 :Public WithEvents CtlHandler As VBControlsEvents,然后在接口IDTExtens ibility的OnConnection方法代码中挂接控制事件处理:Set Me.CtlHandler=VBI nstance.Events.VBControlsEvents (Nothing,Nothi-ng),这样就可以进行事件 处理了。而进行事件处理的代码可写在CtlHandler变量的ItemAdded代码中,如下 :
Private Sub CtlHandler-ItemAddec (ByVal VBControl As VBIDE.VBControl)
'这里可加入所需要的代码。
End Sub
当然,除上述改进外,我们还可以提出更多的改进方案,以丰富原程序的功 能。总之,由于VB显露其IDE包括窗口、工程、控制,甚至事件、代码等各个部分 的接口,使得我们能非常简单而有效地控制自己的编程活动和定制自己的界面, 从而节省了编程时间,提高了工作效率,也减少了错误的发生。
参考文献
1、[美]鲍(Boyle,D)等著·Visual Basic 4 开发人员指南·薛万鹏等译·机械 工业出版社
2、Visual Basic 5联机手册·Microsoft Corporation
=============================================
问题征答()
=============================================
其它
====================================================================
欢迎订阅VB免费邮件:
订阅地址 http://server.com/WebApps/mail-list-subscribe.cgi?id=16852
====================================================================
如果您觉得这个邮件列表好的话,请告诉您的朋友。
====================================================================
欢迎投稿 [email protected]
====================================================================
网易上的主页地址:http://www4.netease.com/~aaaaaaaaa
本网站主页镜像地址:goodvbhome.yeah.net
====================================================================
-- ※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 202.103.48.139]
|
|