你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:杂志经典 / 编程语言
自动列表技术在VB中的实践(上)
 

本文所说自动列表技术是指类似于VBVC6.0中的自动列出成员功能,用过它的朋友恐怕都不会陌生吧!当系统能确定您即将输入的单词在某一有限范围内时,它就会将所有可能输入列出来,并根据你输入的不完整的单词、从列表中选出最匹配的单词自动输入。这种技术大大提高了录入效率,特别是在某些数据库程序中非常有利于提高输入速度、降低出错率和减少录入人员的劳动量。

现在,我们来做一个名叫AutoList的控件,同时用一个简单的应用程序演示它的功能。您将会看到,把此控件加入应用程序中,只须写少量代码就可使普通文本框具有自动列表的特性。

一、AutoList控件的制作:

VB提供了较完善的控件开发工具,用它提供的丰富的控件-应用程序接口方式可以方便、迅速的开发各种控件。编码之前,我们确定要开发的AutoList控件应具有如下特性:

A键入指定的单词后,弹出对应的列表框。

B弹出列表框后,根据随后键入的不完整的单词,加亮显示第一个匹配的列表项。

C鼠标双击任意列表项,列表项跳出到光标处;若有列表项被加亮显示,敲入一个分隔符(分隔符的定义随后说明)则该列表项跳出到光标处。

D光标离开当前输入单词范围,列表框消失。

E其它用户易于操作的特性。

知道需要实现以上功能后,现在结合源代码简要介绍一下编制AutoList控件的基本思路和主要技巧:

控件中声明了一个窗体变量fOwnForm,两个文本框变量mOwnEditfOwnEdit。其中,fOwnFormfOwnEdit是全局变量,mOwnEdit是模块级变量。fOwnForm的作用是让控件知道弹出的列表框应浮动与哪个窗体之上。mOwnEdit就是我们要使它具有自动列表功能的文本框。声明mOwnEdit变量时用了WithEvents关键字,这样,在控件内部就能捕获到mOwnEdit的事件,从而大大提高了控件的封装性能。而fOwnEditmOwnEdit表示的是同一个文本框,只是fOwnEdit为全局变量,使控件中的其它模块能访问此文本框。

为了让AutoList知道何时弹出对话框,外部程序必须向控件加入关键字集合Keys和分隔符集合Separators,分隔符就是分开相邻单词的间隔符(如空格等),程序通过分隔符来分辨一个单词。当满足以下要求时弹出Key对应的列表项:(1) 输入了一个单词Key,且Key属于集合Keys(2) 紧跟Key后键入了一个分隔符SeparatorSeparator属于集合Separators。我想,用过VB的朋友应该容易理解这种处理方式。

为了让AutoList的列表框在光标位置弹出,引用了两个API函数:GetCaretPos()ClientToScreen()GetCaretPos()获得当前光标在客户区的坐标,ClientToScreen()将此坐标转化为对应的屏幕坐标。另外,由于文本框没有TextHeight属性,在得到文本框字符高度时,采用了一个较迂回的办法:将文本框的Font对象赋给窗体,利用窗体的TextHeight属性间接得到文本框的字符高度。要注意单位变换。

制作该控件主要要注意的就这些,源代码中也有较详细的注释。现介绍编制它的全过程。

1)打开VB,在新建工程的对话框中选ActiveX Control,新建一个工程。选菜单Project\Add Form,在弹出的对话框中选Flash Screen加入一个无标题条的窗体,将它的名字改为fBox,删除窗体中所有默认的控件,并向窗体中加入一个ListBox控件。设置其属性值如表中所示。

选菜单Project\Add Module   加入 一代码模块 在此模块中加入代码如下:

'代码模块 Module1.bas 定义全局变量和函数。

Option Explicit

Global bListting As Boolean '列表框是否已经弹出

Global fOwnEdit As TextBox '与此控件相连的文本框

Global fOwnForm As Form  '文本框所在的窗体

Global Keys As New Collection '容纳列表框的集合

Global iNum As Integer '列表框总数

Global Separators As New Collection '分隔符集合

Global lEnd As Long '暂存上次文本框中的光标位置

Global lThis As Long '暂存本次文本框中的光标位置

Public Function GetLastString(iStart As Integer)

'取得刚输入的字符串,最大长度为 100

Dim tempStr As String, sSingle As String

Dim i As Long

sSingle = ""

tempStr = ""

For i = 0 To 100

  If i >= iStart Then GoTo Exit2

  sSingle = Mid(fOwnEdit.Text, (CLng(iStart) - i), 1)

  If IsSeparator(sSingle) Then GoTo Exit2 '遇到分隔符,返回

  tempStr = Mid(fOwnEdit.Text, CLng(iStart) - i, 1) + tempStr

