TListView Header重绘和高度设置

TListView 的 Header 部分默认 BtnFace 颜色,高度也不能改变。我们可以通过编写一些代码来实现这些功能;

  • 获得TListView 的Header 的句柄;

    TListView的Header其实是一个 HeaderContorl 控件。要获得他的句柄需要调用下面的代码

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------

static HWND hListViewHeader = NULL;

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::FormCreate(TObject *Sender)
{
//获得ListView Header的句柄
    hListViewHeader = ListView_GetHeader(ListView1->Handle);
}
//---------------------------------------------------------------------------

其实还有许多ListView_XXXX 这样的windows API ,需要查看MSDN;

  • 改变Header的高度

Header的高度,是在一个处理一个叫 HDM_LAYOUT 的消息的时候进行设置。

msdn的原话如下:

那么我们就用SetWindowLong 来改变 Header的 消息过程。

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------

static HWND hListViewHeader = NULL;

//Header原来的窗口过程
static WNDPROC oldHeaderWindowProc = NULL;

//Header的新窗口过程
LRESULT CALLBACK NewHeaderWindowProc(
      HWND hwnd,
      UINT uMsg,
      WPARAM wParam,
      LPARAM lParam)
{
//先要用原来的窗口过程处理
    LRESULT result = oldHeaderWindowProc(hwnd,uMsg,wParam,lParam);
//再处理HDM_LAYOUT消息
    if (HDM_LAYOUT == uMsg)
    {
        LPHDLAYOUT phdmlayout = (LPHDLAYOUT)lParam;
        //改变header的高度
        phdmlayout->pwpos->cy = 27;
        //改变listview表格部分的高度
        phdmlayout->prc->top  = phdmlayout->pwpos->cy;
    }

    return result;
}

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::FormCreate(TObject *Sender)
{
    hListViewHeader = ListView_GetHeader(ListView1->Handle);
    oldHeaderWindowProc = (WNDPROC)SetWindowLong(
        hListViewHeader,GWL_WNDPROC,(LONG)NewHeaderWindowProc);
    ListView1->Invalidate();
}
//---------------------------------------------------------------------------

我们会看到这样的效果

  • 处理Header的背景色

可以在Header的窗口过程中编写WM_PAINT 消息处理函数。但是这样写,你会发现无法在Header上输出文字。准确的说,你绘制上去的文字

会莫名其妙的不见,比如,拉宽一下Header 中的某一列,文字就会消失边检。使得这种重绘不好控制;

正确的做法是这样

  1. 调用 Header_SetItem 函数(其实是一个macro) 将ListView的Header 的 format 属性,设置为 HDF_OWNERDRAW (默认是 HDF_STRING).
  2. 重写ListView 的消息过程。(不是Header的消息过程);
  3. 在ListView的消息过程中处理 WM_DRAWITEM 消息;(WM_DRAWITEM消息会包含要重绘的 区域,hdc,index,状态等等)
  4. 在Header 的 HDM_LAYOUT 消息处理过程,也要调用 Header_SetItem,将format消息设置为 HDF_OWERDRAW (否则,每次改变列宽,format属性就又变成HDF_STRING,导致无法重绘)

下面是实现的代码

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------

static HWND hListViewHeader = NULL;

//Header原来的窗口过程
static WNDPROC oldHeaderWindowProc = NULL;

//listView 原来的窗口过程
static WNDPROC oldListViewProc = NULL;

//Header的新窗口过程
LRESULT CALLBACK NewHeaderWindowProc(
      HWND hwnd,
      UINT uMsg,
      WPARAM wParam,
      LPARAM lParam)
{
//先要用原来的窗口过程处理
    LRESULT result = oldHeaderWindowProc(hwnd,uMsg,wParam,lParam);
//再处理HDM_LAYOUT消息
    if (HDM_LAYOUT == uMsg)
    {
        LPHDLAYOUT phdmlayout = (LPHDLAYOUT)lParam;
        //改变header的高度
        phdmlayout->pwpos->cy = 27;
        //改变listview表格部分的高度
        phdmlayout->prc->top  = phdmlayout->pwpos->cy;
    }

    return result;
}

