子字符串上的组合框自动完成

Posted

技术标签:

【中文标题】子字符串上的组合框自动完成【英文标题】:ComboBox AutoComplete on SubString 【发布时间】:2011-04-11 07:38:00 【问题描述】:

在我的一个 WinForms 应用程序中,我有一个带有 ComboBox 的窗口供用户从中选择客户。

此列表框中的客户采用以下格式:“CustomerID - CustomerName”,例如“004540 - NorthWind Traders”

本机 WinForms 组合框具有内置的自动完成功能,并且运行良好:问题是它只能通过从组合框列表中每个项目的字符串开头匹配而不是从任何位置(子字符串)匹配。

我希望我的用户能够输入 CustomerID 或 CustomerName 的任何一种类型,因为高级用户熟悉大多数 CustomerID,而新员工将受益于能够输入 CustomerName 并获得自动完成功能. 这意味着我实际上想从输入文本是 ComboBox 项的子字符串的列表中查找最佳匹配。

对于这种情况,通常建议的解决方案是创建一个仅在用户键入时才显示的隐藏列表框,但我对此并不满意,因为它感觉像是一种快速破解并且不易重复使用,并且与标准 ComboBox 控件相比,外观和行为可能不一致。

我自己尝试使用 DroppedDown 属性来实现这一点,以使列表出现并使用 SelectedIndex 来设置项目,但是当我这样做时,组合框的文本框的内容会被重置,而我只想要“最佳匹配item”要从 ComboBox 列表中突出显示(我需要“Suggest”而不是“Append”,Append-mode 不能真正用于子字符串匹配)。

我认为一定有更好的方法吗? 如果有人知道这样做的自定义/第 3 方控件,我也不反对购买。

谢谢。

PS:我正在使用 C# 为 WinForms 编程,使用 .Net Framework 3.5。

【问题讨论】:

【参考方案1】:

好吧,我有一些代码供您尝试。它不是一个组合框,而是一个自动完成文本框,可以根据您的要求进行修改。

将代码复制到新表单中。然后在做任何其他事情之前,保存并构建。然后转到表单设计器并将新的 ClsCustomAutoCompleteTextbox 拖到您的表单上。

那么你应该可以运行它了。我确实意识到你想要 C#(至少现在我意识到了)。在 VB 中试试这个,看看是不是你想要的,我可以把它转换成 C#。

Public Class Form1
  Dim MasterList As New List(Of String)


  Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    Dim L As New List(Of String)

    L.Add("123123 - Bob")
    L.Add("534543 - Sally")
    L.Add("123123 - George")
    L.Add("34213 - Happy")

    MasterList = L

    Me.ClsCustomAutoCompleteTextbox1.AutoCompleteList = L
  End Sub

  Private Sub ClsCustomAutoCompleteTextbox1_BeforeDisplayingAutoComplete(ByVal sender As Object, ByVal e As clsCustomAutoCompleteTextbox.clsAutoCompleteEventArgs) Handles ClsCustomAutoCompleteTextbox1.BeforeDisplayingAutoComplete

    Dim Name As String = Me.ClsCustomAutoCompleteTextbox1.Text.ToLower

    Dim Display As New List(Of String)

    For Each Str As String In MasterList
      If Str.ToLower.IndexOf(Name) > -1 Then
        Display.Add(Str)
      End If
    Next

    e.AutoCompleteList = Display
    e.SelectedIndex = 0
  End Sub
End Class


