Ho iniziato a giocare con il controllo TListBox, a disegnare immagini e cambiare gli stili dei font, ecc. Voglio intensificarlo un po 'e provare a manipolare di più gli oggetti con indentazione e rientro multi livello.TListbox - Manipolazione del layout di immagini e testo?
Date un'occhiata a questa immagine per una migliore idea:
L'idea è che gli elementi nell'elenco che si posizionano tra le voci di inizio e di fine devono essere rientrate di conseguenza.
Così, per dare un'idea Ho modificato la schermata in Paint, quindi sarebbe simile a questa:
Quale sarebbe il modo per avvicinarsi a questo? Il mio pensiero era quello di scorrere la lista e restituire in 2 variabili separate la quantità di articoli iniziali e finali, quindi determinare in qualche modo dove sono gli altri elementi e se la concordanza tra, ma la mia logica non è mai così buona :(
Per facilità d'uso, ho previsto di seguito il codice per mostrare come sto disegnando le immagini e gli stili:.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ImgList, ComCtrls;
type
TForm1 = class(TForm)
ImageList1: TImageList;
PageControl1: TPageControl;
TabSheet1: TTabSheet;
ListBox1: TListBox;
TabSheet2: TTabSheet;
ListBox2: TListBox;
TabSheet3: TTabSheet;
ListBox3: TListBox;
procedure FormCreate(Sender: TObject);
procedure ListBox1MeasureItem(Control: TWinControl; Index: Integer;
var Height: Integer);
procedure ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
procedure ListBox2MeasureItem(Control: TWinControl; Index: Integer;
var Height: Integer);
procedure ListBox2DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
procedure ListBox3MeasureItem(Control: TWinControl; Index: Integer;
var Height: Integer);
procedure ListBox3DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
// assign quick identifiers to image indexes
const
imgLayout = 0;
imgCalculator = 1;
imgComment = 2;
imgTime = 3;
imgStart = 4;
imgEnd = 5;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
ListStyle: TListBoxStyle;
begin
// set the listbox style here
ListStyle := lbOwnerDrawVariable;
ListBox1.Style := ListStyle;
ListBox2.Style := ListStyle;
ListBox3.Style := ListStyle;
end;
{******************************************************************************}
procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
var
TextPosition: Integer;
Images: TImageList;
begin
TListBox(Control).Canvas.FillRect(Rect);
Images := ImageList1;
// draw the images
if TListBox(Control).Items.Strings[Index] = 'Layout' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4, Rect.Top, imgLayout);
end else
if TListBox(Control).Items.Strings[Index] = 'Calculator' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4, Rect.Top,
imgCalculator);
end else
if TListBox(Control).Items.Strings[Index] = 'Comment' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4, Rect.Top, imgComment);
end else
if TListBox(Control).Items.Strings[Index] = 'Time' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4, Rect.Top, imgTime);
end;
// positions the text
TextPosition := (Rect.Bottom - Rect.Top - TListBox(Control).Canvas.TextHeight
(Text)) div 2;
// displays the text
TListBox(Control).Canvas.TextOut(Rect.Left + Images.Width + 8,
Rect.Top + TextPosition, TListBox(Control).Items.Strings[index]);
end;
procedure TForm1.ListBox1MeasureItem(Control: TWinControl; Index: Integer;
var Height: Integer);
begin
Height := ImageList1.Height;
end;
{******************************************************************************}
procedure TForm1.ListBox2DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
var
TextPosition: Integer;
Images: TImageList;
begin
TListBox(Control).Canvas.FillRect(Rect);
Images := ImageList1;
// draw the images
if TListBox(Control).Items.Strings[Index] = 'Layout' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4, Rect.Top, imgLayout);
TListBox(Control).Canvas.Font.Style := [fsBold];
end else
if TListBox(Control).Items.Strings[Index] = 'Calculator' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4, Rect.Top,
imgCalculator);
TListBox(Control).Canvas.Font.Color := clBlue;
TListBox(Control).Canvas.Font.Style := [fsItalic];
end else
if TListBox(Control).Items.Strings[Index] = 'Comment' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4, Rect.Top, imgComment);
TListBox(Control).Canvas.Font.Color := clRed;
end else
if TListBox(Control).Items.Strings[Index] = 'Time' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4, Rect.Top, imgTime);
end;
// positions the text
TextPosition := (Rect.Bottom - Rect.Top - TListBox(Control).Canvas.TextHeight
(Text)) div 2;
// displays the text
TListBox(Control).Canvas.TextOut(Rect.Left + Images.Width + 8,
Rect.Top + TextPosition, TListBox(Control).Items.Strings[index]);
end;
procedure TForm1.ListBox2MeasureItem(Control: TWinControl; Index: Integer;
var Height: Integer);
begin
Height := ImageList1.Height;
end;
{******************************************************************************}
procedure TForm1.ListBox3DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
var
TextPosition: Integer;
Images: TImageList;
begin
TListBox(Control).Canvas.FillRect(Rect);
Images := ImageList1;
// draw the images
if TListBox(Control).Items.Strings[Index] = 'Layout' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4, Rect.Top, imgLayout);
end else
if TListBox(Control).Items.Strings[Index] = 'Calculator' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4, Rect.Top,
imgCalculator);
end else
if TListBox(Control).Items.Strings[Index] = 'Comment' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4, Rect.Top, imgComment);
end else
if TListBox(Control).Items.Strings[Index] = 'Time' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4, Rect.Top, imgTime);
end else
if TListBox(Control).Items.Strings[Index] = 'Start' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4, Rect.Top,
imgStart);
TListBox(Control).Canvas.Font.Style := [fsBold];
end else
if TListBox(Control).Items.Strings[Index] = 'End' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4, Rect.Top, imgEnd);
TListBox(Control).Canvas.Font.Style := [fsBold];
end;
// positions the text
TextPosition := (Rect.Bottom - Rect.Top - TListBox(Control).Canvas.TextHeight
(Text)) div 2;
// displays the text
TListBox(Control).Canvas.TextOut(Rect.Left + Images.Width + 8,
Rect.Top + TextPosition, TListBox(Control).Items.Strings[index]);
end;
procedure TForm1.ListBox3MeasureItem(Control: TWinControl; Index: Integer;
var Height: Integer);
begin
Height := ImageList1.Height;
end;
{******************************************************************************}
end.
Gradirei alcuni suggerimenti su come avrei potuto determinare manipolare gli elementi so di poter cambiare dove il bitmap e i testi sono posizionati, ma si sta identificando se un elemento rientra o meno tra i gruppi e se imposta il livello di rientro corretto.
Spero che questo abbia senso, ecco perché ho messo alcune immagini finte.
Grazie :)
PS, non ho mai scrivere piccoli messaggi dispiace!
AGGIORNAMENTO CON demo funzionante
ho accettato la risposta di Sertac che ho perfettamente funzionante grazie Sertac.
per aiutare gli altri che possono essere di visualizzazione - e perché ho imparato OOP voglio mostrare il mio codice per vedere se è un bene :)
ho fatto 2 unità, Lib.pas contiene le classi per gli elementi di elenco, e Unit1.pas è l'unità Form1 (i unità 1 accorciato per renderlo più chiaro per vedere cosa sta succedendo):
Lib.pas
unit Lib;
interface
uses
Classes, StdCtrls;
type
TMyListData = class(TObject)
public
fCaption: string;
fImageIndex: integer;
public
property Caption: string read fCaption write fCaption;
property ImageIndex: integer read fImageIndex write fImageIndex;
constructor Create;
destructor Destroy; override;
end;
type
TLayoutItem = class(TMyListData);
TCalculatorItem = class(TMyListData);
TCommentItem = class(TMyListData);
TTimeItem = class(TMyListData);
TStartItem = class(TMyListData);
TEndItem = class(TMyListData);
const
imgLayout = 0;
imgCalculator = 1;
imgComment = 2;
imgTime = 3;
imgStart = 4;
imgEnd = 5;
procedure NewLayoutItem(aListBox: TListBox);
procedure NewCalculatorItem(aListBox: TListBox);
procedure NewCommentItem(aListBox: TListBox);
procedure NewTimeItem(aListBox: TListBox);
procedure NewStartItem(aListBox: TListBox);
procedure NewEndItem(aListBox: TListBox);
procedure DeleteItem(aListBox: TListBox; aIndex: integer);
procedure CalculateIndents(aListBox: TListBox);
implementation
{ TMyListData }
constructor TMyListData.Create;
begin
inherited Create;
end;
destructor TMyListData.Destroy;
begin
inherited;
end;
procedure NewLayoutItem(aListBox: TListBox);
var
Obj: TLayoutItem;
begin
Obj := TLayoutItem.Create;
try
Obj.Caption := 'Layout';
Obj.ImageIndex := imgLayout;
aListBox.AddItem(Obj.Caption, Obj);
finally
Obj.Free;
end;
CalculateIndents(aListBox);
end;
procedure NewCalculatorItem(aListBox: TListBox);
var
Obj: TCalculatorItem;
begin
Obj := TCalculatorItem.Create;
try
Obj.Caption := 'Calculator';
Obj.ImageIndex := imgCalculator;
aListBox.AddItem(Obj.Caption, Obj);
finally
Obj.Free;
end;
CalculateIndents(aListBox);
end;
procedure NewCommentItem(aListBox: TListBox);
var
Obj: TCommentItem;
begin
Obj := TCommentItem.Create;
try
Obj.Caption := 'Comment';
Obj.ImageIndex := imgComment;
aListBox.AddItem(Obj.Caption, Obj);
finally
Obj.Free;
end;
CalculateIndents(aListBox);
end;
procedure NewTimeItem(aListBox: TListBox);
var
Obj: TTimeItem;
begin
Obj := TTimeItem.Create;
try
Obj.Caption := 'Time';
Obj.ImageIndex := imgTime;
aListBox.AddItem(Obj.Caption, Obj);
finally
Obj.Free;
end;
CalculateIndents(aListBox);
end;
procedure NewStartItem(aListBox: TListBox);
var
Obj: TStartItem;
begin
Obj := TStartItem.Create;
try
Obj.Caption := 'Start';
Obj.ImageIndex := imgStart;
aListBox.AddItem(Obj.Caption, Obj);
finally
Obj.Free;
end;
CalculateIndents(aListBox);
end;
procedure NewEndItem(aListBox: TListBox);
var
Obj: TEndItem;
begin
Obj := TEndItem.Create;
try
Obj.Caption := 'End';
Obj.ImageIndex := imgEnd;
aListBox.AddItem(Obj.Caption, Obj);
finally
Obj.Free;
end;
CalculateIndents(aListBox);
end;
procedure DeleteItem(aListBox: TListBox; aIndex: integer);
begin
aListBox.Items.Delete(aIndex);
aListBox.Items.Objects[aIndex] := nil;
CalculateIndents(aListBox);
end;
procedure CalculateIndents(aListBox: TListBox);
var
i: Integer;
Indent: Integer;
begin
Indent := 0;
for i := 0 to aListBox.Items.Count - 1 do
begin
if aListBox.Items[i] = 'End' then
Dec(Indent);
if Indent > -1 then
aListBox.Items.Objects[i] := Pointer(Indent);
if aListBox.Items[i] = 'Start' then
Inc(Indent);
end;
for i := aListBox.Items.Count - 1 downto 0 do
begin
if (aListBox.Items[i] = 'End') and (Indent = -1) then
begin
DeleteItem(aListBox, i);
Break;
end;
end;
end;
end.
Unit1.pas
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ImgList, ComCtrls, Buttons;
type
TForm1 = class(TForm)
ImageList1: TImageList;
lbMain: TListBox;
btnLayout: TBitBtn;
btnCalculator: TBitBtn;
btnComment: TBitBtn;
btnTime: TBitBtn;
btnStartGroup: TBitBtn;
btnEndGroup: TBitBtn;
btnDelete: TBitBtn;
procedure FormCreate(Sender: TObject);
procedure lbMainMeasureItem(Control: TWinControl; Index: Integer;
var Height: Integer);
procedure lbMainDrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
procedure btnLayoutClick(Sender: TObject);
procedure btnCalculatorClick(Sender: TObject);
procedure btnCommentClick(Sender: TObject);
procedure btnTimeClick(Sender: TObject);
procedure btnStartGroupClick(Sender: TObject);
procedure btnEndGroupClick(Sender: TObject);
procedure btnDeleteClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses
Lib;
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
// set the listbox style here
lbMain.Style := lbOwnerDrawVariable;
end;
procedure TForm1.lbMainDrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
var
TextPosition: Integer;
Images: TImageList;
begin
TListBox(Control).Canvas.FillRect(Rect);
Images := ImageList1;
// draw the images
if TListBox(Control).Items.Strings[Index] = 'Layout' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4 +
8 * Integer(TListBox(Control).Items.Objects[Index]),
Rect.Top, imgLayout);
end
else if TListBox(Control).Items.Strings[Index] = 'Calculator' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4 +
8 * Integer(TListBox(Control).Items.Objects[Index]),
Rect.Top, imgCalculator);
end
else if TListBox(Control).Items.Strings[Index] = 'Comment' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4 +
8 * Integer(TListBox(Control).Items.Objects[Index]),
Rect.Top, imgComment);
end
else if TListBox(Control).Items.Strings[Index] = 'Time' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4 +
8 * Integer(TListBox(Control).Items.Objects[Index]),
Rect.Top, imgTime);
end
else if TListBox(Control).Items.Strings[Index] = 'Start' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4 +
8 * Integer(TListBox(Control).Items.Objects[Index]),
Rect.Top, imgStart);
end
else if TListBox(Control).Items.Strings[Index] = 'End' then
begin
Images.Draw(TListBox(Control).Canvas, Rect.Left + 4 +
8 * Integer(TListBox(Control).Items.Objects[Index]),
Rect.Top, imgEnd);
end;
// positions the text
TextPosition := (Rect.Bottom - Rect.Top - TListBox(Control).Canvas.TextHeight
(Text)) div 2;
// displays the text
TListBox(Control).Canvas.TextOut(
Rect.Left + Images.Width + 8 + 8 * Longint(TListBox(Control).Items.Objects[Index]),
Rect.Top + TextPosition, TListBox(Control).Items.Strings[index]);
end;
procedure TForm1.lbMainMeasureItem(Control: TWinControl; Index: Integer;
var Height: Integer);
begin
Height := ImageList1.Height;
end;
procedure TForm1.btnLayoutClick(Sender: TObject);
begin
NewLayoutItem(lbMain);
end;
procedure TForm1.btnCalculatorClick(Sender: TObject);
begin
NewCalculatorItem(lbMain);
end;
procedure TForm1.btnCommentClick(Sender: TObject);
begin
NewCommentItem(lbMain);
end;
procedure TForm1.btnTimeClick(Sender: TObject);
begin
NewTimeItem(lbMain);
end;
procedure TForm1.btnStartGroupClick(Sender: TObject);
begin
NewStartItem(lbMain);
end;
procedure TForm1.btnEndGroupClick(Sender: TObject);
begin
NewEndItem(lbMain);
end;
procedure TForm1.btnDeleteClick(Sender: TObject);
begin
if lbMain.ItemIndex <> -1 then
begin
DeleteItem(lbMain, lbMain.ItemIndex);
end;
end;
end.
Può essere fatto meglio, vale a dire l'assegnazione degli indici di immagine in base alle Items.Objects [] di proprietà, ma questo funziona perfettamente :)
Solo per curiosità, perché stai cercando di fare questo con TListBox. Questo sembra un posto perfetto per usare un TTreeView; ha un supporto integrato per il rientro, immagini basate sul tipo di contenuto e così via. Sembra che tu stia facendo un sacco di lavoro extra senza motivo. (All'inizio pensavo che tu stavi usando FireMonkey, ma una revisione del codice non indica che lo sei.) –
Se vuoi un elenco di look-and-feel interamente personalizzato, puoi anche utilizzare TScrollBox e progettare i tuoi elementi su TFrames. Configura una semplice matrice gestita per gestire gli oggetti nello stesso modo di TListBox.Items (fatto facilmente), e quindi non ci sono letteralmente limiti su ciò che puoi creare. – LaKraven
@KenWhite Purtroppo non ho Delphi XE2, anche io sono pienamente consapevole della Treeview che fornisce un modo migliore di visualizzare i dati come dici tu. D'altra parte, tuttavia, per una semplice visualizzazione listbox ho pensato che sarebbe stata una buona cosa provare e fare. Stavo solo sperimentando il TListbox, e dopo aver letto un po 'di più su Fire monkey dopo il tuo commento vedo che Treeview non può essere usato in quanto è un controllo di Windows, quindi fare questo con una listbox ha i suoi vantaggi in tal senso. –