2009-08-07 24 views
5

Tôi không thể tải DropDownHeight của ComboBox đúng cách để hiển thị tất cả các mục.Không thể đặt DropDownHeight của ComboBox

Tôi đang sử dụng điều khiển kế thừa từ ComboBox. Tôi đã ghi đè lên các phương thức OnDrawItem và OnMeasureItem để tạo ra nhiều cột và gói văn bản trong một cột nếu nó được yêu cầu. Điều này tất cả hoạt động tốt.

Sự cố xảy ra khi tôi cố gắng đặt DropDownHeight. Tôi đặt DropDownHeight ở một giá trị lớn tùy ý, lớn hơn một chút so với danh sách các mục. Điều khiển ComboBox xuất hiện để tự động cắt bớt bất kỳ giá trị nào cho DropDownHeight lớn hơn kích thước của tất cả các mục được hiển thị trong danh sách. (Giả sử bạn có thuộc tính MaxDropDownItems được đặt cao hơn số mục, mà tôi làm.) Thông thường, hành vi này hoạt động hoàn hảo, như được hiển thị bên dưới: alt text http://www.freeimagehosting.net/uploads/dd09404697.png

Không, đó không phải là dữ liệu thực của tôi trong hộp thả xuống .

Sự cố xảy ra khi tôi có mục nhập trong trình đơn thả xuống cần bọc để hiển thị toàn văn. Mục này hiển thị tốt, nhưng tuy nhiên ComboBox đang tính toán DropDownHeight, nó bỏ qua thực tế rằng một trong các mục cao gấp hai lần bình thường, vì vậy bạn phải cuộn xuống một dòng để đến mục nhập cuối cùng trong trình đơn thả xuống. alt text http://www.freeimagehosting.net/uploads/d0ef715f83.png

Đây là mã mà tôi đang sử dụng để xác định xem một mục cần gói văn bản và thiết lập chiều cao của từng hạng mục:

Protected Overrides Sub OnMeasureItem(ByVal e As System.Windows.Forms.MeasureItemEventArgs) 
    MyBase.OnMeasureItem(e) 
    //Determine the proper height of the current row in the dropdown based on 
    //the length of the OptionDescription string. 
    Dim tmpStr As String = FilterItemOnProperty(Items(e.Index), "OptionDescription") 
    Dim lng As Single = e.Graphics.MeasureString(tmpStr, Me.Font).Width 
    //Use the length of the item and the width of the column to calculate if wrapping is needed. 
    Dim HeightMultiplier As Integer = Math.Floor(lng/_ColumnWidths(1)) + 1 
    e.ItemHeight = e.ItemHeight * HeightMultiplier 

End Sub 

tôi không thể xác định làm thế nào để buộc các tài sản DropDownHeight để được chính xác giá trị mà tôi muốn, hoặc làm thế nào để cho các ComboBox kiểm soát biết rằng một (hoặc nhiều hơn) của các mục trong danh sách cao hơn bình thường.

Tôi đã cố gắng Ghi đè Đổ bóng thuộc tính DropDownHeight, nhưng điều này dường như không có tác động.

EDIT:
sẽ chuyển sang WPF làm cho vấn đề này biến mất? (Có đủ tùy chỉnh trong các điều khiển WPF tiêu chuẩn để tôi không cần phải viết một điều khiển tùy chỉnh cho một combobox 3 cột, biến chiều cao?)

Trả lời

9

Tôi đang cố gắng giải quyết vấn đề tương tự chính xác này ngay tại thời điểm này cho một ứng dụng mà tôi đang di chuyển từ VB6 sang VB.NET. Điều khiển combo do chủ sở hữu vẽ trong VB6 thiết lập chiều cao của trình đơn thả xuống thông qua lệnh gọi API SetWindowPos để phản hồi thông báo WM_CTLCOLORLISTBOX trên điều khiển kết hợp, điều này cho phép chúng ta truy cập vào HWnd cho danh sách thả xuống của combo điều khiển. Các mã sau đây đã được thêm vào lớp học của tôi mà kế thừa từ ComboBox và dường như làm các trick, nhưng vẫn cần thử nghiệm. Tôi không chắc đó là cách thanh lịch nhất để làm điều này. Rõ ràng bạn sẽ cần phải thay đổi dòng đặt biến newHeight, nhưng điều này sẽ cung cấp cho bạn ý tưởng chung.