#Region "clsCustomAutoCompleteTextbox"
Public Class clsCustomAutoCompleteTextbox
  Inherits TextBox
  Event BeforeDisplayingAutoComplete(ByVal sender As Object, ByVal e As clsAutoCompleteEventArgs)
  Event ItemSelected(ByVal sender As Object, ByVal e As clsItemSelectedEventArgs)


  Public test As New List(Of String)
  Public Tabs As Integer = 0


  Private Function GetLastFunction(Optional ByVal Deep As Integer = 1) As System.Reflection.MethodInfo
    Dim ST As New StackTrace
    Dim Frame As StackFrame = ST.GetFrame(Deep)

    Return Frame.GetMethod()
  End Function

  Private Sub TempLogStart()
    'Dim Meth As System.Reflection.MethodInfo = GetLastFunction(3)

    'test.Add(Now & " - " & New String(" ", Tabs * 2) & "Started " & Meth.Module.Name & "." & Meth.Name)

    'Tabs += 1
  End Sub

  Private Sub TempLogStop()
    '  Dim Meth As System.Reflection.MethodInfo = GetLastFunction(3)

    '  Tabs -= 1

    '  test.Add(Now & " - " & New String(" ", Tabs * 2) & "Stopped " & Meth.Module.Name & "." & Meth.Name)
  End Sub

  Public Enum SelectOptions
    OnEnterPress = 1
    OnSingleClick = 2
    OnDoubleClick = 4
    OnTabPress = 8
    OnRightArrow = 16
    OnEnterSingleClick = 3
    OnEnterSingleDoubleClicks = 7
    OnEnterDoubleClick = 5
    OnEnterTab = 9
    'OnItemChange = 32
  End Enum

  Private mSelStart As Integer
  Private mSelLength As Integer

  Private myAutoCompleteList As New List(Of String)
  Private WithEvents myLbox As New ListBox
  Private WithEvents myForm As New Form
  Private WithEvents myParentForm As Form

  Private DontHide As Boolean = False
  Private SuspendFocus As Boolean = False

  Dim Args As clsAutoCompleteEventArgs

  WithEvents HideTimer As New Timer()
  WithEvents FocusTimer As New Timer()

  Private myShowAutoCompleteOnFocus As Boolean
  Private myAutoCompleteFormBorder As System.Windows.Forms.FormBorderStyle = FormBorderStyle.None
  Private myOnEnterSelect As Boolean
  Private mySelectionMethods As SelectOptions = (SelectOptions.OnDoubleClick Or SelectOptions.OnEnterPress)
  Private mySelectTextAfterItemSelect As Boolean = True


  Public Property SelectTextAfterItemSelect() As Boolean
    Get
      Return mySelectTextAfterItemSelect
    End Get
    Set(ByVal value As Boolean)
      mySelectTextAfterItemSelect = value
    End Set
  End Property

  <System.ComponentModel.Browsable(False)> _
  Public Property SelectionMethods() As SelectOptions
    Get
      Return mySelectionMethods
    End Get
    Set(ByVal value As SelectOptions)
      mySelectionMethods = value
    End Set
  End Property

  Public Property OnEnterSelect() As Boolean
    Get
      Return myOnEnterSelect
    End Get
    Set(ByVal value As Boolean)
      myOnEnterSelect = value
    End Set
  End Property

  Public Property AutoCompleteFormBorder() As System.Windows.Forms.FormBorderStyle
    Get
      Return myAutoCompleteFormBorder
    End Get
    Set(ByVal value As System.Windows.Forms.FormBorderStyle)
      myAutoCompleteFormBorder = value
    End Set
  End Property

  Public Property ShowAutoCompleteOnFocus() As Boolean
    Get
      Return myShowAutoCompleteOnFocus
    End Get
    Set(ByVal value As Boolean)
      myShowAutoCompleteOnFocus = value
    End Set
  End Property

  Public ReadOnly Property Lbox() As ListBox
    Get
      Return myLbox
    End Get
  End Property

  Public Property AutoCompleteList() As List(Of String)
    Get
      Return myAutoCompleteList
    End Get
    Set(ByVal value As List(Of String))
      myAutoCompleteList = value
    End Set
  End Property

  Private Sub TryHideFormWindowsDeactivated()

  End Sub

  Private Declare Auto Function GetForegroundWindow Lib "user32.dll" () As IntPtr
  Private Declare Auto Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hWnd As IntPtr, ByRef ProcessID As Integer) As Integer

  Private Function IsCurProcess(ByVal P As Process) As Boolean
    Dim Ptr As IntPtr = P.MainWindowHandle


  End Function

  Private Function AppHasFocus(Optional ByVal ExeNameWithoutExtension As String = "") As Boolean
    Dim Out As Boolean = False
    Dim PID As Integer = 0

    TempLogStart()

    If ExeNameWithoutExtension = "" Then
      ExeNameWithoutExtension = Process.GetCurrentProcess.ProcessName
    End If
    Dim activeHandle As IntPtr = GetForegroundWindow()
    Call GetWindowThreadProcessId(activeHandle, PID)
    If PID > 0 Then
      'For Each p As Process In Process.GetProcessesByName(ExeNameWithoutExtension)
      If PID = Process.GetCurrentProcess.Id Then
        Out = True
        'Exit For
      End If
      ' Next
    End If

    TempLogStop()

    Return Out
  End Function

  Private Sub SaveSelects()
    Me.mSelStart = Me.SelectionStart
    Me.mSelLength = Me.SelectionLength
  End Sub

  Private Sub LoadSelects()
    Me.SelectionStart = Me.mSelStart
    Me.SelectionLength = Me.mSelLength
  End Sub

  Private Sub ShowAutoComplete()
    TempLogStart()

    Args = New clsAutoCompleteEventArgs()

    With Args
      .Cancel = False
      .AutoCompleteList = Me.myAutoCompleteList

      If myLbox.SelectedIndex = -1 Then
        .SelectedIndex = 0
      Else
        .SelectedIndex = myLbox.SelectedIndex
      End If
    End With

    RaiseEvent BeforeDisplayingAutoComplete(Me, Args)

    Me.myAutoCompleteList = Args.AutoCompleteList

    'If Me.myAutoCompleteList IsNot Nothing AndAlso Me.myAutoCompleteList.Count - 1 < Args.SelectedIndex Then
    '  Args.SelectedIndex = Me.myAutoCompleteList.Count - 1
    'End If

    If Not Args.Cancel AndAlso Args.AutoCompleteList IsNot Nothing AndAlso Args.AutoCompleteList.Count > 0 Then
      Call DoShowAuto()
    Else
      Call DoHideAuto()
    End If
    TempLogStop()
  End Sub

  Private Sub DoShowAuto()
    Call SaveSelects()

    TempLogStart()
    Static First As Boolean = True
    myLbox.BeginUpdate()
    Try
      myLbox.Items.Clear()
      myLbox.Items.AddRange(Me.myAutoCompleteList.ToArray)

      Call Me.MoveLBox(Args.SelectedIndex)
    Catch ex As Exception
    End Try
    myLbox.EndUpdate()


    myParentForm = GetParentForm(Me)
    If myParentForm IsNot Nothing Then
      myLbox.Name = "mmmlbox" & Now.Millisecond
      If myForm.Visible = False Then
        myForm.Font = Me.Font
        myLbox.Font = Me.Font

        myLbox.Visible = True
        myForm.Visible = False

        myForm.ControlBox = False

        myForm.Text = ""

        If First Then
          myForm.Width = Me.Width
          myForm.Height = 200
        End If

        First = False

        If Not myForm.Controls.Contains(myLbox) Then myForm.Controls.Add(myLbox)
        myForm.FormBorderStyle = FormBorderStyle.None
        myForm.ShowInTaskbar = False

        With myLbox
          .Dock = DockStyle.Fill
          .SelectionMode = SelectionMode.One
        End With

        'Frm.Controls.Add(myLbox)

        DontHide = True

        SuspendFocus = True


        myForm.TopMost = True
        myForm.FormBorderStyle = Me.myAutoCompleteFormBorder

        myForm.BringToFront()
        Call MoveDrop()
        myForm.Visible = True
        myForm.Show()
        Call MoveDrop()

        HideTimer.Interval = 10

        Me.Focus()

        SuspendFocus = False

        HideTimer.Enabled = True

        DontHide = False

        Call LoadSelects()
      End If
    End If
    TempLogStop()

  End Sub

  Sub MoveDrop()

    TempLogStart()
    Dim Pnt As Point = New Point(Me.Left, Me.Top + Me.Height + 2)
    Dim ScreenPnt As Point = Me.PointToScreen(New Point(-2, Me.Height))

    'Dim FrmPnt As Point = Frm.PointToClient(ScreenPnt)
    If myForm IsNot Nothing Then
      myForm.Location = ScreenPnt

      'myForm.BringToFront()


      'myForm.Focus()
      'myLbox.Focus()

      'Me.Focus()
    End If
    TempLogStop()
  End Sub

  Sub DoHide(ByVal sender As Object, ByVal e As EventArgs)

    TempLogStart()
    Call HideAuto()
    TempLogStop()
  End Sub

  Private Sub DFocus(Optional ByVal Delay As Integer = 10)

    TempLogStart()
    FocusTimer.Interval = Delay
    FocusTimer.Start()
    TempLogStop()
  End Sub

  Private Sub DoHideAuto()

    TempLogStart()
    myForm.Hide()

    HideTimer.Enabled = False
    FocusTimer.Enabled = False
    TempLogStop()
  End Sub

  Private Sub HideAuto()

    TempLogStart()
    If myForm.Visible AndAlso HasLostFocus() Then
      Call DoHideAuto()
    End If
    TempLogStop()
  End Sub

  Private Function HasLostFocus() As Boolean

    TempLogStart()
    Dim Out As Boolean

    If Me.myForm Is Nothing OrElse myForm.ActiveControl IsNot Me.myLbox Then
      Out = True
    End If

    If Me.myParentForm Is Nothing OrElse Me.myParentForm.ActiveControl IsNot Me Then
      Out = True
    End If

    TempLogStop()
    Return Out
  End Function

  Private Function GetParentForm(ByVal InCon As Control) As Form

    TempLogStart()
    Dim TopCon As Control = FindTopParent(InCon)
    Dim Out As Form = Nothing

    If TypeOf TopCon Is Form Then
      Out = CType(TopCon, Form)
    End If

    TempLogStop()
    Return Out
  End Function

  Private Function FindTopParent(ByVal InCon As Control) As Control

    TempLogStart()
    Dim Out As Control

    If InCon.Parent Is Nothing Then
      Out = InCon
    Else
      Out = FindTopParent(InCon.Parent)
    End If

    TempLogStop()
    Return Out
  End Function

  Public Class clsAutoCompleteEventArgs
    Inherits EventArgs

    Private myAutoCompleteList As List(Of String)
    Private myCancel As Boolean

    Private mySelectedIndex As Integer

    Public Property SelectedIndex() As Integer
      Get
        Return mySelectedIndex
      End Get
      Set(ByVal value As Integer)
        mySelectedIndex = value
      End Set
    End Property

    Public Property Cancel() As Boolean
      Get
        Return myCancel
      End Get
      Set(ByVal value As Boolean)
        myCancel = value
      End Set
    End Property

    Public Property AutoCompleteList() As List(Of String)
      Get
        Return myAutoCompleteList
      End Get
      Set(ByVal value As List(Of String))
        myAutoCompleteList = value
      End Set
    End Property
  End Class

  Protected Overrides Sub OnKeyUp(ByVal e As System.Windows.Forms.KeyEventArgs)

    TempLogStart()
    TempLogStop()
    MyBase.OnKeyUp(e)

    Call ShowOnChar(Chr(e.KeyValue))
  End Sub

  Private Sub ShowOnChar(ByVal C As String)

    TempLogStart()
    TempLogStop()
    If IsPrintChar(C) Then
      Call Me.ShowAutoComplete()
    End If
  End Sub

  Private Function IsPrintChar(ByVal C As Integer) As Boolean

    TempLogStart()
    TempLogStop()
    Return IsPrintChar(Chr(C))
  End Function

  Private Function IsPrintChar(ByVal C As Byte) As Boolean

    TempLogStart()
    TempLogStop()
    Return IsPrintChar(Chr(C))
  End Function

  Private Function IsPrintChar(ByVal C As Char) As Boolean

    TempLogStart()
    TempLogStop()
    Return IsPrintChar(C.ToString)
  End Function

  Private Function IsPrintChar(ByVal C As String) As Boolean

    TempLogStart()
    If System.Text.RegularExpressions.Regex.IsMatch(C, "[^\t\n\r\f\v]") Then
      Return True
    Else
      Return False
    End If
    TempLogStop()
  End Function

  Private Sub clsCustomAutoCompleteTextbox_GotFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.GotFocus

    TempLogStart()
    If Not Me.SuspendFocus AndAlso Me.myShowAutoCompleteOnFocus AndAlso Me.myForm.Visible = False Then
      Call Me.ShowAutoComplete()
    End If
    TempLogStop()
  End Sub

  Private Sub clsCustomAutoCompleteTextbox_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown

    TempLogStart()
    If Not SelectItem(e.KeyCode) Then
      If e.KeyCode = Keys.Up Then
        If myLbox.SelectedIndex > 0 Then
          Call MoveLBox(myLbox.SelectedIndex - 1)
        End If
      ElseIf e.KeyCode = Keys.Down Then
        Call MoveLBox(myLbox.SelectedIndex + 1)
      End If
    End If
    TempLogStop()
  End Sub

  Shadows Sub SelectAll()

  End Sub

  Private Sub MoveLBox(ByVal Index As Integer)

    TempLogStart()
    Try
      If Index > myLbox.Items.Count - 1 Then
        Index = myLbox.Items.Count - 1
      End If

      myLbox.SelectedIndex = Index
    Catch ex As Exception

    End Try
    TempLogStop()
  End Sub

  Private Sub clsCustomAutoCompleteTextbox_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Leave

    TempLogStart()
    Call DoHide(sender, e)
    TempLogStop()
  End Sub

  Private Sub clsCustomAutoCompleteTextbox_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.LostFocus

    TempLogStart()
    Call DoHide(sender, e)
    TempLogStop()
  End Sub

  Private Sub clsCustomAutoCompleteTextbox_Move(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Move

    TempLogStart()
    Call MoveDrop()
    TempLogStop()
  End Sub

  Private Sub clsCustomAutoCompleteTextbox_ParentChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.ParentChanged

    TempLogStart()
    myParentForm = GetParentForm(Me)
    TempLogStop()
  End Sub

  Private Sub HideTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles HideTimer.Tick

    TempLogStart()
    Call MoveDrop()
    Call DoHide(sender, e)

    Static Cnt As Integer = 0

    Cnt += 1

    If Cnt > 300 Then
      If Not AppHasFocus() Then
        Call DoHideAuto()
      End If

      Cnt = 0
    End If
    TempLogStop()
  End Sub

  Public Overrides Property SelectedText() As String
    Get
      Return MyBase.SelectedText
    End Get
    Set(ByVal value As String)
      MyBase.SelectedText = value
    End Set
  End Property

  Public Overrides Property SelectionLength() As Integer
    Get
      Return MyBase.SelectionLength
    End Get
    Set(ByVal value As Integer)
      MyBase.SelectionLength = value
    End Set
  End Property

  Private Sub myLbox_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles myLbox.Click


  End Sub

  Private Sub myLbox_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles myLbox.DoubleClick

  End Sub

  Private Function SelectItem(Optional ByVal Key As Keys = Keys.None, _
                         Optional ByVal SingleClick As Boolean = False, _
                         Optional ByVal DoubleClick As Boolean = False) As Boolean


    TempLogStart()
    Dim DoSelect As Boolean = True
    Dim Meth As SelectOptions
    Static LastItem As Integer = -1
    Select Case True
      Case Me.mySelectionMethods And SelectOptions.OnEnterPress AndAlso Key = Keys.Enter
        Meth = SelectOptions.OnEnterPress
      Case Me.mySelectionMethods And SelectOptions.OnRightArrow AndAlso Key = Keys.Right
        Meth = SelectOptions.OnRightArrow
      Case Me.mySelectionMethods And SelectOptions.OnTabPress AndAlso Key = Keys.Tab
        Meth = SelectOptions.OnTabPress
      Case Me.mySelectionMethods And SelectOptions.OnSingleClick AndAlso SingleClick
        Meth = SelectOptions.OnSingleClick
      Case Me.mySelectionMethods And SelectOptions.OnDoubleClick AndAlso DoubleClick
        Meth = SelectOptions.OnDoubleClick
        'Case Me.mySelectionMethods And SelectOptions.OnItemChange AndAlso (LastItem <> myLbox.SelectedIndex)
      Case Else
        DoSelect = False
    End Select
    LastItem = myLbox.SelectedIndex
    If DoSelect Then
      Call DoSelectItem(Meth)
    End If
    TempLogStop()

    Return DoSelect
  End Function

  Private Sub DoSelectItem(ByVal Method As SelectOptions)

    TempLogStart()
    If Me.myLbox.Items.Count > 0 AndAlso Me.myLbox.SelectedIndex > -1 Then
      Dim Value As String = Me.myLbox.SelectedItem.ToString

      Dim Orig As String = Me.Text

      Me.Text = Value

      If mySelectTextAfterItemSelect Then
        Try
          Me.SelectionStart = Orig.Length
          Me.SelectionLength = Value.Length - Orig.Length
        Catch ex As Exception
        End Try
      Else
        'Me.SelectionStart = Me.Text.Length
        'Me.SelectionLength = 0
      End If

      RaiseEvent ItemSelected(Me, New clsItemSelectedEventArgs(Me.myLbox.SelectedIndex, Method, Value))

      Call Me.DoHideAuto()
    End If
    TempLogStop()
  End Sub

  Public Class clsItemSelectedEventArgs
    Private myIndex As Integer
    Private myMethod As SelectOptions
    Private myItemText As String

    Public Property ItemText() As Boolean
      Get
        Return myItemText
      End Get
      Set(ByVal value As Boolean)
        myItemText = value
      End Set
    End Property

    Public Property Method() As SelectOptions
      Get
        Return myMethod
      End Get
      Set(ByVal value As SelectOptions)
        myMethod = value
      End Set
    End Property

    Public Property Index() As Integer
      Get
        Return myIndex
      End Get
      Set(ByVal value As Integer)
        myIndex = value
      End Set
    End Property

    Sub New()

    End Sub
    Sub New(ByVal Index As Integer, ByVal Method As SelectOptions, ByVal ItemText As String)
      myIndex = Index
      myMethod = Method
      myItemText = ItemText
    End Sub
  End Class

  Private Sub myLbox_GotFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles myLbox.GotFocus

    TempLogStart()
    Call DFocus()
    TempLogStop()
  End Sub

  Private Sub myLbox_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles myLbox.KeyDown

    TempLogStart()
    Call SelectItem(e.KeyCode)
    TempLogStop()
  End Sub

  Private Sub ProcessKeyEvents(ByVal e As KeyEventArgs)

    TempLogStart()
    Select Case e.KeyCode
      Case Is >= Keys.A And e.KeyCode <= Keys.Z
        MyBase.OnKeyUp(e)
      Case Keys.Back

      Case Keys.Enter

      Case Keys.Left, Keys.Right, Keys.Up, Keys.Down

      Case Is >= Keys.NumPad0 And e.KeyCode <= Keys.NumPad9, Is >= Keys.D0 And e.KeyCode <= Keys.D9

    End Select
    TempLogStop()
  End Sub

  Private Sub myLbox_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles myLbox.KeyPress
    If IsPrintChar(e.KeyChar) Then
      'Me.OnKeyPress(e)
      'Call MoveDrop()
    End If
    TempLogStop()
  End Sub

  Private Sub myLbox_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles myLbox.KeyUp
    If IsPrintChar(e.KeyValue) Then
      'Me.OnKeyUp(e)
      'Call MoveDrop()
    End If
    TempLogStop()
  End Sub

  Private Sub myLbox_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles myLbox.LostFocus

    TempLogStart()
    Call DoHide(sender, e)
    TempLogStop()
  End Sub

  Private Sub myLbox_MouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles myLbox.MouseClick

    TempLogStart()
    'If e.Button <> Windows.Forms.MouseButtons.None Then
    Call SelectItem(SingleClick:=True)

    'End If
    TempLogStop()
  End Sub

  Private Sub myLbox_MouseDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles myLbox.MouseDoubleClick

    TempLogStart()
    'If e.Button <> Windows.Forms.MouseButtons.None Then
    Call SelectItem(DoubleClick:=True)

    'End If
    TempLogStop()
  End Sub

  Private Sub myForm_Deactivate(ByVal sender As Object, ByVal e As System.EventArgs) Handles myForm.Deactivate

    TempLogStart()
    Call TryHideFormWindowsDeactivated()
    TempLogStop()
  End Sub

  Private Sub myParentForm_Deactivate(ByVal sender As Object, ByVal e As System.EventArgs) Handles myParentForm.Deactivate

    TempLogStart()
    Call TryHideFormWindowsDeactivated()
    TempLogStop()
  End Sub

  Private Sub FocusTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles FocusTimer.Tick

    TempLogStart()
    Me.Focus()
    TempLogStop()
  End Sub

  Public Sub New()

  End Sub

  Private Sub myLbox_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles myLbox.MouseDown
    myLbox_MouseClick(sender, e)
  End Sub
End Class

#End Region

【讨论】:

【参考方案2】:

这里是 C# 版本。它有很多选择。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1

    public partial class Form1 : Form
    
        public Form1()
        
            this.Load += new EventHandler(this.Form1_Load);

            InitializeComponent();
        

    private clsCustomAutoCompleteTextbox ClsCustomAutoCompleteTextbox1 = null;

    private List<string> MasterList = new List<string> ();

    public void Form1_Load(object sender, System.EventArgs e) 
        this.ClsCustomAutoCompleteTextbox1 = new clsCustomAutoCompleteTextbox();

        this.ClsCustomAutoCompleteTextbox1.AutoCompleteFormBorder = System.Windows.Forms.FormBorderStyle.None;

        this.ClsCustomAutoCompleteTextbox1.AutoCompleteList = null;
        this.ClsCustomAutoCompleteTextbox1.Location = new System.Drawing.Point(27, 57);
        this.ClsCustomAutoCompleteTextbox1.Name = "clsCustomAutoCompleteTextbox1";
        this.ClsCustomAutoCompleteTextbox1.OnEnterSelect = true;
        this.ClsCustomAutoCompleteTextbox1.SelectionMethods = clsCustomAutoCompleteTextbox.SelectOptions.OnEnterSingleClick;
        this.ClsCustomAutoCompleteTextbox1.SelectTextAfterItemSelect = true;
        this.ClsCustomAutoCompleteTextbox1.ShowAutoCompleteOnFocus = false;
        this.ClsCustomAutoCompleteTextbox1.Size = new System.Drawing.Size(232, 20);
        this.ClsCustomAutoCompleteTextbox1.TabIndex = 0;

        this.Controls.Add(this.ClsCustomAutoCompleteTextbox1);

        this.ClsCustomAutoCompleteTextbox1.BeforeDisplayingAutoComplete +=
            new EventHandler<clsCustomAutoCompleteTextbox.clsAutoCompleteEventArgs>(BeforeDisplayingAutoComplete);

        List<string> L;
        L = new List<string>();
        L.Add("123123 - Bob");
        L.Add("534543 - Sally");
        L.Add("123123 - George");
        L.Add("34213 - Happy");
        MasterList = L;
        this.ClsCustomAutoCompleteTextbox1.AutoCompleteList = L;
    

    private void BeforeDisplayingAutoComplete(object sender, clsCustomAutoCompleteTextbox.clsAutoCompleteEventArgs e) 
        string Name = this.ClsCustomAutoCompleteTextbox1.Text.ToLower();
        List<string> Display = new List<string> ();
        foreach (string Str in MasterList) 
            if ((Str.ToLower().IndexOf(Name) > -1)) 
                Display.Add(Str);
            
        
        e.AutoCompleteList = Display;
        e.SelectedIndex = 0;
    

public class clsCustomAutoCompleteTextbox : TextBox

    private bool First = true;

    private object sender;

    private clsAutoCompleteEventArgs e;

    public List<string> test = new List<string> ();

    public int Tabs = 0;

    private int mSelStart;

    private int mSelLength;

    private List<string> myAutoCompleteList = new List<string> ();

    private ListBox myLbox = new ListBox();

    private Form myForm = new Form();

    private Form myParentForm;

    private bool DontHide = false;

    private bool SuspendFocus = false;

    private clsAutoCompleteEventArgs Args;

    private Timer HideTimer = new Timer();

    private Timer FocusTimer = new Timer();

    private bool myShowAutoCompleteOnFocus;

    private System.Windows.Forms.FormBorderStyle myAutoCompleteFormBorder = FormBorderStyle.None;

    private bool myOnEnterSelect;

    private int LastItem;

    private SelectOptions mySelectionMethods = (SelectOptions.OnDoubleClick | SelectOptions.OnEnterPress);

    private bool mySelectTextAfterItemSelect = true;

    private List<string> value;

    private int Cnt = 0;

    public bool SelectTextAfterItemSelect
    
        get
        
            return mySelectTextAfterItemSelect;
        
        set
        
            mySelectTextAfterItemSelect = value;
        
    

    [System.ComponentModel.Browsable(false)]
    public SelectOptions SelectionMethods
    
        get
        
            return mySelectionMethods;
        
        set
        
            mySelectionMethods = value;
        
    

    public bool OnEnterSelect
    
        get
        
            return myOnEnterSelect;
        
        set
        
            myOnEnterSelect = value;
        
    

    public System.Windows.Forms.FormBorderStyle AutoCompleteFormBorder
    
        get
        
            return myAutoCompleteFormBorder;
        
        set
        
            myAutoCompleteFormBorder = value;
        
    

    public bool ShowAutoCompleteOnFocus
    
        get
        
            return myShowAutoCompleteOnFocus;
        
        set
        
            myShowAutoCompleteOnFocus = value;
        
    

    public ListBox Lbox
    
        get
        
            return myLbox;
        
    

    public List<string> AutoCompleteList  get; set; 

    public event EventHandler<clsAutoCompleteEventArgs> BeforeDisplayingAutoComplete;

    public event EventHandler<clsItemSelectedEventArgs> ItemSelected;

    public enum SelectOptions
    
        None = 0,

        OnEnterPress = 1,

        OnSingleClick = 2,

        OnDoubleClick = 4,

        OnTabPress = 8,

        OnRightArrow = 16,

        OnEnterSingleClick = 3,

        OnEnterSingleDoubleClicks = 7,

        OnEnterDoubleClick = 5,

        OnEnterTab = 9,
    

    public class clsAutoCompleteEventArgs : EventArgs
    

        private List<string> myAutoCompleteList;

        private bool myCancel;

        private int mySelectedIndex;

        private List<string> value;

        public int SelectedIndex
        
            get
            
                return mySelectedIndex;
            
            set
            
                mySelectedIndex = value;
            
        

        public bool Cancel
        
            get
            
                return myCancel;
            
            set
            
                myCancel = value;
            
        
        public List<string> AutoCompleteList  get; set; 
    

    public override string SelectedText
    
        get
        
            return base.SelectedText;
        
        set
        
            base.SelectedText = value;
        
    

    public override int SelectionLength
    
        get
        
            return base.SelectionLength;
        
        set
        
            base.SelectionLength = value;
        
    

    public clsCustomAutoCompleteTextbox()
    
        HideTimer.Tick += new EventHandler(HideTimer_Tick);
        FocusTimer.Tick += new EventHandler(FocusTimer_Tick);

        myLbox.Click += new EventHandler(myLbox_Click);
        myLbox.DoubleClick += new EventHandler(myLbox_DoubleClick);
        myLbox.GotFocus += new EventHandler(myLbox_GotFocus);
        myLbox.KeyDown += new KeyEventHandler(myLbox_KeyDown);

        myLbox.KeyUp += new KeyEventHandler(myLbox_KeyUp);
        myLbox.LostFocus += new EventHandler(myLbox_LostFocus);
        myLbox.MouseClick += new MouseEventHandler(myLbox_MouseClick);
        myLbox.MouseDoubleClick += new MouseEventHandler(myLbox_MouseDoubleClick);
        myLbox.MouseDown += new MouseEventHandler(myLbox_MouseDown);


        this.GotFocus += new EventHandler(clsCustomAutoCompleteTextbox_GotFocus);
        this.KeyDown += new KeyEventHandler(clsCustomAutoCompleteTextbox_KeyDown);
        this.Leave += new EventHandler(clsCustomAutoCompleteTextbox_Leave);
        this.LostFocus += new EventHandler(clsCustomAutoCompleteTextbox_LostFocus);
        this.Move += new EventHandler(clsCustomAutoCompleteTextbox_Move);
        this.ParentChanged += new EventHandler(clsCustomAutoCompleteTextbox_ParentChanged);


    

    override protected void OnKeyUp(System.Windows.Forms.KeyEventArgs e)
    


        base.OnKeyUp(e);
        ShowOnChar(new string(((char)(e.KeyValue)),1));
    

    private void ShowOnChar(string C)
    


        if (IsPrintChar(C))
        
            this.ShowAutoComplete();
        
    

    private bool IsPrintChar(int C)
    


        return IsPrintChar(((char)(C)));
    

    private bool IsPrintChar(byte C)
    


        return IsPrintChar(((char)(C)));
    

    private bool IsPrintChar(char C)
    


        return IsPrintChar(C.ToString());
    

    private bool IsPrintChar(string C)
    

        if (System.Text.RegularExpressions.Regex.IsMatch(C, "[^\\t\\n\\r\\f\\v]"))
        
            return true;
        
        else
        
            return false;
        

    

    private void clsCustomAutoCompleteTextbox_GotFocus(object sender, System.EventArgs e)
    

        if ((!this.SuspendFocus
                    && (this.myShowAutoCompleteOnFocus
                    && (this.myForm.Visible == false))))
        
            this.ShowAutoComplete();
        

    

    private void clsCustomAutoCompleteTextbox_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
    

        if (!SelectItem(e.KeyCode, false, false))
        
            if ((e.KeyCode == Keys.Up))
            
                if ((myLbox.SelectedIndex > 0))
                
                    MoveLBox((myLbox.SelectedIndex - 1));
                
            
            else if ((e.KeyCode == Keys.Down))
            
                MoveLBox((myLbox.SelectedIndex + 1));
            
        

    

    new void SelectAll()
    
    

    private void MoveLBox(int Index)
    

        try
        
            if ((Index
                        > (myLbox.Items.Count - 1)))
            
                Index = (myLbox.Items.Count - 1);
            
            myLbox.SelectedIndex = Index;
        
        catch
        
        

    

    private void clsCustomAutoCompleteTextbox_Leave(object sender, System.EventArgs e)
    

        DoHide(sender, e);

    

    private void clsCustomAutoCompleteTextbox_LostFocus(object sender, System.EventArgs e)
    

        DoHide(sender, e);

    

    private void clsCustomAutoCompleteTextbox_Move(object sender, System.EventArgs e)
    

        MoveDrop();

    

    private void clsCustomAutoCompleteTextbox_ParentChanged(object sender, System.EventArgs e)
    

        if (myParentForm != null) myParentForm.Deactivate -= new EventHandler(myParentForm_Deactivate);
        myParentForm = GetParentForm(this);
        if (myParentForm != null) myParentForm.Deactivate += new EventHandler(myParentForm_Deactivate);
    

    private void HideTimer_Tick(object sender, System.EventArgs e)
    

        MoveDrop();
        DoHide(sender, e);
        Cnt++;
        if ((Cnt > 300))
        
            if (!AppHasFocus(""))
            
                DoHideAuto();
            
            Cnt = 0;
        

    

    private void myLbox_Click(object sender, System.EventArgs e)
    
    

    private void myLbox_DoubleClick(object sender, System.EventArgs e)
    
    

    private bool SelectItem(Keys Key, bool SingleClick)
    
        return SelectItem(Key, SingleClick, false);
    

    private bool SelectItem(Keys Key)
    
        return SelectItem(Key, false, false);
    

    private bool SelectItem(Keys Key, bool SingleClick, bool DoubleClick)
    

        // Warning!!! Optional parameters not supported
        // Warning!!! Optional parameters not supported
        // Warning!!! Optional parameters not supported
        bool DoSelect = true;
        SelectOptions Meth = SelectOptions.None;
        LastItem = -1;

        if (((this.mySelectionMethods & SelectOptions.OnEnterPress) > 0) && (Key == Keys.Enter))
        
            Meth = SelectOptions.OnEnterPress;
        
        else if (((this.mySelectionMethods & SelectOptions.OnRightArrow) > 0) && Key == Keys.Right)
        
            Meth = SelectOptions.OnRightArrow;
        
        else if (((this.mySelectionMethods & SelectOptions.OnTabPress) > 0) && Key == Keys.Tab)
        
            Meth = SelectOptions.OnTabPress;
        
        else if (((this.mySelectionMethods & SelectOptions.OnSingleClick) > 0) && SingleClick)
        
            Meth = SelectOptions.OnEnterPress;
        
        else if (((this.mySelectionMethods & SelectOptions.OnDoubleClick) > 0) && DoubleClick)
        
            Meth = SelectOptions.OnEnterPress;
        
        else
        
            DoSelect = false;
        

        LastItem = myLbox.SelectedIndex;
        if (DoSelect)
        
            DoSelectItem(Meth);
        

        return DoSelect;
    
    public class clsItemSelectedEventArgs : EventArgs
    

        private int myIndex;

        private SelectOptions myMethod;

        private string myItemText;

        public clsItemSelectedEventArgs()
        
        

        public clsItemSelectedEventArgs(int Index, SelectOptions Method, string ItemText)
        
            myIndex = Index;
            myMethod = Method;
            myItemText = ItemText;
        

        public string ItemText
        
            get
            
                return myItemText;
            
            set
            
                myItemText = value;
            
        

        public SelectOptions Method
        
            get
            
                return myMethod;
            
            set
            
                myMethod = value;
            
        

        public int Index
        
            get
            
                return myIndex;
            
            set
            
                myIndex = value;
            
        
    

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        static extern IntPtr GetForegroundWindow();

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        static extern int GetWindowThreadProcessId(IntPtr hWnd, ref int ProcessID);

        private bool AppHasFocus(string ExeNameWithoutExtension)
        
            bool Out = false;
            // Warning!!! Optional parameters not supported
            int PID = 0;

            if ((ExeNameWithoutExtension == ""))
            
                ExeNameWithoutExtension = System.Diagnostics.Process.GetCurrentProcess().ProcessName;
            
            IntPtr activeHandle = GetForegroundWindow();
            GetWindowThreadProcessId(activeHandle, ref PID);
            if ((PID > 0))
            
                // For Each p As Process In Process.GetProcessesByName(ExeNameWithoutExtension)
                if ((PID == System.Diagnostics.Process.GetCurrentProcess().Id))
                
                    Out = true;
                
                //  Next
            

            return Out;
        

        private void SaveSelects()
        
            this.mSelStart = this.SelectionStart;
            this.mSelLength = this.SelectionLength;
        

        private void LoadSelects()
        
            this.SelectionStart = this.mSelStart;
            this.SelectionLength = this.mSelLength;
        

        private void ShowAutoComplete()
        

            Args = new clsAutoCompleteEventArgs();
            // With...
            Args.Cancel = false;
            Args.AutoCompleteList = this.myAutoCompleteList;
            if ((myLbox.SelectedIndex == -1))
            
                Args.SelectedIndex = 0;
            
            else
            
                Args.SelectedIndex = myLbox.SelectedIndex;
            

            if (BeforeDisplayingAutoComplete != null) BeforeDisplayingAutoComplete(this, Args);
            this.myAutoCompleteList = Args.AutoCompleteList;
            // If Me.myAutoCompleteList IsNot Nothing AndAlso Me.myAutoCompleteList.Count - 1 < Args.SelectedIndex Then
            //   Args.SelectedIndex = Me.myAutoCompleteList.Count - 1
            // End If
            if ((!Args.Cancel && (Args.AutoCompleteList != null) && Args.AutoCompleteList.Count > 0))
            
                DoShowAuto();
            
            else
            
                DoHideAuto();
            

        

        private void DoShowAuto()
        
            SaveSelects();

            myLbox.BeginUpdate();
            try
            
                myLbox.Items.Clear();
                myLbox.Items.AddRange(this.myAutoCompleteList.ToArray());
                this.MoveLBox(Args.SelectedIndex);
            
            catch (Exception ex)
            
            
            myLbox.EndUpdate();
            myParentForm = GetParentForm(this);
            if (myParentForm != null)
            
                myLbox.Name = ("mmmlbox" + DateTime.Now.Millisecond);
                if ((myForm.Visible == false))
                
                    myForm.Font = this.Font;
                    myLbox.Font = this.Font;
                    myLbox.Visible = true;
                    myForm.Visible = false;
                    myForm.ControlBox = false;
                    myForm.Text = "";
                    if (First)
                    
                        myForm.Width = this.Width;
                        myForm.Height = 200;
                    
                    First = false;
                    if (!myForm.Controls.Contains(myLbox))
                    
                        myForm.Controls.Add(myLbox);
                    
                    myForm.FormBorderStyle = FormBorderStyle.None;
                    myForm.ShowInTaskbar = false;
                    // With...
                    myLbox.Dock = DockStyle.Fill;
                    myLbox.SelectionMode = SelectionMode.One;
                    // Frm.Controls.Add(myLbox)
                    DontHide = true;
                    SuspendFocus = true;
                    myForm.TopMost = true;
                    myForm.FormBorderStyle = this.myAutoCompleteFormBorder;
                    myForm.BringToFront();
                    MoveDrop();
                    myForm.Visible = true;
                    myForm.Show();
                    MoveDrop();
                    HideTimer.Interval = 10;
                    this.Focus();
                    SuspendFocus = false;
                    HideTimer.Enabled = true;
                    DontHide = false;
                    LoadSelects();
                
            

        

        void MoveDrop()
        

            Point Pnt = new Point(this.Left, (this.Top
                            + (this.Height + 2)));
            Point ScreenPnt = this.PointToScreen(new Point(-2, this.Height));
            // Dim FrmPnt As Point = Frm.PointToClient(ScreenPnt)
            if (myForm != null)
            
                myForm.Location = ScreenPnt;
                // myForm.BringToFront()
                // myForm.Focus()
                // myLbox.Focus()
                // Me.Focus()
            

        

        void DoHide(object sender, EventArgs e)
        

            HideAuto();

        

        private void DFocus(int Delay)
        

            // Warning!!! Optional parameters not supported
            FocusTimer.Interval = Delay;
            FocusTimer.Start();

        

        private void DFocus()
        
            DFocus(10);
        

        private void DoHideAuto()
        

            myForm.Hide();
            HideTimer.Enabled = false;
            FocusTimer.Enabled = false;

        

        private void HideAuto()
        

            if ((myForm.Visible && HasLostFocus()))
            
                DoHideAuto();
            

        

        private bool HasLostFocus()
        

            bool Out = false;
            if (this.myForm == null || myForm.ActiveControl != this.myLbox)
            
                Out = true;
            
            if (this.myParentForm == null || this.myParentForm.ActiveControl != this)
            
                Out = true;
            

            return Out;
        

        private Form GetParentForm(Control InCon)
        

            Control TopCon = FindTopParent(InCon);
            Form Out = null;
            if ((TopCon is Form))
            
                Out = ((Form)(TopCon));
            

            return Out;
        

        private Control FindTopParent(Control InCon)
        

            Control Out;
            if ((InCon.Parent == null))
            
                Out = InCon;
            
            else
            
                Out = FindTopParent(InCon.Parent);
            

            return Out;
        

        private void DoSelectItem(SelectOptions Method)
        

            if (((this.myLbox.Items.Count > 0)
                        && (this.myLbox.SelectedIndex > -1)))
            
                string Value = this.myLbox.SelectedItem.ToString();
                string Orig = this.Text;
                this.Text = Value;
                if (mySelectTextAfterItemSelect)
                
                    try
                    
                        this.SelectionStart = Orig.Length;
                        this.SelectionLength = (Value.Length - Orig.Length);
                    
                    catch (Exception ex)
                    
                    
                
                else
                
                    // Me.SelectionStart = Me.Text.Length
                    // Me.SelectionLength = 0
                

                clsItemSelectedEventArgs a;
                a = new clsItemSelectedEventArgs();
                a.Index = this.myLbox.SelectedIndex;
                a.Method = Method;
                a.ItemText = Value;

                if (ItemSelected != null) ItemSelected(this, a);

                //ItemSelected(this, new clsItemSelectedEventArgs(this.myLbox.SelectedIndex, Method, Value));
                this.DoHideAuto();
            

        

        private void myLbox_GotFocus(object sender, System.EventArgs e)
        

            DFocus();

        

        private void myLbox_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
        

            SelectItem(e.KeyCode);

        

        private void ProcessKeyEvents(KeyEventArgs e)
        


                if ((e.KeyCode >= Keys.A) && (e.KeyCode <= Keys.Z))
                    base.OnKeyUp(e);


                //Keys.Back;
                //Keys.Enter;
                //Keys.Left;
                //Keys.Right;
                //Keys.Up;
                //Keys.Down;
                //(Keys.NumPad0 & (e.KeyCode <= Keys.NumPad9));
                //(Keys.D0 & (e.KeyCode <= Keys.D9));


        

        private void myLbox_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
        
            if (IsPrintChar(e.KeyChar))
            
                // Me.OnKeyPress(e)
                // Call MoveDrop()
            

        

        private void myLbox_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
        
            if (IsPrintChar(e.KeyValue))
            
                // Me.OnKeyUp(e)
                // Call MoveDrop()
            

        

        private void myLbox_LostFocus(object sender, System.EventArgs e)
        

            DoHide(sender, e);

        

        private void myLbox_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e)
        

            // If e.Button <> Windows.Forms.MouseButtons.None Then
            SelectItem(Keys.None,true);
            // End If

        

        private void myLbox_MouseDoubleClick(object sender, System.Windows.Forms.MouseEventArgs e)
        

            // If e.Button <> Windows.Forms.MouseButtons.None Then
            SelectItem(Keys.None, false, true);
            // End If

        

        private void myForm_Deactivate(object sender, System.EventArgs e)
        


        

        private void myParentForm_Deactivate(object sender, System.EventArgs e)
        


        

        private void FocusTimer_Tick(object sender, System.EventArgs e)
        

            this.Focus();

        

        private void myLbox_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
        
            myLbox_MouseClick(sender, e);
        
    