Next

Exit2:

GetLastString = tempStr

End Function

Public Function IsSeparator(sChr As String) As Boolean

'判断是否分隔符,空格是默认的分隔符

Dim sSeparator As Variant

If sChr = Chr(10) Or sChr = Chr(13) Or sChr = " " Then

  IsSeparator = True

  Exit Function

End If

For Each sSeparator In Separators

  If sChr = sSeparator Then

    IsSeparator = True

    Exit Function

  End If

Next

IsSeparator = False

End Function

2)在工程管理器中,双击UserControl1,弹出控件设计窗体,在属性列表中将UserControl1的名字改为AutoList。双击控件设计窗体的用户区,在弹出的代码窗口中加入以下代码:

'模块 AutoList 处理控件与外部程序的接口

'主要通过 mOwnEdit 实现接口

Option Explicit

Dim lActive As ListBox '当前列表框

Dim LockKey As Boolean '是否锁定上下键

Dim LockStart As Long '锁定上下键时光标位置

Dim WithEvents mOwnEdit As TextBox '与之关联的文本框

Public Sub AddSeparator(sSeparator As String)

'添加分隔符

  If sSeparator = " " Then Exit Sub    '空格是保留的分隔符

  If Len(sSeparator) <> 1 Then Exit Sub

  Separators.Add sSeparator

End Sub

Public Sub DelSeparator(sSeparator As String)

'删除分隔符

On Error Resume Next

  Dim i  As Integer

  If sSeparator = " " Then Exit Sub    '空格是保留的分隔符

  If Len(sSeparator) <> 1 Then Exit Sub

  For i = 1 To Separators.Count

    If Separators(i) = sSeparator Then

      Separators.Remove i

      Exit Sub

    End If

  Next

End Sub

Public Property Set OwnForm(fForm As Object)

  '文本框所在的窗体

  Set fOwnForm = fForm

End Property

 

Public Property Set OwnEdit(eEdit As Object)

  '与此控件连接的文本框

  Set mOwnEdit = eEdit 'mOwnEdit 用以响应事件

  Set fOwnEdit = eEdit 'fOwnEdit 为全局变量,

                       '供其它模块引用

End Property

Public Sub AddKey(sKey As String)

'添加用以激活此控件的关键字

 'On Error Resume Next

 Dim i As Integer

 For i = 1 To iNum

 With fBox.List1(i)

  If .Tag = "free" Then

    .Tag = "" '清除标志

    Keys.Add fBox.List1(i), sKey '加入集合 Keys,关键字为 sKey

    Exit Sub

  End If

 End With

 Next

 iNum = iNum + 1

 Load fBox.List1(iNum) '加载 List1 控件

 Keys.Add fBox.List1(iNum), sKey

End Sub

Public Sub DelKey(sKey As String)

On Error Resume Next

  Dim i As Integer, j As Integer

  i = Keys(sKey).Index

  Keys.Remove sKey

  With fBox.List1(i)

    For j = .ListCount To 0 Step -1

      .RemoveItem j

    Next

 .Tag = "free" '列表框已为空的标志

  End With

End Sub

Public Sub AddList(sKey As String, sList As String)

On Error Resume Next

'向关键字 sKey 中添加列表项 sList

'当识别到用户输入 sKey 时将列出所有在这里加入的 sList

  Keys(sKey).AddItem sList

End Sub

Public Sub DelList(sKey As String, sList As String)

'删除关键字 sKey 中的列表项 sList

On Error Resume Next

  Dim i As Integer

  With Keys(sKey)

    For i = 0 To .ListCount

      If .List(i) = sList Then

        .RemoveItem i

      End If

    Next

  End With

End Sub

Public Sub PopList(sKey As String)

'弹出此控件

 fBox.PopOut sKey

End Sub

Public Sub FreeMem()

'清除内容,释放所有动态装载的对象,

'供外部程序重新利用

On Error Resume Next

  Dim i As Integer

  For i = Keys.Count To 1 Step -1

    Keys.Remove i

  Next

  For i = Separators.Count To 1 Step -1

    Separators.Remove i

  Next

  For i = 1 To iNum

    Unload fBox.List1(i)

  Next

  iNum = 0

End Sub

