(六) 无闪烁地快速附加字符串到textbox控件 附加文本到TextBox或者RichTextBox控件的通常方法是在当前内容上连接上新的字符串: Text1.Text = Text1.Text & newString 但还有一个更快的方法,并且会减少连接操作的闪烁感,代码如下: Text1.SelStart = Len(Text1.Text) Text1.SelText = newString 快速找到选中的OptionButton OptionButton控件经常是作为控件数组存在的,要快速找到其中的哪一个被选中,可以使用下面的代码: ''假设控件数组包含3个OptionButton控件 intSelected = Option(0).Value * 0 - Option(1).Value * 1 - Option(2).Value * 2 注意,因为第一个操作数总是0,所以上述代码可以精简如下: intSelected = -Option(1).Value - Option(2).Value * 2 表单及控件的引用阻止了表单的卸载 当指派表单或者表单上的控件到该表单模块以外的一个对象变量中时,如果要卸载表单,就必须首先将那个变量设置为 to Nothing。也就是说 ,如果不设置为Nothing,即使看不到这个对象了,但它仍旧是保存在内存中的。 注意:这并非是一个bug,这仅仅是COM引用规则的一个结果。唯一要注意的就是引用的这个控件将阻止整个表单的卸载操作,它将依赖于它的 父表单而存在。 重定义编译DLL文件的基地址 许多VB开发者都知道应该在工程属性对话框的“编译”功能页面中定义一个DLL基地址数值。这不同于工程中任何其他DLL或OCX的基地址。 当操作没有源代码的编译DLL或者OCX文件时,可以使用EDITBIN程序修改它的基地址。EDITBIN程序随Visual Studio安装后就有了,可以在主Visual Studio目录的VC98\BIN目录下找到它。比如,以下代码重新设定一个编译DLL文件的基地址为12000000 (16进制): EDITBIN /REBASE:BASE=0x12000000 myfile.dll 同样,EDITBIN程序对可执行文件也有一些处理技巧。 以下是该程序支持的完整功能选项列表(使用EDITBIN /? 可以列出这些): /BIND[:PATH=path] /HEAP:reserve[,commit] /LARGEADDRESSAWARE[:NO] /NOLOGO /REBASE[:[BASE=address][,BASEFILE][,DOWN]] /RELEASE /SECTION:name[=newname][,[[!]{cdeikomprsuw}][a{1248ptsx}]] /STACK:reserve[,commit] /SUBSYSTEM:{NATIVE|WINDOWS|CONSOLE|WINDOWSCE|POSIX}[,#[.##]] /SWAPRUN:{[!]CD|[!]NET} /VERSION:#[.#] /WS:[!]AGGRESSIVE 快速调入TreeView控件以及ListView控件的子项内容 有一个简单但仍未发现的技巧可用于在TreeView控件中装载多个节点,或者在ListView控件中装载多个ListItems。这种方法要比传统做法快。 先看看下面这个传统方法: For i = 1 To 5000 TreeView1.Nodes.Add , , , "Node " & i Next 改进一下,代替重复引用TreeView1对象的Nodes集合,我们可以先将之保存在临时对象变量中: Dim nods As MSComctlLib.Nodes Set nods = TreeView1.Nodes For i = 1 To 5000 nods.Add , , , "Node " & i Next 甚至,如果使用With代码块,还可以不需要临时变量: With TreeView1.Nodes For i = 1 To 5000 .Add , , , "Node " & i Next End With 经测试,优化的循环代码要比传统方法执行速度快40%左右。原因在于:将Nodes集合对象保存在临时变量中,或者应用With代码块后VB将使用 隐藏的临时变量后,就可以避免在循环中重复绑定Nodes对象到它的父TreeView1对象上。由于这种绑定是低效率的,因此省却它就能节省大量 的执行时间。 同样的道理对于其他ActiveX控件也生效: ListView控件的ListItems、ListSubItems以及ColumnHeaders集合 Toolbar控件的Buttons和ButtonMenus集合 ImageList的ListImages集合 StatusBar控件的Panels集合 TabStrip控件的Tabs集合 (七) Friend过程快于Public过程 你可能会非常惊奇:Friend类型过程的执行速度要明显快于Public类型。这可以通过创建一个带有Private类和Public类 (设定Instancing = MultiUse)的ActiveX EXE工程看到,在2个类模块中添加下面的代码: Public Sub PublicSub(ByVal value As Long) '' End Sub Public Function PublicFunction(ByVal value As Long) As Long '' End Function Friend Sub FriendSub(ByVal value As Long) '' End Sub Friend Function FriendFunction(ByVal value As Long) As Long '' End Function 然后,在表单模块中创建一个循环,执行每个例程许多次。比如,要在一个Pentium II机器上查看执行时间上的区别,可以调用每个例程1,000,000次。下面是测试的结果: Private类模块中,反复调用1,000,000次Public Sub或者Function耗费了0.46秒,而调用内容相同的Friend类型模块则分别只有0.05秒和0.06 秒。前后竟然相差了8-9倍之多!对于MultiUse类型的Public类模块,也是一样的结果。 对于这个不可思议的结果的可能解释是:Friend型过程没有处理汇集和拆装代码的消耗(Public过程可以从当前工程外被调用,因此COM必须要 来回地汇集数据)。 但是在多数情况下,这些时间差别是不明显的,特别是程序中包含一些复杂和耗时的语句时。 即使这样,Friend型过程仍有其他的优势高于Public类型,比如:接受和返回在BAS模块中定义的UDT变量的能力。 使用Objptr函数快速查找集合中的对象 ObjPtr函数的一个最简单但是却最有效的用途就是提供快速寻找集合中对象的关键字。假设有一个对象集合,它没有可以当做关键字以从集合 中取回的属性。那么,我们就可以使用ObjPtr函数的返回值作为集合中的关键字: Dim col As New Collection Dim obj As CPerson ''创建新的CPerson对象,并添加到集合中 Set obj = New CPerson obj.Name = "John Smith" col.Add obj, CStr(ObjPtr(obj)) ''关键字必须是字符串 因为任何对象都有一个明确的ObjPtr数值,而且它是不变的,所以,我们可以容易地、快速地从集合中取回它: '' 删除集合中的对象 col.Remove CStr(ObjPtr(obj)) 这个技巧可以适用于任何类型的对象,包括VB中的表单和控件,以及外部对象。 使用ObjPtr检测2个对象变量是否指向同一对象 判断2个对象变量释放指向同一对象的方法是使用Is操作符,代码如下: If obj1 Is obj2 Then ... 但当2个对象是同一类型时,或者指向同一个二级接口时,我们就可以利用ObjPtr()函数对代码进行一些优化处理: If ObjPtr(obj1) = ObjPtr(obj2) Then ... 后者的执行速度将比前种方法快40%多。但是请注意,2种方法原本就是很有效率的,只有在时间要求非常严格的上百成千次的循环中,才会体 现出这种差别。 读取文件内容的简洁方法 读取text文件的最快方法是使用Input$函数,就象下面的过程: Function FileText (filename$) As String Dim handle As Integer handle = FreeFile Open filename$ For Input As #handle FileText = Input$(LOF(handle), handle) Close #handle End Function 使用上述方法要比使用Input命令读取文件每一行的方法快很多。下面是应用这个函数读取Autoexec.bat的内容到多行textbox控件的例子: Text1.Text = FileText("c:\autoexec.bat") 但请注意:当文件包含Ctrl-Z(EOF)字符时,上面的函数代码可能会发生错误。因此,要修改一下代码: Function FileText(ByVal filename As String) As String Dim handle As Integer '' 判断文件存在性 If Len(Dir$(filename)) = 0 Then Err.Raise 53 ''文件没有找到 End If '' 以binary模式打开文件 handle = FreeFile Open filename$ For Binary As #handle '' 读取内容,关闭文件 FileText = Space$(LOF(handle)) Get #handle, , FileText Close #handle End Function 字体对象克隆招法 当要应用一个控件的字体到另一控件时,最直接的方法就是直接赋值: Set Text2.Font = Text1.Font 但多数情况下这种方法并不奏效,因为这实际上是将同一字体的引用分配给了2个控件。换言之,当随后修改其中之一控件的字体时,另外一个 控件也受到影响。因此,要实现我们的目的,需要做的就是克隆字体对象并赋值给需要的控件。 最简单的克隆字体的方法是手工地拷贝所有单独的字体属性,就象下面一样: Function CloneFont(Font As StdFont) As StdFont Set CloneFont = New StdFont CloneFont.Name = Font.Name CloneFont.Size = Font.Size CloneFont.Bold = Font.Bold CloneFont.Italic = Font.Italic CloneFont.Underline = Font.Underline CloneFont.Strikethrough = Font.Strikethrough End Function ''函数的应用 Set Text2.Font = CloneFont(Text1.Font) 如果使用VB6,就可以使用PropertyBag对象快速拷贝所有字体属性,并且代码会很简练、速度也快2倍: Function CloneFont(Font As StdFont) As StdFont Dim pb As New PropertyBag ''拷贝字体到PropertyBag对象中 pb.WriteProperty "Font", Font 恢复字体对象到新控件 Set CloneFont = pb.ReadProperty("Font") End Function 但是我们还能进一步地对代码进行优化,方法就是使用可被所有StdFont对象识别的隐藏IFont接口。这个接口具有一个Clone方法,用它就可以 精确地实现我们的目的。它以非正常方式执行:创建一个克隆Font对象,然后返回相应的引用。这可能是实现克隆目的的最简洁代码了,而且 ,执行速度也是这里列举的3种方法中最快的一个,要比使用PropertyBag对象的方法快大约3倍左右。来看看具体代码: Function CloneFont(Font As IFont) As StdFont Font.Clone CloneFont End Function
|