Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2015年07月27日] [更新 2015年11月07日] Delphi サンプルプログラム集
072_TListView のスクロールバーの表示制御
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro, Delphi XE5(UP2) Pro VCL-32
072_TListView_ScrollBar.zip [5,061 KB] 2015年11月07日版 (EXE 同梱)



  • 2010年09月15日
  • VCL スタイルを使用した場合の動作確認用サンプルを追加




TListView のスクロールバーの表示制御


TListView のスクロールバーの表示制御関係のサンプルです.

TListView には,TStringGrid や TRichEdit のような,ScrollBars プロパティはありません.
TListView では,クライアント領域にデータが表示できなくなると,必要に応じて水平スクロールバーまたは垂直スクロールバーが現れます.
本ページのサンプルは,TListView の継承クラス (コンポーネント) を作成し,これらのスクロールバーを強制的に表示したり,非表示にするサンプルです.

本ページのサンプルでは,継承クラスをコンポーネントとして登録 (インストール) しないで,[派生元のコンポーネントと交換する方法] で説明しているユニットを使用しています.



[備考]
サンプルプログラム集に ShowScrollBar 関数を使用したサンプルがあります.これは,Windows 2000 時代に作成したコードをそのまま流用しています.ShowScrollBar は,Windows 7 では正常に動作しない場合があります.





01_TListView のスクロールバーの表示と非表示を切り換え


TListView の継承クラス (コンポーネント) を作成して,水平スクロールバー,垂直スクロールバーを常に表示または常に非表示するようにしたサンプルです.
Button1 のクリックで表示と非表示を切り換えるようにしています.

TListView が生成,再生成すると RecreateWnd が実行されます.RecreateWnd は WM_NCCALCSIZE メッセージを発行します.このメッセージ処理内で表示スタイルを設定しています.
本サンプルでは,TListView の継承クラスに HideScrollBars というプロパティを実装して,このプロパティの値を Button1 で切り換えるようにしています.
垂直スクロールバーだけ,あるいは水平スクロールバーだけの表示制御は,スタイルの設定フラグによって変更できます.




図1
設計時の画面
図2
実行開始画面
  • 水平スクロールバー,垂直スクロールバーが共に表示されている
図3
縦方向にリサイズ
  • 垂直スクロールバーは表示されているが,操作はできない
図4
Button1 をクリック
  • スクロールバーが非表示となる

リスト1
TListView のスクロールバーの表示と非表示を切り換え
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, Grids, StdCtrls, ComCtrls, CommCtrl;

type
  TplListView = class(TListView)
  private
    FHideScrollBars : Boolean;
    procedure SetHideScrollBars(const Value: Boolean);
  protected
    procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE;
  public
    property HideScrollBars : Boolean read FHideScrollBars write SetHideScrollBars;
  end;

  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    ListView1: TListView;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    plListView1 : TplListView;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  plChgCompo;

{$R *.dfm}

//=============================================================================
//  フォームのOnCreateイベント処理
//  TListViewの派生クラス(コンポーネント)を生成
//  すぐにテストできるようにテスト用のデータをセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  i     : Integer;
  LCol  : TListColumn;
  LItem : TListItem;
begin
  //フォーム作成時にコンポーネントの入れ替え
  plListView1 := TplListView(ChangeComponent(ListView1, TplListView));

  //TListViewのスタイル
  plListView1.ViewStyle := vsReport;

  plListView1.Items.BeginUpdate;
  //カラムを作成
  for i := 0 to 3 do begin
    LCol := plListView1.Columns.Add;
    LCol.Caption := 'COL-' + IntToStr(i);
    LCol.Width   := 90;
  end;

  //Itemのデータをセット
  for i := 0 to 9 do begin
    LItem :=  plListView1.Items.Add;
    LItem.Caption := Format('    %.6d',[i + 1]);
  end;
  plListView1.Items.EndUpdate;
end;

//=============================================================================
//  TListViewの継承クラスに実装したHideScrollBarsプロパティをトグル操作
//  HideScrollBarsがTrueだと水平,垂直スクロールバーは共に非表示(Hide)になる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  plListView1.HideScrollBars := not plListView1.HideScrollBars;
end;

{ TplListView }

//-----------------------------------------------------------------------------
//  TListViewの継承クラスに実装したHideScrollBarsプロパティの設定メソッド
//  変更したらRecreateWndを実行する
//  RecreateWndを実行するとWM_NCCALCSIZEメッセージが発生する
//-----------------------------------------------------------------------------
procedure TplListView.SetHideScrollBars(const Value: Boolean);
begin
  if FHideScrollBars <> Value then begin
    FHideScrollBars := Value;
    RecreateWnd;
  end;
end;

//=============================================================================
//  WM_NCCALCSIZEメッセージ処理
//  スクロールバー表示のフラグを強制的に変更
//=============================================================================
procedure TplListView.WMNCCalcSize(var Message: TWMNCCalcSize);
var
  LStyle : Integer;
