如何使 TListView header 标题在 VCL 样式的应用程序中居中?
How can I make a TListView header caption centered in a VCL styled application?
我有一个列表视图控件(ListView
这里),我正在用这样的代码填充它:
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;
问题是当我在我的应用程序中使用 VCL 样式时,文本未在列表视图中居中 header:
如何使 header 标题在 VCL 样式的应用程序中居中?
负责绘制列的样式挂钩 header 从不检查列的文本对齐方式,并且始终以左对齐方式绘制文本,这显然是一个疏忽。
首先创建 Vcl.ComCtrls.TListViewStyleHook
的后代和祖先的 class 帮助程序,以便我们可以访问我们需要的私有变量。
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;
修复方法:
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;
最后我们必须为 TListView 控件注册我们的扩展样式钩子:
Initialization
TCustomStyleEngine.RegisterStyleHook(TListView, TListViewStyleHookEx);
Finalization
TCustomStyleEngine.UnRegisterStyleHook(TListView, TListViewStyleHookEx);
我有一个列表视图控件(ListView
这里),我正在用这样的代码填充它:
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;
问题是当我在我的应用程序中使用 VCL 样式时,文本未在列表视图中居中 header:
如何使 header 标题在 VCL 样式的应用程序中居中?
负责绘制列的样式挂钩 header 从不检查列的文本对齐方式,并且始终以左对齐方式绘制文本,这显然是一个疏忽。
首先创建 Vcl.ComCtrls.TListViewStyleHook
的后代和祖先的 class 帮助程序,以便我们可以访问我们需要的私有变量。
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;
修复方法:
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;
最后我们必须为 TListView 控件注册我们的扩展样式钩子:
Initialization
TCustomStyleEngine.RegisterStyleHook(TListView, TListViewStyleHookEx);
Finalization
TCustomStyleEngine.UnRegisterStyleHook(TListView, TListViewStyleHookEx);