2012-08-12 23 views
9

¿Cuál sería la forma más simple y limpia de mostrar un elemento de cuadro de lista centrado/seleccionado con un estilo de Office XP?¿Cómo hacer un cuadro de lista con el tema de Office XP como rectángulo de selección?

Ver esta imagen de ejemplo para mostrar la idea más clara:

enter image description here

creo que es necesario para establecer el estilo de cuadro de lista a cualquiera lbOwnerDrawFixed o lbOwnerDrawVariable y modifique el evento OnDrawItem?

Aquí es donde estoy atascado, no estoy muy seguro de qué código escribir allí, hasta el momento he intentado:

procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer; 
    Rect: TRect; State: TOwnerDrawState); 
begin 
    with (Control as TListBox).Canvas do 
    begin 
    if odSelected in State then 
    begin 
     Brush.Color := $00FCDDC0; 
     Pen.Color := $00FF9933; 
     FillRect(Rect); 
    end; 

    TextOut(Rect.Left, Rect.Top, TListBox(Control).Items[Index]); 
    end; 
end; 

que debería de saber que no iba a funcionar, consigo todo tipo de funky cosas que suceden:

enter image description here

¿qué estoy haciendo mal, lo más importante, ¿qué tengo que cambiar para que funcione?

Gracias.

+0

No creo que existe este tipo de una selección temática de cuadro de lista. Hay ['LBCP_ITEM'] (http://msdn.microsoft.com/en-us/library/windows/desktop/bb(v = vs.85) .aspx) partes para el cuadro de lista, pero se ven exactamente como el caja de lista dibujada sin dueño - aburrida. Por lo tanto, tal vez pueda pedir prestados los elementos de la selección temática, p. desde la vista de árbol como describió Andrew en ['this post'] (http://stackoverflow.com/a/10936108/960757). Solo necesitará modificar ese código para los estados del cuadro de lista. – TLama

+0

Esto es lo que me preocupaba, a diferencia de Treeview y Listview que pintan selecciones temáticas que Listbox no hace. –

+3

Cuando se trata de colores difíciles de codificar, no olvide que un usuario puede tener un esquema de color completamente diferente. –

Respuesta

13

Olvidó pintar los artículos para diferentes estados. Debe determinar en qué estado se encuentra actualmente el artículo y según lo que lo dibuje.

Lo que tienes en la foto puedes obtenerlo de esta manera. Sin embargo, esto no se ve bien si ha habilitado selección múltiple y seleccionar más de un elemento:

procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer; 
    Rect: TRect; State: TOwnerDrawState); 
var 
    Offset: Integer; 
begin 
    with (Control as TListBox) do 
    begin 
    Canvas.Font.Color := Font.Color; 
    if (odSelected in State) then 
    begin 
     Canvas.Pen.Color := $00FF9932; 
     Canvas.Brush.Color := $00FDDDC0; 
    end 
    else 
    begin 
     Canvas.Pen.Color := Color; 
     Canvas.Brush.Color := Color; 
    end; 
    Canvas.Rectangle(Rect); 
    Canvas.Brush.Style := bsClear; 
    Offset := (Rect.Bottom - Rect.Top - Canvas.TextHeight(Items[Index])) div 2; 
    Canvas.TextOut(Rect.Left + Offset + 2, Rect.Top + Offset, Items[Index]); 
    end; 
end; 

Y el resultado con ItemHeight conjunto a 16:

enter image description here

Bonus - selección continua :

Aquí hay una solución compleja que implementa una selección continua. El principio es dibujar el elemento como antes, pero luego sobreescriba las líneas superior e inferior del borde del artículo con las líneas de un color dependiendo del estado de selección del artículo anterior y siguiente. Excepto que, también se debe representar fuera del elemento actual, ya que la selección del elemento no invoca naturalmente los elementos vecinos para volver a pintar. Por lo tanto, las líneas horizontales se pintan un píxel por encima y un píxel por debajo de los límites del elemento actual (los colores de estas líneas también dependen de los estados de selección relativa).

Muy extraño aquí es el uso de objetos de elemento para almacenar el estado seleccionado de cada elemento. Lo hice porque, al utilizar una opción de arrastrar & soltar elemento, la propiedad Selected no devuelve el estado real hasta que suelta el botón del mouse. Afortunadamente, el evento OnDrawItem, por supuesto, se activa con el estado real, por lo que como solución alternativa he utilizado el almacenamiento de estos estados desde el evento OnDrawItem.