【讨论】:

你知道这段代码是否会以我期望的方式处理向上和向下箭头键击,通过向上或向下移动选择?这似乎对我不起作用,但也许我“破坏”了您原始帖子中的某些内容。【参考方案3】:

我不知道这是否是一个选项,但是,例如,您可以这样做:

    private void Main_Load(object sender, EventArgs e)
    
        DataTable dt = new DataTable();
        dt.Columns.Add("colCustomers", typeof(string));
        dt.Rows.Add(new object[]  "1 John" );
        dt.Rows.Add(new object[]  "2 Kate" );
        dt.Rows.Add(new object[]  "3 Jill" );

        comboBox1.DataSource = dt.DefaultView; //allows us to filter the results
        comboBox1.DisplayMember = "Col1";
    

    private void comboBox1_TextChanged(object sender, EventArgs e)
    
        if (comboBox1.SelectedIndex == -1)
        
            dt.DefaultView.RowFilter = "colCustomers LIKE '%" + comboBox1.Text + "%'";
        
    

当然,您也可以使用搜索和定位来代替过滤,但由于可能有很多客户,我认为只留下匹配的客户会更好。当用户删除他的条目时,它应该再次显示所有人。对您的需求进行一些调整可能会使代码受益,但您必须进行试验才能提出您想要的。

