Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports System.Text
Public Class TextBoxEx
Inherits TextBox
Private acDropDownHeight As Integer = 106
Private acDropDownWidth As Integer = 170
'<EditorBrowsable(EditorBrowsableState.Always), _
<Browsable(True), Description("The width, in pixels, of the auto complete drop down box"), DefaultValue(170)> _
Public Property AutoCompleteDropDownWidth() As Integer
Get
Return acDropDownWidth
End Get
Set(value As Integer)
acDropDownWidth = value
End Set
End Property
'<EditorBrowsable(EditorBrowsableState.Always), _
<Browsable(True), Description("The height, in pixels, of the auto complete drop down box"), DefaultValue(106)> _
Public Property AutoCompleteDropDownHeight() As Integer
Get
Return acDropDownHeight
End Get
Set(value As Integer)
acDropDownHeight = value
End Set
End Property
Protected Overrides Sub OnHandleCreated(e As EventArgs)
MyBase.OnHandleCreated(e)
ACWindow.RegisterOwner(Me)
End Sub
''' <summary>
''' Provides an encapsulation of an Auto complete drop down window
''' handle and window proc.
''' </summary>
Private Class ACWindow
Inherits NativeWindow
Private Shared ReadOnly ACWindows As Dictionary(Of IntPtr, ACWindow)
Private Const WM_WINDOWPOSCHANGED As UInt32 = &H47
Private Const WM_NCDESTROY As UInt32 = &H82
Private Const SWP_NOSIZE As UInt32 = &H1
Private Const SWP_NOMOVE As UInt32 = &H2
Private Const SWP_NOZORDER As UInt32 = &H4
Private Const SWP_NOREDRAW As UInt32 = &H8
Private Const SWP_NOACTIVATE As UInt32 = &H10
Private Const WM_SIZING As UInt32 = &H214
Private Const GA_ROOT As UInt32 = 2
Private Shared ReadOnly owners As List(Of TextBoxEx)
<DllImport("user32.dll")> _
Private Shared Function EnumThreadWindows(dwThreadId As Integer, lpfn As EnumThreadDelegate, lParam As IntPtr) As Boolean
End Function
<DllImport("user32.dll")> _
Private Shared Function GetAncestor(hWnd As IntPtr, gaFlags As UInt32) As IntPtr
End Function
<DllImport("kernel32.dll")> _
Private Shared Function GetCurrentThreadId() As Integer
End Function
<DllImport("user32.dll")> _
Private Shared Sub GetClassName(hWnd As IntPtr, lpClassName As StringBuilder, nMaxCount As Integer)
End Sub
<DllImport("user32.dll")> _
Private Shared Function SetWindowPos(hWnd As IntPtr, hWndInsertAfter As IntPtr, X As Integer, Y As Integer, cx As Integer, cy As Integer, _
uFlags As UInteger) As Boolean
End Function
<DllImport("user32.dll")> _
Private Shared Function GetWindowRect(hWnd As IntPtr, ByRef lpRect As RECT) As Boolean
End Function
Private Delegate Function EnumThreadDelegate(hWnd As IntPtr, lParam As IntPtr) As Boolean
<StructLayout(LayoutKind.Sequential)> _
Private Structure RECT
Public ReadOnly Left As Integer
Public ReadOnly Top As Integer
Public ReadOnly Right As Integer
Public ReadOnly Bottom As Integer
Public ReadOnly Property Location() As Point
Get
Return New Point(Left, Top)
End Get
End Property
End Structure
Private owner As TextBoxEx
Shared Sub New()
ACWindows = New Dictionary(Of IntPtr, ACWindow)()
owners = New List(Of TextBoxEx)()
End Sub
''' <summary>
''' Creates a new ACWindow instance from a specific window handle.
''' </summary>
Private Sub New(handle As IntPtr)
AssignHandle(handle)
End Sub
''' <summary>
''' Registers a ComboBoxEx for adjusting the Complete Dropdown window size.
''' </summary>
Public Shared Sub RegisterOwner(owner As TextBoxEx)
If (owners.Contains(owner)) Then
Return
End If
owners.Add(owner)
EnumThreadWindows(GetCurrentThreadId(), AddressOf EnumThreadWindowCallback, IntPtr.Zero)
End Sub
''' <summary>
''' This callback will receive the handle for each window that is
''' associated with the current thread. Here we match the drop down window name
''' to the drop down window name and assign the top window to the collection
''' of auto complete windows.
''' </summary>
Private Shared Function EnumThreadWindowCallback(hWnd As IntPtr, lParam As IntPtr) As Boolean
If (GetClassName(hWnd) = "Auto-Suggest Dropdown") Then
Dim handle As IntPtr = GetAncestor(hWnd, GA_ROOT)
If (Not ACWindows.ContainsKey(handle)) Then
ACWindows.Add(handle, New ACWindow(handle))
End If
End If
Return True
End Function
''' <summary>
''' Gets the class name for a specific window handle.
''' </summary>
Private Shared Function GetClassName(hRef As IntPtr) As String
Dim lpClassName = New StringBuilder(256)
GetClassName(hRef, lpClassName, 256)
Return lpClassName.ToString()
End Function
''' <summary>
''' Overrides the NativeWindow's WndProc to handle when the window
''' attributes changes.
''' </summary>
Protected Overrides Sub WndProc(ByRef m As Message)
'If m.Msg = WM_SIZING Then
' If owner IsNot Nothing Then
' Dim rr As RECT = DirectCast(Marshal.PtrToStructure(m.LParam, GetType(RECT)), RECT)
' owner.acDropDownWidth = (rr.Right - rr.Left)
' owner.acDropDownHeight = (rr.Bottom - rr.Top)
' End If
'End If
Console.WriteLine(m.Msg.ToString())
If (m.Msg = WM_WINDOWPOSCHANGED) Then
' If the owner has not been set we need to find the ComboBoxEx that
' is associated with this dropdown window. We do it by checking if
' the upper-left location of the drop-down window is within the
' ComboxEx client rectangle.
If (owner Is Nothing) Then
Dim ownerRect As Rectangle = Nothing
Dim acRect = New RECT()
For Each cbo As TextBoxEx In owners
GetWindowRect(Handle, acRect)
ownerRect = cbo.RectangleToScreen(cbo.ClientRectangle)
'If (ownerRect.Contains(acRect.Location)) Then
owner = cbo
' TODO: might not be correct. Was : Exit For
'Exit For
' End If
Next
owners.Remove(owner)
End If
If ((owner IsNot Nothing)) Then
SetWindowPos(Handle, IntPtr.Zero, -5, 0, owner.AutoCompleteDropDownWidth, owner.AutoCompleteDropDownHeight, _
SWP_NOMOVE Or SWP_NOZORDER Or SWP_NOACTIVATE)
End If
End If
If (m.Msg = WM_NCDESTROY) Then
ACWindows.Remove(Handle)
End If
MyBase.WndProc(m)
End Sub
End Class
End Class