I'm having a list view control (ListView
here), and I'm filling it by the code like this:
var
Item: TListItem;
Column: TListColumn;
begin
ListView.ViewStyle := vsReport;
Column := ListView.Columns.Add;
Column.Width := 200;
Column.Alignment:= taCenter;
Column.Caption:= 'Column 1';
Column:= ListView.Columns.Add;
Column.Width := 200;
Column.Alignment := taCenter;
Column.Caption := 'Column 2';
Item := ListView.Items.Add;
Item.Caption := 'Item 1';
Item.SubItems.Add('Subitem 1');
end;
The problem is that when I use a VCL style in my application, the text is not centered in the list view header:
How can I make the header caption centered in a VCL styled application?
The style hook responsible for drawing the column header never checks for the text alignment of the column and always draws text with left alignment, clearly an oversight.
First create a descendant of Vcl.ComCtrls.TListViewStyleHook
and a class helper for the ancestor so that we can access the private variable we will need.
TListViewStyleHookHelper = class helper for TListViewStyleHook
function getFHeaderHandle: HWnd;
end;
TListViewStyleHookEx = class(Vcl.ComCtrls.TListViewStyleHook)
strict protected
procedure DrawHeaderSection(Canvas: TCanvas; R: TRect; Index: Integer;
const Text: string; IsPressed, IsBackground: Boolean); override;
end;
Fixing the method:
uses
Winapi.Commctrl;
function TListViewStyleHookHelper.getFHeaderHandle: HWnd;
begin
Result := Self.FHeaderHandle;
end;
procedure TListViewStyleHookEx.DrawHeaderSection(Canvas: TCanvas; R: TRect;
Index: Integer; const Text: string; IsPressed, IsBackground: Boolean);
var
Item: THDItem;
ImageList: HIMAGELIST;
DrawState: TThemedHeader;
IconWidth, IconHeight: Integer;
Details: TThemedElementDetails;
LListView: TListView;
DT_Align: Integer;
begin
FillChar(Item, SizeOf(Item), 0);
Item.mask := HDI_FORMAT;
Header_GetItem(getFHeaderHandle, Index, Item);
if IsBackground then
DrawState := thHeaderItemNormal
else if IsPressed then
DrawState := thHeaderItemPressed
else
DrawState := thHeaderItemNormal;
Details := StyleServices.GetElementDetails(DrawState);
StyleServices.DrawElement(Canvas.Handle, Details, R);
ImageList := SendMessage(getFHeaderHandle, HDM_GETIMAGELIST, 0, 0);
Item.mask := HDI_FORMAT or HDI_IMAGE;
InflateRect(R, -2, -2);
IconWidth := 0;
if (ImageList <> 0) and Header_GetItem(getFHeaderHandle, Index, Item) then
begin
if Item.fmt and HDF_IMAGE = HDF_IMAGE then
begin
ImageList_Draw(ImageList, Item.iImage, Canvas.Handle, R.Left, R.Top,
ILD_TRANSPARENT);
ImageList_GetIconSize(ImageList, IconWidth, IconHeight);
Inc(R.Left, IconWidth + 5);
end;
end;
if IconWidth = 0 then
Inc(R.Left, 2);
DT_Align := 0;
if Control is TListView then
begin
LListView := TListView(Control);
if (Index > -1) and (Index < LListView.Columns.Count) then
case LListView.Columns[Index].Alignment of
taLeftJustify:
DT_Align := 0;
taRightJustify:
DT_Align := 2;
taCenter:
DT_Align := 1;
end;
end;
DrawControlText(Canvas, Details, Text, R, DT_VCENTER or DT_Align or
DT_SINGLELINE or DT_END_ELLIPSIS);
end;
And finally we have to register our extended style hook for the TListView control:
Initialization
TCustomStyleEngine.RegisterStyleHook(TListView, TListViewStyleHookEx);
Finalization
TCustomStyleEngine.UnRegisterStyleHook(TListView, TListViewStyleHookEx);