可以修改Autocomplete高度和宽度的TextBox.(ComboBox也试用)

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

 

posted @ 2015-05-08 18:17  Youjun  阅读(644)  评论(0编辑  收藏  举报