【讨论】:

【参考方案4】:

这是我的数据绑定组合框代码(在我的例子中是 Access DB)。 您将组合框和列表框添加到窗体。然后,使用下面的代码作为参考:

    如何更新数据绑定组合框 添加事件处理程序,以便 当您在组合框中使用箭头键时,它会通过 结果在列表框中。 使用计时器将数据库查询限制为 仅当自上次按键后已过去 500 毫秒时。

我确信有更好的方法可以做到这一点。这段代码“对我有用”。欢迎批评。

    #region AutoComplete Organization Box

    System.Windows.Forms.Timer mOrganizationTextChangedTimer = null;
    void organizationComboBox_TextChanged(object sender, System.EventArgs e)
    
        if (mOrganizationTextChangedTimer == null)
        
            mOrganizationTextChangedTimer = new System.Windows.Forms.Timer();
            mOrganizationTextChangedTimer.Interval = 500;
            mOrganizationTextChangedTimer.Tick += new EventHandler(mOrganizationTextChangedTimer_Tick);
        
        mOrganizationTextChangedTimer.Enabled = true;
    

    void mOrganizationTextChangedTimer_Tick(object sender, EventArgs e)
    
        mOrganizationTextChangedTimer.Enabled = false;
        UpdateOrganizationNameAutocompleteResults( comboBoxOrganizationName.Text );    
    

    void UpdateOrganizationNameAutocompleteResults( string pSearchString ) 
    
        listBoxOrganizationAutocompleteResults.Items.Clear();
        if (comboBoxOrganizationName.Text.Length == 0)
        
            HideOrganizationNameAutocompleteResults();
            return;
        

        // Added a custom query to search our database and return results that match.  The query uses UCase on the columns.
        allertDataSet3.OrganizationDataTable orgs = organizationTableAdapter.GetDataByOrganizationNameSearchUCase("%" + pSearchString.ToUpper() + "%", "%" + pSearchString.ToUpper() + "%");

        foreach( allertDataSet3.OrganizationRow r in orgs ) 
            string longName = r.OrganizationName;
            listBoxOrganizationAutocompleteResults.Items.Add(longName);
            listBoxOrganizationAutocompleteResults.Visible = true;
            listBoxOrganizationAutocompleteResults.BringToFront();
        

        // This Code block is needed because once you select an organization, the combobox text changes which forces another search.
        // There is only one result from that search so this hides it when the search result equals the already selected combobox.
        try 
            System.Data.DataRowView drv = (System.Data.DataRowView) comboBoxOrganizationName.SelectedItem;
            allertDataSet3.OrganizationRow orgRow = (allertDataSet3.OrganizationRow)drv.Row;

            if ( listBoxOrganizationAutocompleteResults.Items.Count == 1 && 
                ((string)listBoxOrganizationAutocompleteResults.Items[0]).Equals(orgRow.OrganizationName) )
            
                HideOrganizationNameAutocompleteResults();
            
        
        catch 
            // do nothing
                    
    

    void organizationComboBoxAutocompleteResults_SelectedIndexChanged(object sender, System.EventArgs e)
    
        foreach (System.Data.DataRowView drv in comboBoxOrganizationName.Items)
        
            Alertus.Allert.allertDataSet3.OrganizationRow orgRow = (Alertus.Allert.allertDataSet3.OrganizationRow)drv.Row;
            if (orgRow.OrganizationName.Equals((string) listBoxOrganizationAutocompleteResults.SelectedItem))
            
                // Prevents it from searching again.
                comboBoxOrganizationName.TextChanged -= organizationComboBox_TextChanged;
                comboBoxOrganizationName.SelectedItem = drv;
                comboBoxOrganizationName.TextChanged += organizationComboBox_TextChanged;
                HideOrganizationNameAutocompleteResults();
                return;
            
        

        // This is basically an error... it should have found it.
        HideOrganizationNameAutocompleteResults();
    

    void listBoxOrganizationAutocompleteResults_LostFocus(object sender, System.EventArgs e)
    
        HideOrganizationNameAutocompleteResults();
    

    void HideOrganizationNameAutocompleteResults()
    
        listBoxOrganizationAutocompleteResults.Visible = false;
    

    private void listBoxOrganizationAutocompleteResults_MouseClick(object sender, MouseEventArgs e)
    
        // When mouse is clicked we assume user made a seletion.
        organizationComboBoxAutocompleteResults_SelectedIndexChanged(null, null);
    

    /// <summary>
    /// Redirects the Arrow keys to update the selected index in the autocomplete list box.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void comboBoxOrganizationName_KeyDown(object sender, KeyEventArgs e)
    
        if (e.KeyCode == Keys.Up)
        
            if (listBoxOrganizationAutocompleteResults.Items.Count >= 1 && listBoxOrganizationAutocompleteResults.Visible)
            
                if (listBoxOrganizationAutocompleteResults.SelectedIndex < 0)
                
                    listBoxOrganizationAutocompleteResults.SelectedIndex = 0;
                
                else
                
                    listBoxOrganizationAutocompleteResults.SelectedIndex = (listBoxOrganizationAutocompleteResults.SelectedIndex - 1) % listBoxOrganizationAutocompleteResults.Items.Count;
                
            
            e.Handled = true;
        
        else if (e.KeyCode == Keys.Down)
        
            if (listBoxOrganizationAutocompleteResults.Items.Count >= 1 && listBoxOrganizationAutocompleteResults.Visible )
            
                if (listBoxOrganizationAutocompleteResults.SelectedIndex < 0)
                

                    listBoxOrganizationAutocompleteResults.SelectedIndex = 0;
                
                else
                
                    listBoxOrganizationAutocompleteResults.SelectedIndex = (listBoxOrganizationAutocompleteResults.SelectedIndex + 1) % listBoxOrganizationAutocompleteResults.Items.Count;
                
            

            e.Handled = true;
        
        else if (e.KeyCode == Keys.Enter)
        
            e.Handled = true;
            if (listBoxOrganizationAutocompleteResults.Visible)
            
                organizationComboBoxAutocompleteResults_SelectedIndexChanged(null, null);
            
        
    

    #endregion

