TListViewでViewStyleが vsReportの設定の時に行の高さを変えるために
TImageListを作ってHeigthに行の高さを指定してSmallImagesに割り当てると思惑通り高さが変わりました。
ところが高さの値を少なくしていくと
行として表示可能な限界値があるようで、ある一定の値から反映されなくなります。
表示が潰れない親切な設計ですが、行の高さを計算しているため実測値と異なってしまいます。
環境にもよりますが高さ 10をTImageListのHeigthに設定すると、実際の高さは 20ほどになりました。
実測値を取得する方法はあるのでしょうか?
OnDrawItemイベントでRectが渡されるのでこれが実際の値ですが
OnDrawItemイベント前に値が必要な場合もあるのでこの方法以外で取得できればいいのですが
うーむ,趣旨が理解し難いというか,何と言ったらいいのか.
以下のコードを試してみてください.
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;
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;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure ListView1DrawItem(Sender: TCustomListView; Item: TListItem;
Rect: TRect; State: TOwnerDrawState);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
//
// 本サンプルの動作確認は以下
// Windows 10 ビルド 19043 Pro 64 bit + Delphi XE5(UP2) Pro VCL-32
//=============================================================================
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;
end;
//=============================================================================
// ListView1 の行の高さを変更
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
ListView1.ItemHeight := ListView1.ItemHeight + 2;
end;
//=============================================================================
// ListView1 の OnDrawItem イベント
//=============================================================================
procedure TForm1.ListView1DrawItem(Sender: TCustomListView; Item: TListItem;
Rect: TRect; State: TOwnerDrawState);
begin
ListView1.Canvas.TextOut(Rect.Left, Rect.Top, Item.Caption);
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でセルに編集機能を持たせるためTEditを置いて編集するクラスを制作しています
【困っていること】
クリックしたときにそのセルのサイズに合わせてTEditの位置を変えています。
しかし Topの座標指定がずれることがあります。
調べていくと、TListViewにImageListを関連づけして、ImageListの高さを変えて TListViewの行の高さを変更する方法だと
表示可能な行よりも低く設定された場合、TListView は ImageListの Heightの値では無く表示可能な高さで表示されます。
そのためズレるようです
ImageListを使わずに 設定値通りの高さで表示する方法
または
実際に表示されている行の高さが取得出来れば助かります。
クリックしたときにTEditをそのセルの位置へ移動させるサンプルを作りました。
【環境】
OS :Windows10
コンパイラ DelphiXE5、Delphi10.4
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ImgList,
Vcl.ComCtrls, Vcl.StdCtrls,CommCtrl;
type
TListViewEx = class(TListView)
private
{ Private 宣言 }
FImages : TImageList;
FEdit : TEdit;
FItemheight : Integer;
function TopIndex() : Integer;
function ColumnLeft(const aCol : Integer) : Integer;
function GetScrollBarLeft() : Integer;
function GetColumAt(const X,Y : Integer) : Integer;
function GetColumnHeight(): Integer;
//function GetItemHeight: Integer;
procedure SetItemHeight(const Value: Integer);
procedure OnSelfClick(Sender: TObject);
procedure OnSelfDrawItem(Sender: TCustomListView; Item: TListItem;
Rect: TRect; State: TOwnerDrawState);
function GetColumn: Integer;
protected
procedure CNMeasureitem(var Message: TWMMeasureItem); message CN_MEASUREITEM;
public
{ Public 宣言 }
constructor Create(AOwner: TComponent); override;
destructor Destroy;override;
procedure Clear();override;
property ItemHeight : Integer read FItemHeight write SetItemHeight;
property Column : Integer read GetColumn;
end;
type
TForm8 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormShow(Sender: TObject);
private
{ Private 宣言 }
FListView : TListViewEx;
public
{ Public 宣言 }
end;
var
Form8: TForm8;
implementation
{$R *.dfm}
{ TListViewEx }
procedure TListViewEx.CNMeasureitem(var Message: TWMMeasureItem);
begin
inherited;
Message.MeasureItemStruct.itemHeight := FItemHeight;
end;
function TListViewEx.ColumnLeft(const aCol: Integer): Integer;
var
i,x: Integer;
begin
result := 0;
if aCol = 0 then exit;
x := 0;
for i := 0 to aCol-1 do begin
x := x + Columns[i].Width;
end;
result := x;
end;
constructor TListViewEx.Create(AOwner: TComponent);
begin
inherited;
FImages := TImageList.Create(Self);
OnClick := OnSelfClick;
OnDrawItem := OnSelfDrawItem;
OwnerDraw := True;
ItemHeight := 11; // 行の高さを指定
FEdit := TEdit.Create(Self);
FEdit.Parent := Self;
FEdit.Visible := False;
end;
destructor TListViewEx.Destroy;
begin
FEdit.Free;
FImages.Free;
inherited;
end;
procedure TListViewEx.Clear;
begin
Items.Clear;
FImages.Clear;
end;
function TListViewEx.GetColumAt(const X, Y: Integer): Integer;
var
i,xx : Integer;
begin
result := -1;
xx := -GetScrollBarLeft(); // 基準値をスクロールバーの現在位置から取得
for i := 0 to Columns.Count-1 do begin // 列数ループ
xx := xx + Columns[i].Width; // 基準値に列幅を加算
if X < xx then begin // カーソルが列内にある場合
result := i; // カーソルの列位置として返す
exit; // 処理終了
end;
end;
end;
function TListViewEx.GetColumn: Integer;
var
p : TPoint;
begin
p := ScreenToClient(Mouse.CursorPos); // マウス位置取得
result := GetColumAt(p.X,p.Y); // マウス位置から列を取得
end;
function TListViewEx.GetColumnHeight: Integer;
var
Header_Handle: HWND;
WindowPlacement: TWindowPlacement;
begin
Header_Handle := ListView_GetHeader(Handle);
FillChar(WindowPlacement, SizeOf(WindowPlacement), 0);
WindowPlacement.Length := SizeOf(WindowPlacement);
GetWindowPlacement(Header_Handle, @WindowPlacement);
Result := WindowPlacement.rcNormalPosition.Bottom -
WindowPlacement.rcNormalPosition.Top+1;
end;
{
function TListViewEx.GetItemHeight: Integer;
begin
if SmallImages<>nil then begin
result := FImages.Height; // ここで実際の行の高さが取得出来るのが理想
end
else begin
result := 16;
end;
end;
}
function TListViewEx.GetScrollBarLeft: Integer;
var
SInfo: TScrollInfo;
begin
SInfo.cbSize := SizeOf(SInfo);
SInfo.fMask := SIF_ALL;
GetScrollInfo(Handle, SB_HORZ, SInfo);
result := SInfo.nPos;
end;
procedure TListViewEx.OnSelfClick(Sender: TObject);
var
aCol : Integer;
begin
aCol := GetColumn;
if aCol=-1 then exit;
FEdit.Left := (ColumnLeft(aCol) - GetScrollBarLeft());
FEdit.Top := (ItemIndex - TopIndex) * (ItemHeight+1) +GetColumnHeight(); // 問題の部分
FEdit.Width := Columns[aCol].Width;
FEdit.Height := ItemHeight;
FEdit.Visible := True;
end;
procedure TListViewEx.OnSelfDrawItem(Sender: TCustomListView; Item: TListItem;
Rect: TRect; State: TOwnerDrawState);
var
i: Integer;
r : Trect;
cv : TCanvas;
begin
r := Rect;
cv := Canvas;
if (odFocused in State) or (odSelected in State) then begin
cv.Brush.Color := clActiveCaption;
cv.Font.Color := clWhite;
end
else begin
cv.Brush.Color := clWhite;
cv.Font.Color := clBlack;
end;
cv.Brush.Style := bsSolid;
cv.FillRect(Rect);
cv.Brush.Style := bsClear;
r.Right := Columns[0].Width;
Canvas.TextOut(r.Left, r.Top, Item.Caption);
for i := 0 to Item.SubItems.Count-1 do begin
r.Left := r.Left +Columns[i].Width;
r.Right :=r.Left + Columns[i+1].Width;
Canvas.TextOut(r.Left, r.Top, Item.SubItems[i]);
end;
end;
procedure TListViewEx.SetItemHeight(const Value: Integer);
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;
// ImageListで行の高さを変更する処理
SmallImages := nil;
FImages.Height := Value;
FImages.Width := Value;
SmallImages := FImages;
end;
function TListViewEx.TopIndex: Integer;
var
SInfo: TScrollInfo;
begin
SInfo.cbSize := SizeOf(SInfo);
SInfo.fMask := SIF_ALL;
GetScrollInfo(Handle, SB_VERT, SInfo);
result := SInfo.nPos;
end;
procedure TForm8.FormCreate(Sender: TObject);
var
dc : TListColumn;
begin
FListView := TListViewEx.Create(Self);
FListView.Parent := Self;
FListView.Align := alClient;
FListView.ViewStyle := vsReport;
FListView.RowSelect := True;
dc := FListView.Columns.Add;
dc.Caption := '列1';
dc.Width := 100;
dc := FListView.Columns.Add;
dc.Caption := '列2';
dc.Width := 200;
end;
procedure TForm8.FormDestroy(Sender: TObject);
begin
FListView.Free;
end;
procedure TForm8.FormShow(Sender: TObject);
var
dl : TListItem;
i: Integer;
begin
for i := 0 to 30 do begin
dl := FListView.Items.Add;
dl.Caption := IntToStr(i+1)+'行目';
dl.SubItems.Add('データ1');
end;
end;
試していませんが
高さを取得したいListItemが分かるなら、TListItem.DisplayRect メソッドとか?
ListView_GetItemRectを使っているので、直接呼んでみる(可視範囲内のListItemのIndexが必要かな?)
https://learn.microsoft.com/ja-jp/windows/win32/api/commctrl/nf-commctrl-listview_getitemrect
> ImageListを使わずに 設定値通りの高さで表示する方法
私が提示したコードは TImageLIst ( ImageList ? ) を使用していませんが・・・
私の提示したコードは「高さの表示」ではなく,「高さの設定」でした.
お役には立たなかったようですね.
調剤薬局マンさん、ありがとうございました ListView_GetItemRectを直接呼ぶ事で解決しました。
Mr.XRAY さん、単なる説明不足で誤解させてしまいました、
ListView_GetItemRectで検索したら Mr.XRAY さんのWebサイトが出てきて、サンプルを参考にさせていただきました。
【072_TListView のスクロールバーの表示制御】
http://mrxray.on.coocan.jp/Delphi/plSamples/072_TListView_ScrollBar.htm
さきほどのサンプルを修正したものはこちらです。
この修正では高さに制限が無く指定した高さに設定されるようになりました。
挙動は変わりましたが、これで問題が無いので解決とさせて頂きます。
ありがとうございました。
procedure TListViewEx.OnSelfClick(Sender: TObject);
var
aCol : Integer;
r : TRect;
begin
aCol := GetColumn;
if aCol=-1 then exit;
ListView_GetItemRect(Handle,ItemIndex,r,LVIR_BOUNDS); // 追加
FEdit.Left := (ColumnLeft(aCol) - GetScrollBarLeft());
//FEdit.Top := (ItemIndex - TopIndex) * (ItemHeight+1) +GetColumnHeight(); // 修正前
FEdit.Top := (ItemIndex - TopIndex) * (r.Height) +GetColumnHeight(); // 修正後
FEdit.Width := Columns[aCol].Width;
//FEdit.Height := ItemHeight; // 修正前
FEdit.Height := r.Height; // 修正後
FEdit.Visible := True;
end;
ツイート | ![]() |