4
votes

I have a form which has a Combo Box Control. I have selected the drop down style property to DropDown. I have also set the DropDown Width to 250. I have set the auto complete mode to suggest and the auto complete source to listitems. it works absolutely fine when i click on the drop down. but when i type in somethin, the auto complete mode activates a drop down which has a small width.

any help appreciate. i wanna know how to increase the width of the auto complete drop down via code so that the list items are viewed properly. I am using C#.

I had asked this a couple of months back but didn't get a proper answer. now the customer wants it bad :( ??

3

3 Answers

7
votes
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

/// <summary>  
/// Represents an ComboBox with additional properties for setting the   
/// size of the AutoComplete Drop-Down window.  
/// </summary>  
public class ComboBoxEx : ComboBox
{
private int acDropDownHeight = 106;
private int acDropDownWidth = 170;


//<EditorBrowsable(EditorBrowsableState.Always), _  

[Browsable(true), Description("The width, in pixels, of the auto complete drop down box"), DefaultValue(170)]
public int AutoCompleteDropDownWidth
{
    get { return acDropDownWidth; }

    set { acDropDownWidth = value; }
}


//<EditorBrowsable(EditorBrowsableState.Always), _  

[Browsable(true), Description("The height, in pixels, of the auto complete drop down box"), DefaultValue(106)]
public int AutoCompleteDropDownHeight
{
    get { return acDropDownHeight; }

    set { acDropDownHeight = value; }
}


protected override void OnHandleCreated(EventArgs e)
{
    base.OnHandleCreated(e);

    ACWindow.RegisterOwner(this);
}

#region Nested type: ACWindow

/// <summary>  
/// Provides an encapsulation of an Auto complete drop down window   
/// handle and window proc.  
/// </summary>  
private class ACWindow : NativeWindow
{
    private static readonly Dictionary<IntPtr, ACWindow> ACWindows;

    #region "Win API Declarations"

    private const UInt32 WM_WINDOWPOSCHANGED = 0x47;

    private const UInt32 WM_NCDESTROY = 0x82;


    private const UInt32 SWP_NOSIZE = 0x1;

    private const UInt32 SWP_NOMOVE = 0x2;

    private const UInt32 SWP_NOZORDER = 0x4;

    private const UInt32 SWP_NOREDRAW = 0x8;

    private const UInt32 SWP_NOACTIVATE = 0x10;


    private const UInt32 GA_ROOT = 2;
    private static readonly List<ComboBoxEx> owners;


    [DllImport("user32.dll")]
    private static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);


    [DllImport("user32.dll")]
    private static extern IntPtr GetAncestor(IntPtr hWnd, UInt32 gaFlags);


    [DllImport("kernel32.dll")]
    private static extern int GetCurrentThreadId();


    [DllImport("user32.dll")]
    private static extern void GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);


    [DllImport("user32.dll")]
    private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy,
                                            uint uFlags);


    [DllImport("user32.dll")]
    private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);

    #region Nested type: EnumThreadDelegate

    private delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);

    #endregion

    #region Nested type: RECT

    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        public readonly int Left;

        public readonly int Top;

        public readonly int Right;

        public readonly int Bottom;


        public Point Location
        {
            get { return new Point(Left, Top); }
        }
    }

    #endregion

    #endregion

    private ComboBoxEx owner;

    static ACWindow()
    {
        ACWindows = new Dictionary<IntPtr, ACWindow>();

        owners = new List<ComboBoxEx>();
    }


    /// <summary>  
    /// Creates a new ACWindow instance from a specific window handle.  
    /// </summary>  
    private ACWindow(IntPtr handle)
    {
        AssignHandle(handle);
    }


    /// <summary>  
    /// Registers a ComboBoxEx for adjusting the Complete Dropdown window size.  
    /// </summary>  
    public static void RegisterOwner(ComboBoxEx owner)
    {
        if ((owners.Contains(owner)))
        {
            return;
        }

        owners.Add(owner);

        EnumThreadWindows(GetCurrentThreadId(), EnumThreadWindowCallback, IntPtr.Zero);
    }


    /// <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 static bool EnumThreadWindowCallback(IntPtr hWnd, IntPtr lParam)
    {
        if ((GetClassName(hWnd) == "Auto-Suggest Dropdown"))
        {
            IntPtr handle = GetAncestor(hWnd, GA_ROOT);


            if ((!ACWindows.ContainsKey(handle)))
            {
                ACWindows.Add(handle, new ACWindow(handle));
            }
        }

        return true;
    }


    /// <summary>  
    /// Gets the class name for a specific window handle.  
    /// </summary>  
    private static string GetClassName(IntPtr hRef)
    {
        var lpClassName = new StringBuilder(256);

        GetClassName(hRef, lpClassName, 256);

        return lpClassName.ToString();
    }


    /// <summary>  
    /// Overrides the NativeWindow's WndProc to handle when the window  
    /// attributes changes.  
    /// </summary>  
    protected override void WndProc(ref Message m)
    {
        if ((m.Msg == WM_WINDOWPOSCHANGED))
        {
            // 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 == null))
            {
                Rectangle ownerRect = default(Rectangle);

                var acRect = new RECT();

                foreach (ComboBoxEx cbo in owners)
                {
                    GetWindowRect(Handle, ref acRect);

                    ownerRect = cbo.RectangleToScreen(cbo.ClientRectangle);

                    if ((ownerRect.Contains(acRect.Location)))
                    {
                        owner = cbo;

                        break; // TODO: might not be correct. Was : Exit For
                    }
                }

                owners.Remove(owner);
            }


            if (((owner != null)))
            {
                SetWindowPos(Handle, IntPtr.Zero, -5, 0, owner.AutoCompleteDropDownWidth,
                             owner.AutoCompleteDropDownHeight, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
            }
        }


        if ((m.Msg == WM_NCDESTROY))
        {
            ACWindows.Remove(Handle);
        }


        base.WndProc(ref m);
    }
}

#endregion
}

This is what I did and it actually works really well. Good to find an answer atlast :)

3
votes

This answer is an addition to reggie's answer.

If you want the user to be able to resize the auto-complete dropdown, then add the following code inside the WndProc method:

    private const int WM_SIZING = 0x214;

        if (m.Msg == WM_SIZING) {
            if (owner != null) {
                RECT rr = (RECT) Marshal.PtrToStructure(m.LParam, typeof(RECT));
                owner.acDropDownWidth = (rr.Right - rr.Left);
                owner.acDropDownHeight = (rr.Bottom - rr.Top);
            }
        }
1
votes

kind of a bad design decision to do that. Why not set it to a static large size to start with? You can always use one of the events to get the text width and then use that to set the combobox width. Possibly the onPaint? easier way might be to create your own combobox class that inherits from combo box and then override the methods to do this yourself.