Delphi Programming / Object Pascal
[掲載 2022年10月15日] [更新 2022年10月21日] Delphi サンプルプログラム集 |
784_TListView の行の高さを設定 |
動作確認等 |
Windows 10 ビルド 19043 Pro 64 bit + Delphi XE5(UP2) Pro VCL-32 |
|
784_TListView_ItemHeight.zip [2.7 MB] 2022年10月21日版 (EXE 同梱) |
TListView の行の高さを設定,あるいは変更するサンプルです.
本ページのサンプルは,TListView.ViewStyle が vsReport の時の結果です.このプロパティが他の値の時の挙動は異なります.CN_MEASUREITEM メッセージによる方法は,vsReport 以外の時は機能しません. |
01_TImageList を使用して TListView の行の高さを設定 |
TImageList を使用する方法です.
フォームに TImageList を配置して,TListView の StateImages プロパティをその TImageList にします.行の高さは TImageList の Height プロパティで指定します.TListView の行の高さは,( TImageList.Height + 1 ) になります.ただし,TImageList.Height の値が 16 以下の場合は,常に 17 になりました.16 は,TImageList の Widht, Height プロパティのデフォルト値です.
実際の行の高さは,TListView.Font.Size の値によつても異なります.
このサンプルは,Button1 をクリックすると,TImageList.Height の値を 2 ピクセル単位で増加させます.これで実行時に動作を確認できるようにしています.
通常,TListView.SmallImages は,各行,つまり,各項目 (アイテム) に関連したアイコン画像に使用します.そこで,本サンプルでは,TListView.StateImages を使用しています. |
|
図1
EXE 起動時の画面
- TImageList.Height の値は 4
TImageList.Widht の値は 1
- 行の高さは 17 となる
- TListView.Font.size の値によっても異なる
|
|
図2
TImageList.Height の値を変更
- TImageList.Height が 16 以下の場合,
行の高さは 17 になる
- TImageList.Height が 17 以上の場合,
行の高さは TImageList.Height + 1 になる
|
リスト1
TImageList を使用して TListView の行の高さを設定 |
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls,
Vcl.ImgList, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
ListView1: TListView;
ImageList1: TImageList;
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses
Winapi.CommCtrl;
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
LCol : TListColumn;
LItem : TListItem;
LIndex : Integer;
begin
ListView1.ViewStyle := vsReport;
// 行の高さを設定
ImageList1.Height := 4;
ImageList1.Width := 1;
ListView1.StateImages := ImageList1;
ListView1.Items.BeginUpdate;
LCol := ListView1.Columns.Add;
LCol.Caption := 'Test Items';
LCol.Width := 200;
// テスト表示用のデータ
for LIndex := 0 to 49 do begin
LItem := ListView1.Items.Add;
LItem.Caption := Format(' %.6d',[LIndex + 1]);
end;
ListView1.Items.EndUpdate;
Button1Click(nil);
end;
//=============================================================================
// ListView1 の行の高さを変更
//
// ImageList を使用した場合,最小値があるので,それ以下の値は設定できない
// 本サンプルでは 17 なので,実用的には問題ないと思われる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LRect : TRect;
begin
if Sender <> nil then begin
ImageList1.Height := ImageList1.Height + 2;
end;
// ListView の 1 番目 ( インデックスが 0 ) の行の高さを取得して表示
// uses に Winapi.CommCtrl が必要
LRect := ListView1.Items[0].DisplayRect(drBounds);
Panel1.Caption := 'ItemHeight : ' + IntToStr(LRect.Bottom - LRect.Top);
end;
end.
上のサンプルの実行結果ては,TListView の各行の文字列の描画開始位置の左端にマージンがあります.このマージンは TImagaeList を使用することで起こります.これを回避するには,TListView.OwnerDraw プロパティを True にして,オーナ描画します. |
|
図3
任意の位置に描画 |
procedure TForm1.ListView1DrawItem(Sender: TCustomListView; Item: TListItem;
Rect: TRect; State: TOwnerDrawState);
var
LRect : TRect;
LText : string;
begin
LRect := Rect;
LText := Item.Caption;
ListView1.Canvas.TextRect(LRect, LText, [tfVerticalCenter, tfSingleLine]);
end;
02_CN_MEASUREITEM を使用して TListView の行の高さを設定 |
CN_MEASUREITEM メッセージを使用する方法です.この方法は,TImageList を使用する必要がない場合に有効な方法です.
TListView は,Windows のコモンコントロールである ListView のラッパークラスです.Windows のコモンコントロールは,コントロールを生成する時に,自分自身に通知メッセージを送信します.Delphi ではこれを CN_MEASUREITEM メッセージで捕捉できます.
このメッセージの TWMMeasureItem レコード化に ItemHeight というメンバがあります.この値を設定します.
CN_MEASUREITEM は,コントロールか生成された時の通知メッセージですから,実行時に行の高さを変更するには,コントロールの再生成が必要です.本サンプルでは RecreateWnd 手続きで再生成しています.
行の高さは,TListView に ItemHeight というプロパティを追加して使用しています.
このサンプルは,Button1 をクリックすると,ItemHeight プロパティの値を2 ピクセル単位で増加させます.これで実行時に動作を確認できるようにしています. |
|
図4
EXE 起動時の画面
- TListView.ItemHeight の値は 4
- この値が TListView の行の高さ
|
|
図5
TLisview.ItemHeight の値を変更
- 20 の場合
- この値が TListView の行の高さ
|
TListView の StateImages ゃ SmallImages プロパティに TImageList を指定すると,行の高さは TImageList の Height プロパティの値によって決まります.
CN_MEASUREITEM メッセージによる行の高さの設定を有効にしたままで TListView にアイコン画像などを描画したい場合は,Draw 関係のイベント内で画像を描画します. |
リスト2
CN_MEASUREITEM メッセージを使用して TListView の行の高さを設定 |
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls,
Vcl.ExtCtrls;
type
TListView = class(Vcl.ComCtrls.TListView)
private
FItemHeight : UInt;
procedure SetItemHeight(const Value: UInt);
protected
procedure CNMeasureitem(var Message: TWMMeasureItem); message CN_MEASUREITEM;
public
property ItemHeight : UInt read FItemHeight write SetItemHeight;
end;
TForm1 = class(TForm)
Button1: TButton;
ListView1: TListView;
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses
Winapi.CommCtrl;
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
//
// ここで表示している ItemHeight の値は ListView.ItemHeight の値と同じ
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
LCol : TListColumn;
LItem : TListItem;
LIndex: Integer;
begin
ListView1.ViewStyle := vsReport;
ListView1.OwnerDraw := True;
ListView1.ItemHeight := 4;
ListView1.Items.BeginUpdate;
LCol := ListView1.Columns.Add;
LCol.Caption := 'Test Items';
LCol.Width := 200;
// テスト表示用のデータ
for LIndex := 0 to 49 do begin
LItem := ListView1.Items.Add;
LItem.Caption := Format(' %.6d',[LIndex + 1]);
end;
ListView1.Items.EndUpdate;
Button1Click(nil);
end;
//=============================================================================
// ListView1 の行の高さを変更
//
// ここで表示している ItemHeight の値は ListView.ItemHeight の値と同じ
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LRect : TRect;
begin
if Sender <> nil then begin
ListView1.ItemHeight := ListView1.ItemHeight + 2;
end;
// ListView の 1 番目 ( インデックスが 0 ) の行の高さを取得して表示
// uses に Winapi.CommCtrl が必要
LRect := ListView1.Items[0].DisplayRect(drBounds);
Panel1.Caption := 'ItemHeight : ' + IntToStr(LRect.Bottom - LRect.Top);
end;
{ TListView }
//----------------------------------------------- -------------------------
// TListView の CN_MEASUREITEM メッセージ処理
// CN_MEASUREITEM は,コントロール生成時に自身に送信されてくるメッセージ
// ItemHeight の値を設定
// OwnerDraw の値が True でないと機能しない
//----------------------------------------------- -------------------------
procedure TListView.CNMeasureitem(var Message: TWMMeasureItem);
begin
inherited;
Message.MeasureItemStruct.itemHeight := FItemHeight;
end;
//----------------------------------------------- -------------------------
// ItemHeight プロパティの設定用メソッド
//----------------------------------------------- -------------------------
procedure TListView.SetItemHeight(const Value: UInt);
var
LTopIndex : Integer;
begin
if Value <> FItemHeight then begin
FItemHeight := Value;
if Items.Count > 1 then begin
LTopIndex := TopItem.Index;
Selected := nil;
RecreateWnd;
Scroll(0, LTopIndex * FItemHeight);
end;
end;
end;
end.
このサンプルでは,以下のクラス宣言を使用しています.これは,ユニット内の TListView に対してだけ有効です.元のクラスとは機能が異なるわけですから,通常は,元のクラス名とは異なるクラス名の継承クラスを宣言し,実行時に生成して使用します.
今回は,行の高さ設定の動作確認だけが目的なので,この宣言を使用しています. |
type
TListView = class(Vcl.ComCtrls.TListView)
TListView の行の高さは,TListItem.DiaplayRect メソッド,あるいは ListView_GetItemRect関数で取得できます.実際に取得するのは Item の TRect 構造体の値ですが,この値から ( (LRect.Bottom - LRect.Top ) で計算できます.LIndex は,Item のインデックス ( 0 ペース ) の値です.TListView では,全ての行が同じ高さですから,この値は 0 で構いません.
これらのメソッド,関数は,TListView に LVM_GETITEMRECT メッセージを送信して値を取得しています.このコードをコンパイルするには,uses に Winapi.CommCtrl が必要です. |
procedure TForm1.Button1Click(Sender: TObject);
var
LRect : TRect;
LIndex : Integer;
begin
LIndex := 0;
LRect := ListView1.Items[LIndex].DisplayRect(drBounds);
// 行の高さ = LRect.Bottom - LRect.Top
end;
procedure TForm1.Button1Click(Sender: TObject);
var
LRect : TRect;
LIndex : Integer;
begin
LIndex := 0;
ListView_GetItemRect(ListView1.Handle, LIndex, LRect, LVIR_BOUNDS);
// 行の高さ = LRect.Bottom - LRect.Top
end;
04_TListView のカラムヘッダの高さの取得と設定 |
TListView のカラムヘッダのの高さは,次のようなコードで取得できます.
このコードをコンパイルするには.uses に Winapi.CommCtrl が必要です. |
procedure TForm1.Button1Click(Sender: TObject);
var
LhHeader : THandle;
LRect : TRect;
begin
LhHeader := ListView_GetHeader(ListView1.Handle);
if (ListView1.Columns.Count > 0) then begin
Header_GetItemRect(LhHeader, 0, @LRect);
// カラムヘッダの高さ = LRect.Bottom - LRect.Top
end;
end;
以下は,TListView のカラムヘッダの高さを設定するサンプルです.
TListView のカラムヘッダ部の描画は,TCustomListView.HeaderWndProc メソッドで行われています.このメソッドは,private 部にあるので,外部からは利用できません.このサンプルでは,カラムヘッダのコントロールをサブクラス化して処理しています.
カラムヘッダの生成時に,カラムヘッがダが送信する HDM_LAYOUTメッセージで処理します.実行中に変更したい場合は,CN_MEASUREITEM のサンプルのように,ウィンドウの再生成が必要です. |
|
図6
カラムヘッダ設定コードがない場合
- ヘッダ部にマウスカーソルがある状態
- カラムヘッダ部の高さは 24
|
|
図7
カラムヘッダの高さを設定した場合
- ヘッダ部にマウスカーソルがある状態
- カラムヘッダ部の高さは 70
|
リスト3
HDM_LAYOUT メッセージを処理して,TListView のカラムヘッダの高さを設定 |
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls,
Vcl.ExtCtrls;
type
TForm1 = class(TForm)
ListView1: TListView;
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
function LVHeaderWndProc(hWnd: HWND; Msg: UINT; wParam: WPARAM;
lParam: LPARAM): LRESULT; stdcall;
var
Form1: TForm1;
implementation
uses
Winapi.CommCtrl,
System.Types;
{$R *.dfm}
var
FLVHOriginProc : Pointer;
FhLVHeader : THandle;
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
LCol : TListColumn;
LItem : TListItem;
LIndex: Integer;
begin
ListView1.ViewStyle := vsReport;
ListView1.Items.BeginUpdate;
LCol := ListView1.Columns.Add;
LCol.Caption := 'Test Items';
LCol.Width := 200;
// テスト表示用のデータ
for LIndex := 0 to 49 do begin
LItem := ListView1.Items.Add;
LItem.Caption := Format(' %.6d',[LIndex + 1]);
end;
ListView1.Items.EndUpdate;
// カラムヘッダのコントロールのウィンドウハンドルを取得
FhLVHeader := ListView_GetHeader(ListView1.Handle);
// カラムヘッダコントロールの WndProc を退避し,新しく WndProc を設定
FLVHOriginProc := Pointer(SetWindowLong(FhLVHeader,
GWL_WNDPROC,
NativeInt(@LVHeaderWndProc)));
end;
//=============================================================================
// フォームを閉じる時の処理
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
SetWindowLong(FhLVHeader, GWL_WNDPROC, NativeInt(FLVHOriginProc));
end;
//-----------------------------------------------------------------------------^
// SetWindowLong 関数のコールバック関数
// TListView のカラムヘッダコントロールのサブクラス化処理
//
// HDM_LAYOUT メッセージを捕捉して処理
// PHDLayout 構造体のPHDLayout.WindowPos.cy がヘッダ部の高さ
//-----------------------------------------------------------------------------^
function LVHeaderwndProc(hWnd: HWND; Msg: UINT; wParam: WPARAM;
lParam: LPARAM): LRESULT; stdcall;
var
LHDLyaout : PHDLayout;
LRect : TRect;
begin
Result := CallWindowProc(FLVHOriginProc, hWnd, Msg, wParam, lParam);
if Msg = HDM_LAYOUT then begin
LHDLyaout := PHDLayout(lParam);
LHDLyaout.WindowPos.cy := 70;
LHDLyaout.Rect.Top := LHDLyaout.WindowPos.cy + 2;
// ヘッダ部の高さを取得して表示
Header_GetItemRect(FhLVHeader, 0, @LRect);
Form1.Panel1.Caption := 'Header Height : ' + IntToStr(LRect.Bottom - LRect.Top);
end;
end;
end.
|