设计师代码:

    // 
    // comboBoxOrganizationName
    // 
    this.comboBoxOrganizationName.FormattingEnabled = true;
    this.comboBoxOrganizationName.Location = new System.Drawing.Point(19, 53);
    this.comboBoxOrganizationName.Name = "comboBoxOrganizationName";
    this.comboBoxOrganizationName.Size = new System.Drawing.Size(363, 21);
    this.comboBoxOrganizationName.TabIndex = 1;
    this.comboBoxOrganizationName.SelectedIndexChanged += new System.EventHandler(this.comboBoxOrganizationName_SelectedIndexChanged);
    this.comboBoxOrganizationName.Enter += new System.EventHandler(this.comboBoxOrganizationName_Enter);
    this.comboBoxOrganizationName.KeyDown += new System.Windows.Forms.KeyEventHandler(this.comboBoxOrganizationName_KeyDown);
    this.comboBoxOrganizationName.TextChanged += new System.EventHandler(this.organizationComboBox_TextChanged);**

  // 
            // listBoxOrganizationAutocompleteResults
            // 
            this.listBoxOrganizationAutocompleteResults.FormattingEnabled = true;
            this.listBoxOrganizationAutocompleteResults.Location = new System.Drawing.Point(20, 76);
            this.listBoxOrganizationAutocompleteResults.Name = "listBox1";
            this.listBoxOrganizationAutocompleteResults.Size = new System.Drawing.Size(359, 95);
            this.listBoxOrganizationAutocompleteResults.TabIndex = 19;
            this.listBoxOrganizationAutocompleteResults.Visible = false;
            this.listBoxOrganizationAutocompleteResults.MouseClick += new System.Windows.Forms.MouseEventHandler(this.listBoxOrganizationAutocompleteResults_MouseClick);
            this.listBoxOrganizationAutocompleteResults.Leave += new System.EventHandler(this.listBoxOrganizationAutocompleteResults_LostFocus);