begin
  LStyle := GetWindowLong(Handle, GWL_STYLE);
  if FHideScrollBars then begin
    LStyle := LStyle and not WS_HSCROLL and not WS_VSCROLL;
  end else begin
    LStyle := LStyle or WS_HSCROLL or WS_VSCROLL;
  end;
  SetWindowLong(Handle, GWL_STYLE, LStyle);

  inherited;
end;

end.




02_TListView のスクロールバーの無効化表示


TListView のスクロールバーは,操作不可の状態になっても,無効化の表示とはなりません.
これを無効化の表示状態にするサンプルです.

リサイズ以外にも,アイテムの追加,削除,あるいはカラムの変更のチェックが必要です.これらをチェックする処理も追加してみました.



[備考]
TListView のメッセージとして,MSDN の [List View Messages] の記事に LVM_XXXX という形式がありますが,これらのメッセージは,情報の取得や設定に使用するメッセージです.LVM_XXX 処理のメッセージを受信するには,LVM_XXX 受信用のプロシージャーを実装します.
もし,すでに VCL 内で LVM_XXX 受信用のプロシージャーが実装されている場合は,inherited 処理が必要です.
処理の結果としてのメッセージは,[List View Notifications] の記事にある LVN_XXXX 等の通知メッセージが使用できます.




図5
EXE の実行開始画面
図6
フォームを縦方向にリサイズ
  • 垂直スクロールバーが無効化の状態になる
図7
カラムの幅を狭く (小さく) してみる
  • 横方向のデータが全て表示されている状態
  • 水平スクロールバーが無効化される

リスト2
TListView のスクロールバーの無効化表示
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, Grids, StdCtrls, ComCtrls, CommCtrl;

type
  TplListView = class(TListView)
  private
    procedure Available_Disable_ScrollBars;
  protected
    procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE;
    procedure Resize; override;
    procedure CNNotify(var Message: TWMNotifyLV); message CN_NOTIFY;
    procedure WMNotify(var Message: TWMNotify); message WM_NOTIFY;
  public
  end;

  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    ListView1: TListView;
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    plListView1 : TplListView;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  plChgCompo;

{$R *.dfm}

//=============================================================================
//  フォームのOnCreateイベント処理
//  TListViewの派生クラス(コンポーネント)を生成
//  すぐにテストできるようにテスト用のデータをセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  i     : Integer;
  LCol  : TListColumn;
  LItem : TListItem;
begin
  //フォーム作成時にコンポーネントの入れ替え
  plListView1 := TplListView(ChangeComponent(ListView1, TplListView));

  //TListViewのスタイル
  plListView1.ViewStyle := vsReport;

  plListView1.Items.BeginUpdate;
  //カラムを作成
  for i := 0 to 3 do begin
    LCol := plListView1.Columns.Add;
    LCol.Caption := 'COL-' + IntToStr(i);
    LCol.Width   := 90;
  end;

  //Itemのデータをセット
  for i := 0 to 9 do begin
    LItem :=  plListView1.Items.Add;
    LItem.Caption := Format('    %.6d',[i + 1]);
  end;
  plListView1.Items.EndUpdate;
end;

{ TplListView }

//-----------------------------------------------------------------------------
//  スクロールバーの有効化表示と無効化表示の制御用メソッド
//-----------------------------------------------------------------------------
procedure TplListView.Available_Disable_ScrollBars;
var
  i            : Integer;
  LTopItemRect : TRect;
  LItemHeight  : Integer;
  LTotalHeight : Integer;
  LTotalWidth  : Integer;
begin
  if TopItem = nil then exit;

  //TopItemの矩形領域を取得
  ListView_GetItemRect(Handle, TopItem.Index, LTopItemRect, LVIR_BOUNDS);

  //Itemの高さの合計を計算
  LItemHeight  := LTopItemRect.Bottom - LTopItemRect.Top;
  LTotalHeight := Items.Count * LItemHeight + LItemHeight div 2;
  if ShowColumnHeaders then begin
    LTotalHeight := LTotalHeight + LItemHeight;
  end;

  //Itemの高さの合計がクライアント領域の高さよリ短い時は垂直スクロールバーを無効化
  if LTotalHeight < ClientHeight then begin
    EnableScrollBar(Handle, SB_VERT, ESB_DISABLE_BOTH);
  end else begin
    EnableScrollBar(Handle, SB_VERT, ESB_ENABLE_BOTH);
  end;


  //カラムの幅の合計値を計算
  LTotalWidth := 0;
  for i := 0 to Columns.Count - 1 do begin
    LTotalWidth := LTotalWidth + Column[i].Width;
  end;
  LTotalWidth := LTotalWidth + 10;

  //カラムの幅の合計がクライアント領域の幅より短い時は水平スクロールバーを無効化
  if LTotalWidth < ClientWidth then begin
    EnableScrollBar(Handle, SB_HORZ, ESB_DISABLE_BOTH);
  end else begin
    EnableScrollBar(Handle, SB_HORZ, ESB_ENABLE_BOTH);
  end;
end;

//=============================================================================
//  Resizeメソッド
//  コントロールがリサイズされると自動的に実行されるメソッド(OnResize相当)
//=============================================================================
procedure TplListView.Resize;
begin
  inherited;
  Available_Disable_ScrollBars;
