Версия для печати


ListBox с Fixed рядами и произвольными иконками для каждого ряда.
http://www.delphikingdom.com/asp/viewitem.asp?catalogID=1107

Климов Иван
дата публикации 12-01-2005 18:00

ListBox с Fixed рядами и произвольными иконками для каждого ряда.

В данном примере хочется рассмотреть, как с помощью канвы можно преобразить и добавить функциональности компоненту ListBox. Хочу оговориться сразу - для простоты вида я рисую прямо на канве. Для уменьшения эффекта мигания можно рисовать на bmp.

Перейду непосредственно к примеру:
Форма = Form7, Компонент - ListBox1
Я решил подгрузить bmp для списка с FormCreate, хотя можно где угодно…

procedure TForm7.FormCreate(Sender: TObject);
begin
  bmp:=Tbitmap.Create; //создаем бмп
  bmp.Transparent:=True; //включаем прозрачность
  bmp.TransparentMode:=tmauto; //способ-авто, есть и другие флаги-смотри справку
  bmp.LoadFromFile(ExtractFilePath(Application.ExeName)+''user.bmp'');
end;

procedure TForm7.ListBox1DrawItem(Control: TWinControl; Index: Integer;
  Rect: TRect; State: TOwnerDrawState);
var
  int,int2,int3:integer; //для определения нечетности
  OldBkMode: integer; //для задания прозрачности фона текста через api
  myrect,Myrect2:Trect; // произвольные ректы для боковой полосы и дорисовки ее
                        //до конца листбокса
  oldbs:TbrushStyle; //для отмены прозрачности кисти
  Fcolor:Tcolor; //для отмены цвета шрифта
Begin
 //задаем координаты боковой полоски
  myrect.Top:=rect.top;
  myrect.Bottom:=rect.Bottom;
  myrect.Left :=Rect.Left{ListBox1.Left};
  Myrect.Right:=Rect.Left+Bmp.Width+2;
  Myrect2.Top:=rect.bottom;
  Myrect2.Left:=Myrect.Left;
  Myrect2.Right:=Myrect.Right;
  Myrect2.Bottom:=ListBox1.Height;
//Если элемент последний то дорисовываем градиент до конца листбокса
// (эффект на любителя)
  if index=ListBox1.Count-1 then
  DrawGradient(Listbox1.Canvas,Myrect2,true,[$00FDF6F6,$00D9C9C9]);

 if (index <> 0) and (index <> 6)then
 begin
  // Отрисовываем листбокс без Fixed рядом (у нас 0-ой и 6-й)
  ListBox1.Canvas.TextRect(rect,rect.left+bmp.width+3,rect.top+3,ListBox1.Items[Index]);
  int:= index mod 2;
  if int = 0 then
  begin //если ряд четный то рисуем полоску светлого цвета (а-ля Linux!!)
   ListBox1.Canvas.Brush.Color:=RGB(242,245,253); // я выбрал такой цвет
   //убираем линии прямоугольника. (рисуем его без рамки)
   ListBox1.Canvas.pen.Style :=psclear;
   //рисуем прямоугольник
   ListBox1.Canvas.RoundRect(Rect.Left+bmp.Width,Rect.Top,rect.Right,
                             rect.bottom,0,0);
   // задаем прозрачность текста
   OldBkMode := SetBkMode(ListBox1.Canvas.Handle, TRANSPARENT);
   // рисуем текст
   ListBox1.Canvas.TextRect(rect,rect.left+bmp.Width+3,rect.top+3,
                            ListBox1.Items[Index]);
   // возвращаем старые настройки прозрачности
   SetBkMode(ListBox1.Canvas.Handle, OldBkMode);
  end;
 end;
 if (index <> 0) and (index <> 6) then
 begin // Если не Fixed то рисуем градиент сбоку
   DrawGradient(Listbox1.Canvas,Myrect,true,[$00FDF6F6,$00D9C9C9]);
   //если хотим чтобы все bmp были одинаковые
   {ListBox1.Canvas.Draw(rect.Left,Rect.Top,bmp); }
 end;
 if (index=0) or (index = 6) then
 begin //отрисовка Fixed рядов
   ListBox1.Canvas.Font.Style:=[fsBold]; //Шрифт жирный
   DrawGradient(Listbox1.Canvas,Rect,false,[$00FDF6F6,$00D9C9C9]); // другой градиент
   ListBox1.Canvas.Brush.Color:=$00947C7C; //Цвет каемки Fixed прямоугольника
   ListBox1.Canvas.FrameRect(Rect); //Рисуем рамку
   oldbs:=ListBox1.Canvas.Brush.Style;
   Listbox1.Canvas.Brush.Style:=bsclear; //делаем фон текста прозрачным
   Fcolor:=ListBox1.Canvas.Font.Color; //запоминаем цвет шрифта
   ListBox1.Canvas.Font.Color:=clMedGray;
   DrawText(ListBox1.Canvas.Handle,Pchar(ListBox1.Items[Index]),
            length(ListBox1.Items[Index]),rect,
            DT_SINGLELINE  or DT_CENTER or DT_VCENTER); //рисуем текст в центре ячейки
   ListBox1.Canvas.Font.Color:=Fcolor; //восстанавливаем цвет
   ListBox1.Canvas.Brush.Style:=oldbs; //восстанавливаем стиль прозрачности
 end;
 if (index <> 0) and (index <> 6) then
 begin //чтобы не мигали Fixed ячейки
   if odselected in state then
   begin //отрисовываем выбранный элемент
     ListBox1.Canvas.pen.Width:=1;
     ListBox1.Canvas.pen.Color :=$00947C7C;
     ListBox1.Canvas.pen.Style :=pssolid;
     ListBox1.Canvas.Brush.Color:=$00C2EEFF;
     ListBox1.Canvas.Rectangle(rect.Left,rect.Top,rect.Right,rect.bottom);
     oldbs:=ListBox1.Canvas.Brush.Style;
     ListBox1.Canvas.Brush.Style:=bsclear;
     ListBox1.Canvas.TextRect(rect,rect.left+bmp.Width+3,rect.top+3,
                   ListBox1.Items[Index]);
     ListBox1.Canvas.Brush.Style:=oldbs;
   end;
 end;
 if index = 5 then
   //выводим произвольный битмап в выбранный ряд
   ListBox1.Canvas.Draw(rect.Left,Rect.Top,bmp);
 if odFocused in State then
   //убираем стандартные точечки выделения при потере фокуса
   ListBox1.Canvas.DrawFocusRect(Rect);
