TListView Header重繪和高度設置

TListView 的 Header 部分默認 BtnFace 顏色,高度也不能改變。咱們能夠經過編寫一些代碼來實現這些功能;windows

 

  • 得到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;spa

 

  • 改變Header的高度

Header的高度,是在一個處理一個叫 HDM_LAYOUT 的消息的時候進行設置。code

msdn的原話以下:orm

 

那麼咱們就用SetWindowLong 來改變 Header的 消息過程。blog

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

#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上輸出文字。準確的說,你繪製上去的文字it

會莫名其妙的不見,好比,拉寬一下Header 中的某一列,文字就會消失邊檢。使得這種重繪很差控制;ast

 

正確的作法是這樣form

  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();
}
//---------------------------------------------------------------------------

 

---2016年6月15日更新

 

上個版本雖然實現了效果,可是有一個bug,雙擊列頭中間的分割區域,會致使背景色丟失;

 

解決辦法是,

  1. 在ListView 的消息過程當中,處理 WM_NOTIFY 消息
  2. 在WM_NOTIFY中處理Header發來的HDN_DIVIDERDBLCLICK 消息
  3. WM_NOTIFY消息,必定要在默認消息處理完以後執行
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);


    }

    LRESULT result = oldListViewProc(hwnd,uMsg,wParam,lParam);

    //--雙擊列的分隔區事件
    if (uMsg == WM_NOTIFY)
    {
         NMHDR* nmhdr = (NMHDR*)lParam;
         if (nmhdr != NULL &&
             nmhdr->hwndFrom == hListViewHeader &&
             nmhdr->code == HDN_DIVIDERDBLCLICK)
         {
            //將HDF_STRING 從新設置爲 HDF_OWNERDRAW
            SetHDItem();
         }
    }

    return result;
}

 以後雙擊分隔區的效果

 

相關文章
相關標籤/搜索