.net - obtener - selectedvalue c#
No se puede establecer DropDownHeight de ComboBox (4)
No puedo obtener el DropDownHeight del ComboBox configurado correctamente para mostrar todos los elementos.
Estoy usando un control que hereda de ComboBox. He reemplazado los métodos OnDrawItem y OnMeasureItem para crear varias columnas y ajuste de texto dentro de una columna si es necesario. Todo esto funciona bien
El problema ocurre cuando intento establecer DropDownHeight. Establecí DropDownHeight en un valor arbitrariamente grande, un poco más grande que la lista de elementos. El control ComboBox parece truncar automáticamente cualquier valor para DropDownHeight que sea más grande que el tamaño de todos los elementos mostrados en la lista. (Suponiendo que tiene la propiedad MaxDropDownItems configurada más alta que el número de elementos, lo cual hago.) Normalmente, este comportamiento funciona perfectamente, como se muestra a continuación: texto alternativo http://www.freeimagehosting.net/uploads/dd09404697.png
No, esos no son mis datos reales en el cuadro desplegable.
El problema ocurre cuando tengo una entrada en el menú desplegable que se debe ajustar para mostrar el texto completo. Esta entrada se muestra bien, pero sin embargo, el ComboBox está calculando DropDownHeight, ignora el hecho de que una de las entradas es dos veces más alta de lo normal, por lo que tiene que desplazarse hacia abajo una línea para llegar a la última entrada en el menú desplegable. texto alternativo http://www.freeimagehosting.net/uploads/d0ef715f83.png
Este es el código que estoy usando para determinar si un artículo necesita envoltura de texto y para establecer el alto de cada artículo:
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
No puedo determinar cómo forzar la propiedad DropDownHeight para que sea exactamente el valor que deseo, o cómo informar al control ComboBox que uno (o más) de los elementos en la lista son más altos que lo normal.
Intenté anular la propiedad Shadow the DropDownHeight, pero esto no pareció tener ningún impacto.
EDITAR:
¿Cambiaría este problema a WPF? (¿Hay suficiente personalización en los controles estándar de WPF para que no necesite escribir un control personalizado para un combobox de 3 columnas y altura variable?)
Intenta llamar a MyBase.OnMeasureItem
al final del método
Editar: Intenté reproducir tu problema, pero todo funciona bien:
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();
}
}
Si recuerdo correctamente, debe establecer la propiedad DrawMode en OwnerDrawVariable para habilitar el dibujo de las alturas de los artículos personalizados. Si haces esto, también tendrás que manejar el evento DrawItem . Echa un vistazo a la ayuda de la propiedad en MSDN .
Aquí está la versión c # de la respuesta aceptada.
[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);
}
Estoy tratando de resolver exactamente este mismo problema en este momento para una aplicación que estoy migrando de VB6 a VB.NET. El control combo dibujado por el propietario que tengo en VB6 establece el alto del menú desplegable a través de una llamada API SetWindowPos en respuesta al mensaje WM_CTLCOLORLISTBOX en el control combinado, que nos da acceso al HWnd para la lista desplegable del combo controlar. El siguiente código fue agregado a mi clase que hereda de ComboBox y parece ser el truco, pero aún necesita ser probado. No estoy seguro de que sea la manera más elegante de hacer esto tampoco. Obviamente, deberá cambiar la línea que establece la nueva variable HeHight, pero esto debería darle la idea general.
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