LRESULT CALLBACK NewListViewWindowProc(
      HWND hwnd,
      UINT uMsg,
      WPARAM wParam,
      LPARAM lParam)
{
    if (uMsg == WM_DRAWITEM)
    {
        DRAWITEMSTRUCT* drawItemStruct = (DRAWITEMSTRUCT*)lParam;
        HDC hdc = drawItemStruct->hDC;
        int index = drawItemStruct->itemID;
        RECT rect = drawItemStruct->rcItem;

        TBrush* brush = new TBrush();
        TColor fontColor = clBlack;
        if (index == 0)
        {
            brush->Color = clRed;
            fontColor    = clWhite;
        }

        if (index == 1)
        {
            brush->Color = clYellow;
            fontColor    = clBlue;
        }

        if (index == 2)
        {
            brush->Color = clBlue;
            fontColor    = clWhite;
        }

        if (index == 3)
        {
            brush->Color = clLime;
            fontColor    = clBlue;
        }

        FillRect(hdc,&rect,brush->Handle);
        delete brush;

        SetTextColor(hdc,fontColor);
        SetBkMode(hdc,TRANSPARENT);
        //获取文本
        HDITEM hditem= {0};
        char buf[100] = {0};
        hditem.mask = HDI_TEXT;
        hditem.pszText = buf;
        hditem.cchTextMax = 100;
        Header_GetItem(hListViewHeader,index,&hditem);

        DrawText(hdc,hditem.pszText,strlen(hditem.pszText),&rect,
        DT_CENTER |DT_VCENTER |DT_SINGLELINE);

    }

    return oldListViewProc(hwnd,uMsg,wParam,lParam);
}

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------

void SetHDItem()
{
    if (hListViewHeader == NULL)
    {
        return;
    }

    HDITEM hditem;
    hditem.mask = HDI_FORMAT;
    hditem.fmt  = HDF_OWNERDRAW; //默认是 HDF_STRING

    int count = Header_GetItemCount(hListViewHeader);
    for (int i = 0; i < count ;i++)
    {
        Header_SetItem(hListViewHeader,i,&hditem);
    }
}

void __fastcall TForm1::FormCreate(TObject *Sender)
{
    hListViewHeader = ListView_GetHeader(ListView1->Handle);

    oldHeaderWindowProc = (WNDPROC)SetWindowLong(
        hListViewHeader,GWL_WNDPROC,(LONG)NewHeaderWindowProc);

    oldListViewProc = (WNDPROC)SetWindowLong(
        ListView1->Handle,GWL_WNDPROC,(LONG)NewListViewWindowProc);

    SetHDItem();

    ListView1->Invalidate();
}
//---------------------------------------------------------------------------

显示效果如下

但是,一旦拖动列头会变成这样

所以需要有上述步骤中第4步,在HDM_LAYOUT消息中加入SetHDItem 函数调用

最终代码

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------

static HWND hListViewHeader = NULL;

//Header原来的窗口过程
static WNDPROC oldHeaderWindowProc = NULL;

//listView 原来的窗口过程
static WNDPROC oldListViewProc = NULL;

void SetHDItem()
{
    if (hListViewHeader == NULL)
    {
        return;
    }

    HDITEM hditem;
    hditem.mask = HDI_FORMAT;
    hditem.fmt  = HDF_OWNERDRAW; //默认是 HDF_STRING

    int count = Header_GetItemCount(hListViewHeader);
    for (int i = 0; i < count ;i++)
    {
        Header_SetItem(hListViewHeader,i,&hditem);
    }
}

