【问题标题】:Combo box drop down width on suggest建议上的组合框下拉宽度
【发布时间】:2011-01-27 18:42:40
【问题描述】:

我有一个包含组合框控件的表单。我已将下拉样式属性选择为 DropDown。我还将下拉宽度设置为 250。我将自动完成模式设置为建议,将自动完成源设置为列表项。当我单击下拉菜单时,它工作得非常好。但是当我输入一些东西时,自动完成模式会激活一个宽度较小的下拉菜单。

任何帮助表示赞赏。我想知道如何通过代码增加自动完成下拉菜单的宽度,以便正确查看列表项。我正在使用 C#。

几个月前我曾问过这个问题,但没有得到正确的答案。现在客户想要它不好:( ??

【问题讨论】:

    标签: c# .net winforms visual-studio-2008 combobox


    【解决方案1】:
    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
    }
    

    这就是我所做的,实际上效果很好。很高兴最后找到答案:)

    【讨论】:

    • 这是我对这段代码的体验。只要您不尝试调整建议下拉菜单的大小,这就会很好地工作。您将无法这样做。
    • @omatrot 我同意 resiz 不起作用。但它解决了我的问题,我不得不在没有所需的调整大小功能的情况下扩大下拉范围。
    • 查看我关于修复调整大小问题的答案。我试图编辑接受的答案,但被拒绝了。
    • 哇,不错的代码,不过工作量很大。谢谢雷吉!
    【解决方案2】:

    此答案是对 reggie 答案的补充。

    如果您希望用户能够调整自动完成下拉菜单的大小,请在 WndProc 方法中添加以下代码:

        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);
                }
            }
    

    【讨论】:

      【解决方案3】:

      这样做是一个糟糕的设计决定。为什么不将其设置为静态大尺寸开始呢?您始终可以使用其中一个事件来获取文本宽度,然后使用它来设置组合框宽度。可能是onPaint?更简单的方法可能是创建您自己的从组合框继承的组合框类,然后自己重​​写方法来执行此操作。

      【讨论】:

      • 我无法访问出现的建议下拉菜单的宽度并将其设置为与下拉菜单宽度相同
      • 查看 Visual Studio 中的对象浏览器并查看 system.windows.forms.ComboBox 并查看您可以公开访问的内容。否则,从组合框继承,然后查看您可以访问的内容。必要时使用反射器。
      猜你喜欢
      • 1970-01-01
      • 2017-06-08
      • 1970-01-01
      • 1970-01-01
      • 2014-08-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多