2009-08-07 15 views
5

Ayarlanamadı Tüm öğeleri görüntülemek için ComboBox kümesinin DropDownHeight ayarını düzgün alamıyorum.DropDownHeight of ComboBox

ComboBox'tan devralınan bir denetim kullanıyorum. Gerekirse, bir sütun içinde birden çok sütun ve metin kaydırma oluşturmak için OnDrawItem ve OnMeasureItem yöntemlerini geçersiz kıldım. Her şey yolunda.

DropDownHeight'ı ayarlamaya çalıştığımda sorun oluşuyor. DropDownHeight'ı rasgele büyük bir değere, öğeler listesinden daha iyi bir değere ayarlıyorum. ComboBox denetimi, listedeki tüm görüntülenen öğelerin boyutundan daha büyük olan DropDownHeight için herhangi bir değeri otomatik olarak kırpıyor gibi görünür. (Eğer MaxDropDownItems mülkiyet yapmam öğelerin sayısı, daha yüksek ayarlanmış olduğunu varsayarsak.) Normalde bu davranış mükemmel çalışıyor, aşağıda gösterildiği gibi: alt text http://www.freeimagehosting.net/uploads/dd09404697.png

Hayır, o değil açılır kutuda benim gerçek veri .

Sorun, tam metnin görüntülenmesi için sarılması gereken açılır menüde bir giriş olduğunda oluşur. Bu giriş iyi görünüyor, ancak ComboBox DropDownHeight hesaplıyor, bu girdilerden biri normalin iki katı uzun olduğu gerçeği yok sayar, bu nedenle açılan satırdaki son girişe ulaşmak için bir satır aşağı kaydırmanız gerekir.

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 

ben tam olarak olmak DropDownHeight özelliğini zorlamak nasıl tespit edemez: alt text http://www.freeimagehosting.net/uploads/d0ef715f83.png

Bu Ben bir öğe metin kaydırma ihtiyacı olup olmadığını belirlemek için kullanıyorum ve her öğenin yüksekliğini ayarlamak için koddur İstediğim değer veya ComboBox denetiminin, listedeki öğelerin bir (veya daha fazla) öğesinin normalden daha uzun olduğunu bilmesine nasıl izin verileceği.

Geçersiz kılmayı denedim Shadow the DropDownHeight özelliği, ancak bunun hiçbir etkisi yok gibi görünüyordu.

DÜZENLEME:
bu problemi ortadan WPF geçiş misiniz? (Standart WPF yeterli customizability Ben 3 sütunlu, değişken yükseklik combobox için özel bir denetim yazmak gerekmez, böylece orada kontrol mı?)

cevap

9

VB6'dan VB.NET'e geçiş yaptığım bir uygulama için şu anda bu aynı sorunu kendim çözmeye çalışıyorum. VB6'da sahip olduğum sahipli combo denetimi, açılan kontrolün üzerindeki açılır menüden HWnd'e erişebilmemizi sağlayan, combo kontrolündeki WM_CTLCOLORLISTBOX mesajına cevaben bir SetWindowPos API çağrısı aracılığıyla açılır pencerenin yüksekliğini ayarlar. kontrol. Aşağıdaki kod ComboBox'tan miras alan sınıfıma eklendi ve hile yapmak gibi görünüyor, ancak yine de teste ihtiyacı var. Bunu yapmanın en zarif yolu olduğundan emin değilim. Açıkçası, newHeight değişkenini belirleyen satırı değiştirmeniz gerekir, ancak bu size genel bir fikir vermelidir.

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, cevabınız için teşekkürler. Windows İletilerini ele geçirmekten kaçınmaya çalışıyorum, ama çözümünüz başka bir yol bulamıyorsa benim için çalışabilir gibi görünüyor. Her bir öğe için ItemHeight öğesini combobox'ta saklayan bazı ek işlevler eklemem gerekir, ancak bu çok zor olmamalıdır. – Stewbob

+0

Teşekkürler JDHnz. Bunu uygulamamda başarılı bir şekilde uygulayabildim. – Stewbob

0

deneyin yönteminin

sonunda MyBase.OnMeasureItem aramaya
+1

Kodumdan önce MyBase.OnMeasureItem'i çağırmayı denedim ve sonra da tamamen iptal ediyorum. Hepsi etkili değil. Yine de cevap verdiğin için teşekkürler. Ben bunun üzerine bir tümböceği ile sarılacağını düşünmeye başlamıştım. – Stewbob

0

Düzenleme: Ben sadece sorunu yeniden çalıştı ama her şey iyi çalışır:

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

Ben etkinleştirmek OwnerDrawVariablemülk DrawMode ayarlamak zorunda Yanlış hatırlamıyorsam özel ürün yükseklikleri çizme. Bunu yaparsanız, DrawItem olayını da işlemeniz gerekir. MSDN adresinden yardım alabilirsiniz.

+0

DrawMode'u kurucuda OwnerDrawVariable olarak ayarlıyorum ve DrawItem olayını geçersiz kıldım. Çok sütunlu ekranı, alternatif arka plan renklerini ve açılır menüdeki metin sarmalarını bu şekilde alırım. – Stewbob

2

Kabul edilen yanıtın C# sürümüdür.

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