Private Structure RECT 
    Public Left As Integer  'x position Of upper-left corner 
    Public Top As Integer   'y position Of upper-left corner 
    Public Right As Integer  'x position Of lower-right corner 
    Public Bottom As Integer  'y position Of lower-right corner 
End Structure 

Private Declare Function GetWindowRect Lib "user32" _ 
     (ByVal hwnd As Integer, ByRef lpRect As RECT) As Integer 

Private Declare Sub SetWindowPos Lib "user32" _ 
     (ByVal hwnd As Integer, ByVal hWndInsertAfter As Integer, _ 
     ByVal X As Integer, ByVal Y As Integer, _ 
     ByVal cx As Integer, ByVal cy As Integer, _ 
     ByVal wFlags As Integer) 

Private Const SWP_NOZORDER As Integer = &H4 
Private Const SWP_NOACTIVATE As Integer = &H10 
Private Const SWP_FRAMECHANGED As Integer = &H20 
Private Const SWP_NOOWNERZORDER As Integer = &H200 

Private _hwndDropDown As Integer = 0 

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message) 
    Const WM_CTLCOLORLISTBOX As Integer = &H134 

    If m.Msg = WM_CTLCOLORLISTBOX Then 
     If _hwndDropDown = 0 Then 
      _hwndDropDown = m.LParam.ToInt32 

      Dim r As RECT 
      GetWindowRect(m.LParam.ToInt32, r) 

      'height of four items plus 2 pixels for the border in my test 
      Dim newHeight As Integer = 4 * MyBase.ItemHeight + 2 

      SetWindowPos(m.LParam.ToInt32, 0, _ 
         r.Left, _ 
         r.Top, _ 
         MyBase.DropDownWidth, _ 
         newHeight, _ 
         SWP_FRAMECHANGED Or _ 
           SWP_NOACTIVATE Or _ 
           SWP_NOZORDER Or _ 
           SWP_NOOWNERZORDER) 
     End If 
    End If 

    MyBase.WndProc(m) 
End Sub 

Protected Overrides Sub OnDropDownClosed(ByVal e As System.EventArgs) 
    _hwndDropDown = 0 
    MyBase.OnDropDownClosed(e) 
End Sub 
+0

JDHnz, cảm ơn phản hồi của bạn. Tôi đang cố gắng để tránh cướp tin nhắn Windows, nhưng giải pháp của bạn trông giống như nó có thể làm việc cho tôi nếu tôi không thể tìm thấy một cách khác. Tôi sẽ cần phải thêm một số chức năng bổ sung để kiểm soát của tôi lưu trữ ItemHeight cho mỗi mục trong combobox, nhưng điều đó không nên quá khó. – Stewbob

+0

Cảm ơn JDHnz. Tôi đã có thể triển khai thành công ứng dụng này vào ứng dụng của mình. – Stewbob

0

Cố gắng gọi MyBase.OnMeasureItem ở cuối phương thức

+1

Tôi đã thử gọi MyBase.OnMeasureItem trước mã của tôi, sau đó và thậm chí là thoát hoàn toàn. Tất cả đều không có hiệu lực. Cảm ơn bạn đã trả lời. Tôi đã bắt đầu nghĩ rằng tôi sẽ gió lên với một tumbleweed trên này. – Stewbob

0

Edit: Tôi chỉ cố gắng để tái tạo vấn đề của bạn, nhưng tất cả mọi thứ hoạt động tốt:

class MyCustomComboBox : ComboBox 
{ 
    public MyCustomComboBox() 
    { 
     DrawMode = DrawMode.OwnerDrawVariable; 

     DropDownHeight = 255; 
     DropDownWidth = 300; 
     MaxDropDownItems = 20; 
    } 