//Header的新窗口过程
LRESULT CALLBACK NewHeaderWindowProc(
      HWND hwnd,
      UINT uMsg,
      WPARAM wParam,
      LPARAM lParam)
{
//先要用原来的窗口过程处理
    LRESULT result = oldHeaderWindowProc(hwnd,uMsg,wParam,lParam);
//再处理HDM_LAYOUT消息
    if (HDM_LAYOUT == uMsg)
    {
        LPHDLAYOUT phdmlayout = (LPHDLAYOUT)lParam;
        //改变header的高度
        phdmlayout->pwpos->cy = 27;
        //改变listview表格部分的高度
        phdmlayout->prc->top  = phdmlayout->pwpos->cy;
        //将HDF_STRING 重新设置为 HDF_OWNERDRAW
        SetHDItem();
    }

    return result;
}

LRESULT CALLBACK NewListViewWindowProc(
      HWND hwnd,
      UINT uMsg,
      WPARAM wParam,
      LPARAM lParam)
{
    if (uMsg == WM_DRAWITEM)
    {
        DRAWITEMSTRUCT* drawItemStruct = (DRAWITEMSTRUCT*)lParam;
        HDC hdc = drawItemStruct->hDC;
        int index = drawItemStruct->itemID;
        RECT rect = drawItemStruct->rcItem;

        TBrush* brush = new TBrush();
        TColor fontColor = clBlack;
        if (index == 0)
        {
            brush->Color = clRed;
            fontColor    = clWhite;
        }

        if (index == 1)
        {
            brush->Color = clYellow;
            fontColor    = clBlue;
        }

        if (index == 2)
        {
            brush->Color = clBlue;
            fontColor    = clWhite;
        }

        if (index == 3)
        {
            brush->Color = clLime;
            fontColor    = clBlue;
        }

        FillRect(hdc,&rect,brush->Handle);
        delete brush;

        SetTextColor(hdc,fontColor);
        SetBkMode(hdc,TRANSPARENT);
        //获取文本
        HDITEM hditem= {0};
        char buf[100] = {0};
        hditem.mask = HDI_TEXT;
        hditem.pszText = buf;
        hditem.cchTextMax = 100;
        Header_GetItem(hListViewHeader,index,&hditem);

        DrawText(hdc,hditem.pszText,strlen(hditem.pszText),&rect,
        DT_CENTER |DT_VCENTER |DT_SINGLELINE);

    }

    return oldListViewProc(hwnd,uMsg,wParam,lParam);
}

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::FormCreate(TObject *Sender)
{
    hListViewHeader = ListView_GetHeader(ListView1->Handle);

    oldHeaderWindowProc = (WNDPROC)SetWindowLong(
        hListViewHeader,GWL_WNDPROC,(LONG)NewHeaderWindowProc);

    oldListViewProc = (WNDPROC)SetWindowLong(
        ListView1->Handle,GWL_WNDPROC,(LONG)NewListViewWindowProc);

    SetHDItem();

    ListView1->Invalidate();
}
//---------------------------------------------------------------------------
时间: 2024-08-14 06:01:36

TListView Header重绘和高度设置的相关文章

iOS 视图:重绘与UIScrollView(内容根据iOS编程编写)

我们继续之前的 Hypnosister 应用,当用户开始触摸的时候,圆形的颜色会改变. 首先,在 JXHypnosisView 头文件中声明一个属性,用来表示圆形的颜色. #import "JXHypnosisView.h" @interface JXHypnosisView () /** 颜色 */ @property (nonatomic,strong) UIColor * circleColor; @end @implementation JXHypnosisView - (in

android,view的重绘

