【问题标题】:How can I make a TListView header caption centered in a VCL styled application?如何使 TListView 标题标题以 VCL 样式的应用程序为中心?
【发布时间】:2015-07-16 13:27:48
【问题描述】:

我有一个列表视图控件(此处为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 样式时,文本不在列表视图标题中居中:

如何使标题标题在 VCL 样式的应用程序中居中?

【问题讨论】:

  • 我标记了这只火猴,对吗?
  • 不,它的 vcl david(fileList 是 listview)。 XE6
  • 感谢@TLama,我很抱歉,我在 delphi 方面并不完美,我是初学者。 idk如何真正地问
  • 您可以修改样式并更改它,但是它也会更改所有列标题。也许有一个 VCL 风格的 hack 可以即时改变这一点?在样式设计器中,转到 Objects > Header > Section,然后在右侧找到 TextAlign。也就是说,如果您希望这是系统范围的。如果没有,您将需要自己绘制或找到一个 hack。
  • 这就是 vcl 风格的生活。我无法理解吸引力。我还记得 WinAmp。

标签: delphi delphi-xe6 vcl-styles


【解决方案1】:

负责绘制列标题的样式挂钩从不检查列的文本对齐方式,并且总是以左对齐方式绘制文本,这显然是一个疏忽。

首先创建Vcl.ComCtrls.TListViewStyleHook 的后代和祖先的类助手,以便我们可以访问我们需要的私有变量。

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);

【讨论】:

  • 实际上,正如我在问题中的评论中所述,VCL Styles 实际上对此有自己的属性。我还没有测试过它,所以它可能不起作用(就像 VCL 样式中的许多其他东西一样)。
  • @JerryDodge,是的,但正如我所说,原始代码从不使用该属性。您在样式设计器中看到的道具只是一组继承的属性,并不意味着它们被以任何方式使用。这实际上比 VCL 样式看起来更常见。
  • 我知道您要求不考虑 TListView 列对齐,但我在 VCL 样式中谈论这一点 - 两个不同的属性。显然他们都没有得到承认。我想除了 VCL Styles 中的设置至少应该优先于实际控制。
猜你喜欢
  • 2019-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-04
  • 2016-01-19
  • 1970-01-01
相关资源
最近更新 更多