    protected override void OnMeasureItem(MeasureItemEventArgs e) 
    { 
     base.OnMeasureItem(e); 

     if (e.Index % 2 == 0) 
      e.ItemHeight = ItemHeight * 3; 
     else 
      e.ItemHeight = ItemHeight * 2; 
    } 

    protected override void OnDrawItem(DrawItemEventArgs e) 
    { 
     base.OnDrawItem(e); 

     // Draw the background of the item. 
     e.DrawBackground(); 

     Rectangle rectangle = new Rectangle(2, e.Bounds.Top + 2, 
       e.Bounds.Height, e.Bounds.Height - 4); 
     e.Graphics.FillRectangle(new SolidBrush(Color.Gray), rectangle); 

     Font myFont = new Font(FontFamily.GenericSansSerif, 30, FontStyle.Bold); 
     e.Graphics.DrawString(this.Items[e.Index] as string, myFont, Brushes.Black, 
      new RectangleF(e.Bounds.X + rectangle.Width, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height)); 

     // Draw the focus rectangle if the mouse hovers over an item. 
     e.DrawFocusRectangle(); 
    } 
} 

Nếu tôi nhớ chính xác bạn cần phải thiết lập các DrawMode tài sản để OwnerDrawVariable để cho phép vẽ chiều cao mục tùy chỉnh. Nếu bạn làm điều này, bạn cũng sẽ phải xử lý sự kiện DrawItem. Hãy xem trợ giúp về tài sản ở MSDN.

+0

Tôi đặt DrawMode thành OwnerDrawVariable trong hàm tạo và ghi đè lên sự kiện DrawItem. Đó là cách tôi nhận được màn hình đa cột, màu nền xen kẽ và gói văn bản trong menu thả xuống. – Stewbob

2

Đây là phiên bản C# của câu trả lời được chấp nhận.

[DllImport("user32.dll")] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect); 

    [DllImport("user32.dll", SetLastError = true)] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags); 

    [StructLayout(LayoutKind.Sequential)] 
    public struct RECT 
    { 
     public int Left;  // x position of upper-left corner 
     public int Top;   // y position of upper-left corner 
     public int Right;  // x position of lower-right corner 
     public int Bottom;  // y position of lower-right corner 
    } 

    public const int SWP_NOZORDER = 0x0004; 
    public const int SWP_NOACTIVATE = 0x0010; 
    public const int SWP_FRAMECHANGED = 0x0020; 
    public const int SWP_NOOWNERZORDER = 0x0200; 

    public const int WM_CTLCOLORLISTBOX = 0x0134; 

    private int _hwndDropDown = 0; 

    protected override void WndProc(ref Message m) 
    { 
     if (m.Msg == WM_CTLCOLORLISTBOX) 
     { 
      if (_hwndDropDown == 0) 
      { 
       _hwndDropDown = m.LParam.ToInt32(); 

       RECT r; 
       GetWindowRect((IntPtr)_hwndDropDown, out r); 

       //height of four items plus 2 pixels for the border in my test 
       int newHeight; 

       if (Items.Count <= MaxDropDownItems) 
       { 
        newHeight = Items.Count * ItemHeight + 2; 
       } 
       else 
       { 
        newHeight = MaxDropDownItems * ItemHeight + 2; 
       } 

       SetWindowPos((IntPtr)_hwndDropDown, IntPtr.Zero, 
        r.Left, 
          r.Top, 
          DropDownWidth, 
          newHeight, 
          SWP_FRAMECHANGED | 
           SWP_NOACTIVATE | 
           SWP_NOZORDER | 
           SWP_NOOWNERZORDER); 
      } 
     } 

     base.WndProc(ref m); 
    } 

    protected override void OnDropDownClosed(EventArgs e) 
    { 
     _hwndDropDown = 0; 
     base.OnDropDownClosed(e); 
    } 
Các vấn đề liên quan