【讨论】:

【参考方案5】:

你可以使用这个类:

public partial class MyComboBox : ComboBox
    

        private IList<object> collectionList = null;

        public MyComboBox()
        
            InitializeComponent();
            collectionList = new List<object>();

        
        public MyComboBox(IContainer container)
            : this()
        
            container.Add(this);
        

        protected override void OnTextUpdate(EventArgs e)
        
            try
            
                //base.OnTextUpdate(e);
                IList<object> values = collectionList
                 .Where(x => x.ToString().ToLower().Contains(Text.ToLower()))
                 .ToList();

                //Don't use Items.Clear() because the selectionstart resets to Zero
                while (Items.Count > 0)
                
                    Items.RemoveAt(0);
                

                this.Items.AddRange(values.ToArray());

                this.DroppedDown = true;
                Cursor.Current = Cursors.Default;
            
            catch(Exception ex)
            
                SelectedIndex = -1;
                //MessageBox.Show(ex.Message);
            

        

        protected override void OnTextChanged(EventArgs e)
        
            //base.OnTextChanged(e);
            if (this.Text == string.Empty)
            
                Items.Clear();
                this.Items.AddRange(collectionList.ToArray());
            
        

        protected override void OnBindingContextChanged(EventArgs e)
        
            base.OnBindingContextChanged(e);
            collectionList = this.Items.OfType<object>().ToList();
        
    

【讨论】:

以上是关于子字符串上的组合框自动完成的主要内容,如果未能解决你的问题,请参考以下文章

熊猫数据框列上的子字符串

组合框更改后子表单不重新查询

从字符串生成子字符串的组合

如何在访问中禁用组合框上的自动完成功能?

MS Access 2010 多列组合框自动完成

在MS访问中的子表单上显示查询结果