end;

Наверное, Вы хотите спросить: "А что такое DrawGradient???" Это процедура по прорисовке градиента с произвольным количеством цветов (взята мною из DelphiWorld''a).

Примечание:
Автор процедуры: David Johannes Rieger
Оригинал материала: http://www.djr-delphi.gmxhome.de/old/Tipps/Grafik/005.htm
http://www.swissdelphicenter.ch/torry/showcode.php?id=1162

procedure DrawGradient(ACanvas: TCanvas; Rect: TRect;
   Horicontal: Boolean; Colors: array of TColor);
 type
   RGBArray = array[0..2] of Byte;
 var
   x, y, z, stelle, mx, bis, faColorsh, mass: Integer;
   Faktor: double;
   A: RGBArray;
   B: array of RGBArray;
   merkw: integer;
   merks: TPenStyle;
   merkp: TColor;
 begin
   mx := High(Colors);
   if mx > 0 then
   begin
     if Horicontal then
       mass := Rect.Right - Rect.Left
     else
       mass := Rect.Bottom - Rect.Top;
     SetLength(b, mx + 1);
     for x := 0 to mx do
     begin
       Colors[x] := ColorToRGB(Colors[x]);
       b[x][0] := GetRValue(Colors[x]);
       b[x][1] := GetGValue(Colors[x]);
       b[x][2] := GetBValue(Colors[x]);
     end;
     merkw := ACanvas.Pen.Width;
     merks := ACanvas.Pen.Style;
     merkp := ACanvas.Pen.Color;
     ACanvas.Pen.Width := 1;
     ACanvas.Pen.Style := psSolid;
     faColorsh := Round(mass / mx);
     for y := 0 to mx - 1 do
     begin
       if y = mx - 1 then
         bis := mass - y * faColorsh - 1
       else
         bis := faColorsh;
       for x := 0 to bis do
       begin
         Stelle := x + y * faColorsh;
         faktor := x / bis;
         for z := 0 to 3 do
           a[z] := Trunc(b[y][z] + ((b[y + 1][z] - b[y][z]) * Faktor));
         ACanvas.Pen.Color := RGB(a[0], a[1], a[2]);
         if Horicontal then
         begin
           ACanvas.MoveTo(Rect.Left + Stelle, Rect.Top);
           ACanvas.LineTo(Rect.Left + Stelle, Rect.Bottom);
         end
         else
         begin
           ACanvas.MoveTo(Rect.Left, Rect.Top + Stelle);
           ACanvas.LineTo(Rect.Right, Rect.Top + Stelle);
         end;
       end;
     end;
     b := nil;
     ACanvas.Pen.Width := merkw;
     ACanvas.Pen.Style := merks;
     ACanvas.Pen.Color := merkp;
   end
   else
     // Please specify at least two colors
    raise EMathError.Create(''Es mussen mindestens zwei Farben angegeben werden.'');
 end;

Иван Климов
www.isoftware.amillo.net