2012-08-12 12 views
9

Jaki byłby najprostszy i najprostszy sposób, aby pokazać skoncentrowany/wybrany element listy w stylu Office XP?Jak zrobić pole listy z motywem Office XP, jak prostokąt wyboru?

Zobacz ten przykładowy obraz, aby pokazać pomysł bardziej przejrzyste:

enter image description here

myślę, że trzeba ustawić styl ListBox albo lbOwnerDrawFixed lub lbOwnerDrawVariable a następnie zmodyfikować zdarzenie OnDrawItem?

To gdzie siedzę, nie jestem pewien, jaki kod napisać tam, do tej pory próbowałem:

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; 

powinny I wiadomo, że nie będzie działać, mam wszelkiego rodzaju Funky rzeczy dzieje:

enter image description here

co robię źle, a co ważniejsze, co muszę zmienić, aby to działało?

Dzięki.

+0

Nie sądzę, istnieje taki rodzaj skrzynki lista motywem wyboru. W polu listy znajdują się części '' LBCP_ITEM'] (http://msdn.microsoft.com/en-us/library/windows/desktop/bb(v = vs.85) .aspx), ale wyglądają dokładnie tak samo, jak pole listy wykreślone przez właściciela - nudne. Może więc możesz pożyczyć części tematyczne do wyboru, np. z widoku drzewa opisanego przez Andrew w ['this post'] (http://stackoverflow.com/a/10936108/960757). Będziesz tylko musiał zmodyfikować ten kod dla stanów pola listy. – TLama

+0

Właśnie o to się martwiłem, w przeciwieństwie do widoku drzewa i listview, które malują sekcje tematyczne, których nie robi Listbox. –

+3

W przypadku intensywnego kodowania kolorów nie należy zapominać, że użytkownik może mieć zupełnie inny schemat kolorów. –

Odpowiedz

13

Zapomniałeś pomalować elementy dla różnych stanów. Musisz określić, w jakim stanie jest aktualnie przedmiot i zgodnie z nim go narysować.

To, co masz na swoim zdjęciu, możesz zdobyć w ten sposób. Jednak to nie wygląda dobrze, jeśli masz włączone wielokrotny i wybrać więcej niż jeden przedmiot:

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; 

a wynik z ItemHeight zestaw do 16:

enter image description here

Bonus - ciągłą selekcję :

Oto podstępne rozwiązanie wdrażające ciągły wybór. Zasada polega na tym, aby narysować element jak poprzednio, a następnie przeciągnąć górną i dolną linię przedmiotu liniami koloru w zależności od stanu wyboru poprzedniego i następnego elementu. Poza tym, musi być wyrenderowany również poza bieżącym elementem, ponieważ wybór elementu nie wywołuje w sposób naturalny przedmiotów sąsiednich, które mają zostać przemalowane. W związku z tym linie poziome są malowane o jeden piksel powyżej i jeden piksel poniżej bieżących granic elementu (kolory tych linii zależą również od względnych stanów wyboru).

Dość dziwne jest tutaj użycie obiektów przedmiotów do przechowywania wybranego stanu każdego elementu. Zrobiłem to, ponieważ podczas korzystania z opcji rozwijanej przeciągnij &, właściwość Selected nie zwraca stanu rzeczywistego, dopóki nie zwolnisz przycisku myszy. Na szczęście zdarzenie OnDrawItem wywołuje oczywiście stan rzeczywisty, więc jako obejście używam przechowywania tych stanów ze zdarzenia OnDrawItem.

Ważne:

Zauważ, że używam obiektów element do przechowywania stanu faktycznego wyboru, więc należy być ostrożnym, a gdy używasz przedmiotów pozycja dla czegoś innego, przechowywać ten rzeczywisty stwierdza np do tablicy 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; 

A wynik:

enter image description here

+2

Tak, zapomniałem sprawdzić, czy stan został wybrany, ale nawet wtedy byłem daleko od działającej odpowiedzi - przynajmniej teraz widzę, jakie zmiany powinienem był zrobić. Również zapomniałem o rozważaniu wielu selekcji, efekt nie jest zły :) W każdym razie dobra odpowiedź jak zawsze dzięki TLama;) –

+2

Nie ma za co! O multiselect jest to bardziej skomplikowane, gdy masz obramowanie wokół przedmiotu. W takim przypadku musisz dodatkowo sprawdzić, czy poprzednia pozycja (jeśli istnieje) i następna pozycja (jeśli taka istnieje) zostały wybrane, czy nie. Jeśli tak, to musisz * ukryć * górną lub dolną linię graniczną aktualnie renderowanego przedmiotu, aby uzyskać ciągły wybór ["like this"] (http://i.imgur.com/hG7zG.png). – TLama

+1

Doskonałe rozwiązanie, starałem się pracować z multiseleksem, jak pokazano na drugim zrzucie ekranu, ale nie byłem nawet w porównaniu do zaktualizowanej wersji. Zaakceptowałbym twoją odpowiedź dwa razy, gdyby to było możliwe :) –

2

Należy przyjrzeć się wartości zmiennej State przekazanej do funkcji. Informuje o tym, czy element został wybrany, czy nie, a następnie można odpowiednio ustawić pędzel i pióro.

Powiązane problemy