【问题标题】:Delphi: ListView (vsReport) single column header caption with custom font color?Delphi:带有自定义字体颜色的ListView(vsReport)单列标题标题?
【发布时间】:2013-02-24 17:30:24
【问题描述】:

在带有 vsReport ViewStyle 的 ListView 中,如何自定义任何单个列标题标题的字体颜色?例如(第二列标题标题的字体颜色为红色):

【问题讨论】:

    标签: windows delphi listview delphi-xe2 columnheader


    【解决方案1】:

    我将处理NM_CUSTOMDRAW 标头通知代码,并在CDDS_ITEMPREPAINT 呈现阶段使用CDRF_NEWFONT 返回代码响应此通知消息。以下代码显示了如何扩展列表视图控件以具有指定标题项字体颜色的事件:

    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, ComCtrls, CommCtrl, StdCtrls;
    
    type
      TGetHeaderItemFontColorEvent = procedure(Sender: TCustomListView;
        ItemIndex: Integer; var FontColor: TColor) of object;
      TListView = class(ComCtrls.TListView)
      private
        FHeaderHandle: HWND;
        FOnGetHeaderItemFontColor: TGetHeaderItemFontColorEvent;
        procedure WMNotify(var AMessage: TWMNotify); message WM_NOTIFY;
      protected
        procedure CreateWnd; override;
      published
        property OnGetHeaderItemFontColor: TGetHeaderItemFontColorEvent read
          FOnGetHeaderItemFontColor write FOnGetHeaderItemFontColor;
      end;
    
    type
      TForm1 = class(TForm)
        ListView1: TListView;
        procedure FormCreate(Sender: TObject);
      private
        procedure GetHeaderItemFontColor(Sender: TCustomListView;
          ItemIndex: Integer; var FontColor: TColor);
      public
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    { TListView }
    
    procedure TListView.CreateWnd;
    begin
      inherited;
      FHeaderHandle := ListView_GetHeader(Handle);
    end;
    
    procedure TListView.WMNotify(var AMessage: TWMNotify);
    var
      FontColor: TColor;
      NMCustomDraw: TNMCustomDraw;
    begin
      if (AMessage.NMHdr.hwndFrom = FHeaderHandle) and
        (AMessage.NMHdr.code = NM_CUSTOMDRAW) then
      begin
        NMCustomDraw := PNMCustomDraw(TMessage(AMessage).LParam)^;
        case NMCustomDraw.dwDrawStage of
          CDDS_PREPAINT:
            AMessage.Result := CDRF_NOTIFYITEMDRAW;
          CDDS_ITEMPREPAINT:
          begin
            FontColor := Font.Color;
            if Assigned(FOnGetHeaderItemFontColor) then
              FOnGetHeaderItemFontColor(Self, NMCustomDraw.dwItemSpec, FontColor);
            SetTextColor(NMCustomDraw.hdc, ColorToRGB(FontColor));
            AMessage.Result := CDRF_NEWFONT;
          end;
        else
          AMessage.Result := CDRF_DODEFAULT;
        end;
      end
      else
        inherited;
    end;
    
    { TForm1 }
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      ListView1.OnGetHeaderItemFontColor := GetHeaderItemFontColor;
    end;
    
    procedure TForm1.GetHeaderItemFontColor(Sender: TCustomListView;
      ItemIndex: Integer; var FontColor: TColor);
    begin
      case ItemIndex of
        0: FontColor := clRed;
        1: FontColor := clGreen;
        2: FontColor := clBlue;
      end;
    end;
    
    end.
    

    整个项目你可以download from here。这是上面示例的结果:

    【讨论】:

    • @TLama,可能是。我刚刚执行了你的 EXE 并报告了我看到的内容 :)
    • @kobik,如果您只是将该列表视图剪切并粘贴到不同的项目中,您也会看到同样的情况;-) 您可以通过不喜欢的DoubleBuffered 属性来解决此问题,或使用虚拟模式解决。我敢打赌,QC 有很多关于它的报告。
    • 我明天在工作中试试这个(那里有一个 XP 系统)。我认为它会在那里工作得很好。
    • 已测试:在 Windows 2000 和 Vista 上工作,但在 Windows XP 上不工作。即使切换到 Xp 标准主题。
    • 找到解决方案:Condition1 := True; InvalidateRect(ListView1.FHeaderHandle, nil, True);
    【解决方案2】:

    您可以从列表视图中获取本机标题控件,然后将列的特定项目标记为所有者绘制。您只需要在请求绘制标题项时更改文本颜色(如果您不删除 string 标志)。绘图消息将被发送到标题的父级 - 列表视图,因此您需要在那里处理消息。有关所有者绘制的标题控件,请参阅 here

    示例代码:

    type
      TForm1 = class(TForm)
        ListView1: TListView;
        procedure FormCreate(Sender: TObject);
         ...
      private
        FLVHeader: HWND;
        FSaveLVWndProc: TWndMethod;
        procedure LVWndProc(var Msg: TMessage);
        procedure SetHeaderItemStyle(Index: Integer);
      end;
    
    ..
    uses commctrl;
    ..
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      FLVHeader := ListView_GetHeader(ListView1.Handle);
      SetHeaderItemStyle(1);
    
      FSaveLVWndProc := ListView1.WindowProc;
      ListView1.WindowProc := LVWndProc;
    end;
    
    procedure TForm1.SetHeaderItemStyle(Index: Integer);
    var
      HeaderItem: THDItem;
    begin
      HeaderItem.Mask := HDI_FORMAT or HDI_TEXT or HDI_LPARAM;
      Header_GetItem(FLVHeader, 1, HeaderItem);
      HeaderItem.Mask := HDI_FORMAT;
      HeaderItem.fmt := HeaderItem.fmt or HDF_OWNERDRAW;
      Header_SetItem(FLVHeader, 1, HeaderItem);
    end;
    
    procedure TForm1.LVWndProc(var Msg: TMessage);
    begin
      FSaveLVWndProc(Msg);    // thanks to @Kobik (cause SO if called later then WM_NOTIFY case on some (all other then mine?) machines)
    
      case Msg.Msg of
        WM_DRAWITEM:
          if (TWmDrawItem(Msg).DrawItemStruct.CtlType = ODT_HEADER) and
              (TWmDrawItem(Msg).DrawItemStruct.hwndItem = FLVHeader) and
              (TWmDrawItem(Msg).DrawItemStruct.itemID = 1) then
            SetTextColor(TWmDrawItem(Msg).DrawItemStruct.hDC, ColorToRGB(clRed));
        WM_NOTIFY:
          if (TWMNotify(Msg).NMHdr.hwndFrom = FLVHeader) and
              (TWMNotify(Msg).NMHdr.code = HDN_ITEMCHANGED) then
              // also try 'HDN_ENDTRACK' if it doesn't work as expected
            SetHeaderItemStyle(1);
        WM_DESTROY: ListView1.WindowProc := FSaveLVWndProc;
      end;
    end;
    

    【讨论】:

    • @SertacAkyuz:当我尝试调整任何列的大小时,我得到了 AV。
    • @Andreas,感谢关于NMCustomDraw.dwItemSpec的观点!
    • @TLama - 这让我很难过,结果它实际上甚至是documented。好答案(+1)顺便说一句..
    • @SertacAkyuz,这也是我的第一个想法(因为 Stack Overflow)。这没有帮助。
    • @kobik - 这是操作系统的一个特性,也由 VCL 源验证:在TCustomHeaderControl.CNNotify 中有一个案例分支:HDN_ITEMCHANGEDA, HDN_ITEMCHANGEDW:。 VCL 也不能冒险使用HDN_ITEMCHANGED
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多