end;

//=============================================================================
//  WM_NOTIFYメッセージ処理
//  HDN_XXX通知メッセージ(カラム関係の通知メッセージ)を処理
//=============================================================================
procedure TplListView.WMNotify(var Message: TWMNotify);
var
  LCode : Integer;
begin
  inherited;

  LCode := Message.NMHdr^.code;
  case LCode of
    HDN_ITEMCHANGED:
    begin
      Available_Disable_ScrollBars;
    end;
  end;
end;

//=============================================================================
//  CN_NOTIFYメッセージ処理
//  LVN_XXX通知メッセージ(アイテム関係の通知メッセージ)を処理
//=============================================================================
procedure TplListView.CNNotify(var Message: TWMNotifyLV);
var
  LCode : Integer;
begin
  inherited;

  LCode := Message.NMHdr.code;
  case LCode of
    LVN_DELETEITEM,
    LVN_INSERTITEM:
    begin
      Available_Disable_ScrollBars;
    end;
  end;
end;

//=============================================================================
//  WM_NCCALCSIZEメッセージ処理
//  スクロールバー表示のフラグを強制的に変更
//=============================================================================
procedure TplListView.WMNCCalcSize(var Message: TWMNCCalcSize);
var
  LStyle : Integer;
begin
  LStyle := GetWindowLong(Handle, GWL_STYLE);
  SetWindowLong(Handle, GWL_STYLE, LStyle or WS_HSCROLL or WS_VSCROLL);

  inherited;
end;

end.




03_TListView のスクロールバーのコンテキストメニュー


TListView のスクロールバー上で,マウスの右ボタンをクリックすると,下図のようなコンテキストメニューが現れます.このコンテキストメニューを表示しないようにする方法です.

コンテキストメニューが表示する際,WM_CONTEXTMENU メッセージが発生します.このメッセージ処理内で何もしなければコンテキストメニューは現れません.
何もしないと,TListView で TPopupMenue コンポーネント等を使用していると,そのポップアップメニューも表示しなくなってしまいます.そこで,マウスクリックの位置が,TListView のクライアント領域の時は,WM_CONTEXTMENU メッセージ本来の処理を実行するようにしています.




図8
スクロールバーのコンテキストメニュー
  • このコンテキストメニューを表示しないようにする

リスト3
TListView のスクロールバーのコンテキストメニューを表示しないようにする
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, Grids, StdCtrls, ComCtrls, CommCtrl;

type
  TplListView = class(TListView)
  protected
    procedure WMContextMenu(var Message: TWMContextMenu);
      message WM_CONTEXTMENU;
  end;

  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    ListView1: TListView;
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    plListView1 : TplListView;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  plChgCompo,
  Types;

{$R *.dfm}

//=============================================================================
//  フォームのOnCreateイベント処理
//  TListViewの派生クラス(コンポーネント)を生成
//  すぐにテストできるようにテスト用のデータをセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  i     : Integer;
  LCol  : TListColumn;
  LItem : TListItem;
begin
  //フォーム作成時にコンポーネントの入れ替え
  plListView1 := TplListView(ChangeComponent(ListView1, TplListView));

  //TListViewのスタイル
  plListView1.ViewStyle := vsReport;

  plListView1.Items.BeginUpdate;
  //カラムを作成
  for i := 0 to 3 do begin
    LCol := plListView1.Columns.Add;
    LCol.Caption := 'COL-' + IntToStr(i);
    LCol.Width   := 90;
  end;

  //Itemのデータをセット
  for i := 0 to 9 do begin
    LItem :=  plListView1.Items.Add;
    LItem.Caption := Format('    %.6d',[i + 1]);
  end;
  plListView1.Items.EndUpdate;
end;

{ TplListView }

//=============================================================================
//  WM_CONTEXTMENUメッセージ処理
//  スクロールバーのコンテキストメニューを表示しないようにする
//=============================================================================
procedure TplListView.WMContextMenu(var Message: TWMContextMenu);
var
  LPos : TPoint;
begin
  LPos := ScreenToClient(Point(Message.XPos, Message.YPos));
  if PtInRect(ClientRect, LPos) then begin
    inherited;
  end else begin
    Message.Result := 0;
  end;
end;

end.




04_VCL スタイルを使用した場合の動作確認 - Delphi XE2 以降


Delphi XE2 で実装された VCL スタイルを使用した場合の動作確認です.
TListView が再生成する時に実行される RecreateWnd メソッドが発行するメッセージで処理しているので,基本的には動作します.VCL スタイルは設計時に設定しています.
下図は,スクロールバーの表示と非表示を切り換えるサンプルで,VCL スタイルを使用した場合の操作結果です.




図9
スクロールバーが表示されている状態
  • VCL のスタイルは Aqua Light State
図10
Button1 クリックで,スクロールバーを非表示にした状態

下図は,スクロールバーを無効化表示するサンプルで,VCL スタイルを使用した場合の結果です.




図11
スクロールバーが有効になっている状態
  • VCL スタイルは Light
図12
垂直方向にリサイズして,スクロールバーが無効化になった状態