Importante:

Aviso, que estoy usando los objetos de elemento para almacenar el estado actual selección, así que tenga cuidado, y cuando se está utilizando objetos de elemento para otra cosa, almacenar este real estados, por ejemplo en una matriz de Boolean.

procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer; 
    Rect: TRect; State: TOwnerDrawState); 
const 
    SelBackColor = $00FDDDC0; 
    SelBorderColor = $00FF9932; 
var 
    Offset: Integer; 
    ItemSelected: Boolean; 
begin 
    with (Control as TListBox) do 
    begin 
    Items.Objects[Index] := TObject((odSelected in State));  

    if (odSelected in State) then 
    begin 
     Canvas.Pen.Color := SelBorderColor; 
     Canvas.Brush.Color := SelBackColor; 
     Canvas.Rectangle(Rect); 
    end 
    else 
    begin 
     Canvas.Pen.Color := Color; 
     Canvas.Brush.Color := Color; 
     Canvas.Rectangle(Rect); 
    end; 

    if MultiSelect then 
    begin 
     if (Index > 0) then 
     begin 
     ItemSelected := Boolean(ListBox1.Items.Objects[Index - 1]); 
     if ItemSelected then 
     begin 
      if (odSelected in State) then 
      begin 
      Canvas.Pen.Color := SelBackColor; 
      Canvas.MoveTo(Rect.Left + 1, Rect.Top); 
      Canvas.LineTo(Rect.Right - 1, Rect.Top); 
      end 
      else 
      Canvas.Pen.Color := SelBorderColor; 
     end 
     else 
      Canvas.Pen.Color := Color; 
     Canvas.MoveTo(Rect.Left + 1, Rect.Top - 1); 
     Canvas.LineTo(Rect.Right - 1, Rect.Top - 1); 
     end; 

     if (Index < Items.Count - 1) then 
     begin 
     ItemSelected := Boolean(ListBox1.Items.Objects[Index + 1]); 
     if ItemSelected then 
     begin 
      if (odSelected in State) then 
      begin 
      Canvas.Pen.Color := SelBackColor; 
      Canvas.MoveTo(Rect.Left + 1, Rect.Bottom - 1); 
      Canvas.LineTo(Rect.Right - 1, Rect.Bottom - 1); 
      end 
      else 
      Canvas.Pen.Color := SelBorderColor; 
     end 
     else 
      Canvas.Pen.Color := Color; 
     Canvas.MoveTo(Rect.Left + 1, Rect.Bottom); 
     Canvas.LineTo(Rect.Right - 1, Rect.Bottom); 
     end; 
    end; 

    Offset := (Rect.Bottom - Rect.Top - Canvas.TextHeight(Items[Index])) div 2; 
    Canvas.Brush.Style := bsClear; 
    Canvas.Font.Color := Font.Color; 
    Canvas.TextOut(Rect.Left + Offset + 2, Rect.Top + Offset, Items[Index]); 
    end; 
end; 

Y el resultado:

enter image description here

+2

Sí, me olvidé de comprobar si se seleccionó el estado, pero incluso entonces estaba lejos de una respuesta operativa, al menos ahora puedo ver qué cambios debería haber hecho. También me olvidé de considerar selecciones múltiples, el efecto no es malo :) De todos modos una buena respuesta como siempre gracias TLama;) –

+2

¡De nada! Acerca del multiselect es más complicado cuando tiene un borde alrededor del artículo. En este caso, también debe comprobar si el elemento anterior (si lo hay) y el siguiente (si lo hay) están seleccionados o no. Si es así, entonces necesita * ocultar * la línea de borde superior o inferior del elemento actualmente procesado para obtener la selección continua ['like this'] (http://i.imgur.com/hG7zG.png). – TLama

+1

Excelente solución, me probé a mí mismo para trabajar el multiselect como se muestra en su segunda captura de pantalla pero no estaba ni remotamente comparado con su versión actualizada. Aceptaría su respuesta dos veces si fuera posible :) –

2

Debe mirar el valor de la variable de estado que se pasa a la función. Esto le indica si el artículo está seleccionado o no y luego puede configurar el pincel y el bolígrafo de manera adecuada.

Cuestiones relacionadas