2011-01-27 104 views
2

我有一个窗体有一个组合框控件。我已选择DropDown样式属性为DropDown。我也将DropDown Width设置为250.我已经将auto complete模式设置为建议,并将自动完成源设置为listitems。当我点击下拉菜单时,它工作得很好。但是当我输入somethin时,自动完成模式会激活一个宽度较小的下拉菜单。组合框下拉宽度建议

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

我已经问了几个月后,但没有得到正确的答案。现在客户希望它不好:( ??

回答

5
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 
} 

这是什么我做了,它实际上工作得很好。很好找到答案atlast :)

0

一种糟糕的设计决定,要做到这一点。为什么不将它设置为静态大尺寸开始?您可以随时使用其中一个事件来获取文本宽度,然后用它来设置组合框的宽度。可能在的OnPaint?更简单的方法可能是创建一个从组合框继承自己的组合框类,然后覆盖的方法自己做。

+1

是有没有办法,我可以访问表明下降的宽度然后将其设置为与下拉宽度相同 – reggie 2011-01-27 20:01:26

+0

查看Visual Studio中的对象浏览器,然后查看system.windows.forms.ComboBox并查看您公开访问的内容。否则,从组合框继承,然后查看您有权访问的内容。如果必须使用反射器。 – 2011-01-27 20:29:46

3

这个答案是reggie的一个补充swer。

如果你希望用户能够调整自动完成下拉菜单,然后添加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); 
      } 
     }