TlistView (and TJvlistView) - Subitems line in 255 characters. Is there a way to get around this restriction?
Dynamically I fill in the SubItems of 670 characters, but the line turns out only 255 characters.
unit Unit1;
interface
uses
//Winapi.Windows,
//Winapi.Messages,
System.SysUtils, // IntToStr, FreeAndNil
Vcl.Controls, // alClient
Vcl.Forms,
CommCtrl, // for LVSCW_AUTOSIZE
ComCtrls; // for TListView
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
ListView1: TListView;
ListItem1: TListItem;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
form1.Height := 115;
form1.Width := 350;
form1.Position := poScreenCenter;
form1.Caption := 'Subitems[2] in 255 characters';
ListView1 := TListView.Create(Self);
with ListView1 do
begin
Parent := Self;
Align := alClient;
ViewStyle := vsReport;
BorderWidth := 2;
GridLines := true;
end;
with ListView1.Columns do
begin
Add.Caption := 'Line № ';
Add.Caption := 'Error ';
Add.Caption := 'String ';
end;
try
ListView1.Items.BeginUpdate;
ListItem1 := ListView1.Items.Add;
ListItem1.Caption := '22421 ';
ListItem1.SubItems.Add('All Columns ' + IntToStr(ListView1.Columns.Count));
ListItem1.SubItems.Add('<RHINOSTRING English="Exploding this mesh will create %d individual meshes. This may be more than your system can safely manage using the available memory. You can use Weld to make the mesh explode into fewer pieces, or see Help for more information.\n\nClick OK to proceed with Explode, or Cancel to leave the mesh as is.[[24836]]" Localized="Exploding this mesh will create %d individual meshes. This may be more than your system can safely manage using the available memory. You can use Weld to make the mesh explode into fewer pieces, or see Help for more information.\n\nClick OK to proceed with Explode, or Cancel to leave the mesh as is.[[24836]]" />');
//uses CommCtrl;
ListView1.Columns[0].Width := {LVSCW_AUTOSIZE or} LVSCW_AUTOSIZE_USEHEADER;
ListView1.Columns[1].Width := {LVSCW_AUTOSIZE or} LVSCW_AUTOSIZE_USEHEADER;
ListView1.Columns[2].Width := LVSCW_AUTOSIZE {or LVSCW_AUTOSIZE_USEHEADER};
finally
ListView1.Items.EndUpdate;
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeAndNil(ListView1);
end;
end.
I don't think you can have Windows display long columns if you let Windows itself draw them.
Indeed, the official documentation of the LVITEM
structure says this:
If the structure receives item attributes, pszText is a pointer to a buffer that receives the item text. Note that although the list-view control allows any length string to be stored as item text, only the first 260 TCHARs are displayed.
But there is still a solution: Instead of having Windows draw the column, draw it yourself!
Set the TListView
control's OwnerDraw
to True
and add the following OnDrawItem
handler:
procedure TForm1.ListView1DrawItem(Sender: TCustomListView; Item: TListItem;
Rect: TRect; State: TOwnerDrawState);
begin
if Sender = nil then
Exit;
if Item = nil then
Exit;
if odSelected in State then
begin
Sender.Canvas.Font.Color := clHighlightText;
Sender.Canvas.Brush.Color := clHighlight;
end
else
begin
Sender.Canvas.Font.Color := clWindowText;
Sender.Canvas.Brush.Color := clWindow;
end;
Sender.Canvas.Brush.Style := bsSolid;
Sender.Canvas.FillRect(Rect);
var R := Rect;
var S: string;
Sender.Canvas.Brush.Style := bsClear;
R.Offset(ScaleValue(5), 0);
for var i := 0 to ListView1.Columns.Count - 1 do
begin
R.Width := Sender.Column[i].Width - ScaleValue(5);
if i = 0 then
S := Item.Caption
else
S := Item.SubItems[i - 1];
Sender.Canvas.TextRect(R, S, [tfSingleLine, tfVerticalCenter, tfEndEllipsis]);
R.Offset(Sender.Column[i].Width, 0);
end;
R := Rect;
R.Inflate(-ScaleValue(1), -ScaleValue(1));
if odFocused in State then
begin
Sender.Canvas.Pen.Color := clWindow;
Sender.Canvas.Brush.Color := clWindow;
Sender.Canvas.DrawFocusRect(R);
end;
end;
To test this, I created a new VCL application, dropped a TListView
on the main form, made it Align = alClient
, BorderStyle = bsNone
, DoubleBuffered = True
, ViewStyle = vsReport
, MultiSelect = True
, and added three columns at design time.
Then I populate it at run time:
procedure TForm1.FormCreate(Sender: TObject);
begin
for var i := 1 to 1000 do
begin
var lm := ListView1.Items.Add;
lm.Caption := 'Caption of item ' + i.ToString;
lm.SubItems.Add('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. '+'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est LABORUM. This is item ' + i.ToString);
lm.SubItems.Add('Second subitem of ' + i.ToString);
end;
end;
Doing owner drawing requires quite a lot of attention to detail. My code above handles selections, focus rectangles, and horizontal scrolling.