============问题描述============ mars数独制作视频,页面重绘的时候出现了问题,"Unfortunately,shudu08 has stopped."感觉是某个xml文件没有配置好,求高手帮助纠正错误,顺便告诉下android运行的基本流程.我是纯小白,高手勿喷 进入界面: 点击空白处: 报错: 代码如下: Game.java package com.liuyuan.shudu08; public class Game { private final Str

前端必备的浏览器知识(渲染过程、回流和重绘等)

常用哪几种浏览器测试?有哪些浏览器内核(Rendering Engine)? (Q1)浏览器:Chrome,IE,FireFox,Safari,Opera. (Q2)对应内核:Webkit,Trident,Gecko,Webkit,Presto.(国内的浏览器,除了傲游是直接基于Webkit开发的,其他基本都是基于谷歌在webkit上开发的Chromium,当然谷歌自己也是用的Chromium.另外值得的一提的是手机的系统(安卓.苹果)默认浏览器都是基于webkit内核的) 如何理解浏览器内核?

html文档页面重绘和重新布局

当浏览器下载完所有页面HTML 标记,JavaScript,CSS,图片之后,它解析文件并创建两个内部数据结构:一棵DOM树表示页面结构,一棵渲染树表示DOM节点如何显示. 在对html页面的操作过程中会改变页面的结构或者页面的外观或者同时改变页面的结构和外观.在过程中,文档可见外观改变很小(如:改变了某些元素的颜色,或者改变了某些元素的可见性,但这种改变不会影响到页面的布局),这种行为称为“重绘”,也就是浏览器对文档进行了重新绘制.如果页面外观发生了显著变化并且影响到文档的实际布局(如一个<d

winform重绘

1.重绘文字#多行文字a.先定义一个矩形 Rectangle p1 = new Rectangle(10, 0, 200, this.Height); Rectangle p2 = new Rectangle(210, 0, 200, this.Height); Rectangle p3 = new Rectangle(410, 0, 100, this.Height); b.在矩形中写入文字 TextRenderer.DrawText(g,name,Font,p1,ForeColor,Text

IOS中对图片进行重绘处理的方法总结

一.CGImageRef是什么 CGImageRef是定义在QuartzCore框架中的一个结构体指针,用C语言编写.在CGImage.h文件中,我们可以看到下面的定义: ? 1 typedef struct CGImage *CGImageRef; CGImageRef 和 struct CGImage * 是完全等价的.这个结构用来创建像素位图,可以通过操作存储的像素位来编辑图片. QuartzCore这个框架是可移植的. 二.CGImageRef相关的一些方法解析 CFTypeID CGI

浏览器重排与重绘

前几天内推某街,被问到了酱紫一个问题,了解浏览器的重绘与重排吗?瞬间蒙住了,的确好像没有怎么听说过.于是今天抽了点时间研究了下重排和重绘,这里分享给大家. 浏览器在页面渲染过程中非常重要的两个概念,即重排和重绘.了解这两个概念对于你在今后写代码过程中,尤其是对性能要求比较高的话,有非常大的帮助.来看看这两个概念: 重排(reflow) - 浏览器构建渲染树完成时不包含位置和大小信息.计算元素位置和其他几何信息的过程称为重绘. 重绘(repaint) - 当布局结束后,浏览器遍历呈现树,调用呈现器

关于重绘and重排

在研究CSS3动画性能的时候,看到了重排两个字. 突然想到自己虽然听说过这么个东东,但一直也没深入研究之. 趁着当下正好有研究的劲头,所以一不做二不休,把这个point也给学习了. 同样是一番查找资料...不过这次我得跟作者和原文say sorry了... 因为...我在整理的过程中没注意mark下原文的url...so~~~ 主会原谅我的... 浏览器的工作流程 1.浏览器会解析三个东西: HTML/SVG/XHTML,事实上,Webkit 有三个 C++ 的类对应这三类文档.解析这三种文件会

[Android FrameWork 6.0源码学习] View的重绘过程

View绘制的三部曲,  测量,布局,绘画今天我们分析测量过程 view的测量是从ViewRootImpl发起的,View需要重绘,都是发送请求给ViewRootImpl,然后他组织重绘在重绘的过程中,有一步就是测量,通过代码来分析测量过程 private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp, final Resources res, final int desiredWind