Private Sub mOwnEdit_KeyDown(KeyCode As Integer, Shift As Integer)

  '在次事件中处理输入字符串和分隔符,说明如下:

  '第一层 if 条件语句:对fBox是否弹出的情况分别处理

  '第二层 if 条件语句:若fBox未弹出,对分隔符和非分隔符分别处理

  '                fBox已弹出,先处理控制键(方向键和Esc)

  '                再对分隔符和非分隔符分别处理

  Dim tempKey As String '刚输入的字符串

  Dim lShow As ListBox

  Dim SeparatorBak As Boolean '刚输入的字符是否为分隔符

  Dim lLen As Long, selBak As Long, TxtBak As String

  On Error Resume Next

  DoEvents

  If LockKey Then '键盘已锁定

    If fOwnEdit.SelStart <> LockStart Then

      '光标位置已改变,隐藏

      LockKey = False

      lEnd = 0: lThis = 0

      bListting = False

      Set lActive = Nothing

      fBox.GoBack

    End If

    Exit Sub

  End If

  SeparatorBak = IsSeparator(Mid(fOwnEdit.Text, fOwnEdit.SelStart, 1))

'  If SeparatorBak Then Stop

  If Not bListting Then     '若列表框未弹出(第一层 if 条件语句)

    Select Case KeyCode

      Case 38, 40, 37, 39, 8, 27

        Exit Sub '若是方向键或退格键、Esc,跳出

    End Select

    lEnd = fOwnEdit.SelStart

    lThis = lEnd

    If SeparatorBak Then '若是分隔符,取得并处理刚输入的字符串

                         '(第二层 if 条件语句)

      If KeyCode = 13 Then

        '因为多个换行符

        tempKey = GetLastString(CInt(lEnd - 2))

      Else

        tempKey = GetLastString(CInt(lEnd - 1))

      End If

      If Len(tempKey) <= 1 Then Exit Sub

      tempKey = Trim(tempKey)

      If Mid(tempKey, Len(tempKey), 1) = Chr(13) Then _

        tempKey = Left(tempKey, Len(tempKey) - 1)

      Set lShow = Keys(tempKey) '在集合 Keys 中查找关键字为

                                  'tempKey 的成员

      If lShow Is Nothing Then Exit Sub '未找到,跳出

      bListting = True

      Set lActive = lShow

      PopList tempKey

    Else            '不是分隔符,跳出(第二层 if 条件语句)

      Exit Sub

    End If

  Else                      '若列表框已弹出(第一层 if 条件语句)

    Select Case KeyCode

    Case 37, 39, 8, 27 '若是左右键、退格键或 Esc

      selBak = fOwnEdit.SelStart

      If KeyCode = 37 Then  '计算响应此键后光标的位置

        selBak = selBak - 1

      ElseIf KeyCode = 39 Then

        selBak = selBak + 1

      ElseIf KeyCode = 8 Then

        lThis = fOwnEdit.SelStart

        '向列表框发送字符串,查找于之匹配的列表项

        fBox.ReceiStr GetLastString(fOwnEdit.SelStart)

      End If

      If selBak < lEnd Or selBak > lThis _

        Or KeyCode = 27 Then '若光标离去或按下Esc键,隐藏

        lEnd = 0: lThis = 0

        bListting = False

        Set lActive = Nothing

        fBox.GoBack

      End If

      Exit Sub

    Case 38, 40

      LockStart = fOwnEdit.SelStart

      LockKey = True '锁定上下键用于滚动列表框

      With fBox.lThisList

        .SetFocus

        If KeyCode = 38 Then

          If .ListIndex = -1 Then .ListIndex = .ListCount - 1 _

            Else .ListIndex = .ListIndex - 1

        Else

          .ListIndex = .ListIndex + 1

        End If

      End With

      Exit Sub

    End Select

    If SeparatorBak Then '若输入分隔符,将列表项插入到文本框

                         '(第二层 if 条件语句)

    With fOwnEdit

      If lActive.ListIndex >= 0 Then

        lLen = .SelStart - lEnd  '须替换的长度

        .SelStart = lEnd

        .SelLength = lLen

        .SelText = lActive.Text  '插入

'        DoEvents

        TxtBak = Chr(KeyCode)  '加上刚输入的字符

      End If

      lEnd = 0 '复位标志

      bListting = False

      Set lActive = Nothing

      fBox.GoBack

      If TxtBak <> "" Then

        fOwnEdit.SetFocus

        SendKeys TxtBak '补上刚输入的字符

        DoEvents

      End If

     Exit Sub

    End With

    Else '若输入的不是分隔符(第二层 if 条件语句)

      lThis = fOwnEdit.SelStart

      '向列表框发送字符串,查找于之匹配的列表项

      fBox.ReceiStr GetLastString(fOwnEdit.SelStart)

    End If

  End If

End Sub

  推荐精品文章

·2024年9月目录 
·2024年8月目录 
·2024年7月目录 
·2024年6月目录 
·2024年5月目录 
·2024年4月目录 
·2024年3月目录 
·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录
·2023年10月目录
·2023年9月目录 
·2023年8月目录 

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089