Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2015年07月28日] [更新 2015年07月28日] Delphi サンプルプログラム集
073_TTreeView のスクロールバーの表示制御
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
073_TTreeView_ScrollBar.zip [2,282 KB] 2015年07月28日版 (EXE 同梱)







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


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

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

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






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


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

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




図1
設計時の画面
図2
実行開始画面
  • 水平,垂直スクロールバーが共に表示されている
図3
ノードの一部を閉じる
  • 全てのデータが表示されてもスクロールバーは表示
図4
Button1 をクリック
  • 未表示のデータがあってもスクロールバーが非表示となる

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

interface

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

type
  TplTreeView = class(TTreeView)
  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;
    TreeView1: TTreeView;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    plTreeView1 : TplTreeView;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  plChgCompo,
  CommCtrl;

{$R *.dfm}

//=============================================================================
//  フォームのOnCreateイベント処理
//  TTreeViewの派生クラス(コンポーネント)を生成
//  すぐにテストできるようにテスト用のデータをセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  i      : Integer;
  j      : Integer;
  k      : Integer;
  LText1 : String;
  LText2 : String;
  LNode1 : TTreeNode;
  LNode2 : TTreeNode;
begin
  //フォーム作成時にコンポーネントの入れ替え
  plTreeView1 := TplTreeView(ChangeComponent(TreeView1, TplTreeView));

  plTreeView1.Items.BeginUpdate;

  for i := 0 to 2 do begin
    LText1 := Format('%.5d',[i + 1]);
    LNode1 := TreeView1.Items.Add(nil, LText1);
    for j := 0 to 2 do begin
      LText2 := LText1 + '-' + Format('%.5d',[j + 1]);
      LNode2 := plTreeView1.Items.AddChild(LNode1, LText2);
      if i = 2 then begin
        for k := 0 to 2 do begin
          plTreeView1.Items.AddChild(LNode2, LText2 + '-' + Format('%.10d',[k + 1]));
        end;
      end;
    end;
  end;

  plTreeView1.FullExpand;
  plTreeView1.Items.EndUpdate;

  LNode1 := plTreeView1.Items.GetFirstNode;
  LNode1.MakeVisible;
  LNode1.Selected := True;

  ActiveControl := plTreeView1;
end;

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

{ TplTreeView }

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

//=============================================================================
//  WM_NCCALCSIZEメッセージ処理
//  スクロールバー表示のフラグを強制的に変更
//=============================================================================
procedure TplTreeView.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_TTreeView のスクロールバーの無効化表示


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

GetScrollInfo 関数で,表示データがクライアント領域に収まらない状態でスクロールの Range を取得すると,水平スクロールの範囲としてのピクセル数が,垂直のスクロール範囲として表示可能なノード数が取得できます.表示可能なノード数には,展開していないノードは含まれていません.
データがクライアント領域に全て表示されている時は,これらの値は 0 です.
このことを利用しています.

リサイズ以外にも,ノードの追加,削除,あるいはノードを展開した時やノードを閉じた時もチェックが必要です.これらをチェックする処理も追加してみました.



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




図5
EXE の実行開始画面
図6
一部のノードを閉じる
  • データが全てクライアント領域内にある状態
  • スクロールバーが無効化の表示になる
図7
ノードを全て展開して縦方向にリサイズ
  • 垂直スクロールバーが無効化の状態になる

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

interface

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

type
  TplTreeView = class(TTreeView)
  private
    procedure Available_Disable_ScrollBars;
  protected
    procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE;
    procedure CNNotify(var Message: TWMNotifyTV); message CN_NOTIFY;
    procedure Added(Node: TTreeNode); override;
    procedure Resize; override;
  end;

  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    TreeView1: TTreeView;
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    plTreeView1 : TplTreeView;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  plChgCompo;

{$R *.dfm}

//=============================================================================
//  フォームのOnCreateイベント処理
//  TTreeViewの派生クラス(コンポーネント)を生成
//  すぐにテストできるようにテスト用のデータをセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  i      : Integer;
  j      : Integer;
  k      : Integer;
  LText1 : String;
  LText2 : String;
  LNode1 : TTreeNode;
  LNode2 : TTreeNode;
begin
  //フォーム作成時にコンポーネントの入れ替え
  plTreeView1 := TplTreeView(ChangeComponent(TreeView1, TplTreeView));

  plTreeView1.Items.BeginUpdate;

  for i := 0 to 2 do begin

    LText1 := Format('%.5d',[i + 1]);
    LNode1 := plTreeView1.Items.Add(nil, LText1);
    for j := 0 to 2 do begin
      LText2 := LText1 + '-' + Format('%.5d',[j + 1]);
      LNode2 := plTreeView1.Items.AddChild(LNode1, LText2);
      if i = 2 then begin
        for k := 0 to 2 do begin
          plTreeView1.Items.AddChild(LNode2, LText2 + '-' + Format('%.15d',[k + 1]));
        end;
      end;
    end;
  end;

  plTreeView1.FullExpand;
  plTreeView1.Items.EndUpdate;

  LNode1 := plTreeView1.Items.GetFirstNode;
  LNode1.MakeVisible;
  LNode1.Selected := True;
end;

{ TplTreeView }

//-----------------------------------------------------------------------------
//  スクロールバーの有効化表示と無効化表示の制御用メソッド
//-----------------------------------------------------------------------------
procedure TplTreeView.Available_Disable_ScrollBars;
var
  LScrollInfo : TScrollInfo;
begin
  LScrollInfo.cbSize := SizeOf(LScrollInfo);
  LScrollInfo.fMask  := SIF_ALL;

  //垂直スクロールバーの有効化表示,無効化表示の処理
  GetScrollInfo(Handle, SB_VERT, LScrollInfo);
  if LScrollInfo.nMax > 0 then begin
    EnableScrollBar(Handle, SB_VERT, ESB_ENABLE_BOTH);
  end else begin
    EnableScrollBar(Handle, SB_VERT, ESB_DISABLE_BOTH);
  end;

  //水平スクロールバーの有効化表示,無効化表示の処理
  GetScrollInfo(Handle, SB_HORZ, LScrollInfo);
  if LScrollInfo.nMax > 0 then begin
    EnableScrollBar(Handle, SB_HORZ, ESB_ENABLE_BOTH);
  end else begin
    EnableScrollBar(Handle, SB_HORZ, ESB_DISABLE_BOTH);
  end;
end;

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

//=============================================================================
//  Addedメソッド
//  ノードが追加された時に自動的に実行されるメソッド
//=============================================================================
procedure TplTreeView.Added(Node: TTreeNode);
begin
  inherited;
  Available_Disable_ScrollBars;
end;

//=============================================================================
//  CN_NOTIFYメッセージ処理
//  TVN_XXX等はCN_NOTIFYメッセージで受信できる
//  CN_NOTIFYの引数の型は,コード補完機能を使用するとTMessage型となる
//  これをTWMNotifyTV型に変更して使用
//
//  TVN_XXXの使用にはusesにCommCtrlが必要
//=============================================================================
procedure TplTreeView.CNNotify(var Message: TWMNotifyTV);
var
  LCode : Integer;
begin
  inherited;

  LCode := Message.NMHdr.code;
  case LCode of
    TVN_ENDLABELEDIT,
    TVN_DELETEITEM,
    TVN_ITEMEXPANDED:
    begin
      Available_Disable_ScrollBars;
    end;
  end;
end;

//=============================================================================
//  WM_NCCALCSIZEメッセージ処理
//  スクロールバー表示のフラグを強制的に変更
//=============================================================================
procedure TplTreeView.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_TTreeView のスクロールバーのコンテキストメニュー


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

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




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

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

interface

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

type
  TplTreeView = class(TTreeView)
  private
  protected
    procedure WMContextMenu(var Message: TWMContextMenu);
      message WM_CONTEXTMENU;
  end;

  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    TreeView1: TTreeView;
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    plTreeView1 : TplTreeView;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  plChgCompo;

{$R *.dfm}

//=============================================================================
//  フォームのOnCreateイベント処理
//  TTreeViewの派生クラス(コンポーネント)を生成
//  すぐにテストできるようにテスト用のデータをセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  i      : Integer;
  j      : Integer;
  k      : Integer;
  LText1 : String;
  LText2 : String;
  LNode1 : TTreeNode;
  LNode2 : TTreeNode;
begin
  //フォーム作成時にコンポーネントの入れ替え
  plTreeView1 := TplTreeView(ChangeComponent(TreeView1, TplTreeView));

  plTreeView1.Items.BeginUpdate;

  for i := 0 to 2 do begin
    LText1 := Format('%.5d',[i + 1]);
    LNode1 := TreeView1.Items.Add(nil, LText1);
    for j := 0 to 2 do begin
      LText2 := LText1 + '-' + Format('%.5d',[j + 1]);
      LNode2 := plTreeView1.Items.AddChild(LNode1, LText2);
      if i = 2 then begin
        for k := 0 to 2 do begin
          plTreeView1.Items.AddChild(LNode2, LText2 + '-' + Format('%.10d',[k + 1]));
        end;
      end;
    end;
  end;

  plTreeView1.FullExpand;
  plTreeView1.Items.EndUpdate;

  LNode1 := plTreeView1.Items.GetFirstNode;
  LNode1.MakeVisible;
  LNode1.Selected := True;

  ActiveControl := plTreeView1;
end;

{ TplTreeView }

//=============================================================================
//  WM_CONTEXTMENUメッセージ処理
//  スクロールバーのコンテキストメニューを表示しないようにする
//=============================================================================
procedure TplTreeView.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_TTreeView のノードのテキストのツールチップを非表示


TTreeView のノードにマウスカーソルを移動します.この時,ノードのテキストがクライアントの右端に表示しきれない場合,下図のようなツールチップが現れます.
このツールチップが現れないようにする方法です.
方法は,スクロールバーの表示または非表示と同じで,ウィンドウのスタイルに TVS_NOTOOLTIPS フラグをセットします.実際にこの設定による動作を確認するためのサンプルです.

もし,上の方のサンプルのような TTreeView のウィンドウの再生成の処理を行わなければ,以下のように TreeView_SetToolTips メソッドで非表示にできます.



procedure TForm1.Button1Click(Sender: TObject);
begin
  TreeView_SetToolTips(TreeView1.Handle, 0);
end;




図9
ノードのテキストのツールチップ
  • このツールチップを表示しないようにする

リスト4
TTreeView のノードのテキストのツールチップを表示しないようにする
unit Unit1;

interface

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

type
  TplTreeView = class(TTreeView)
  protected
    procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE;
  end;

  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    TreeView1: TTreeView;
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    plTreeView1 : TplTreeView;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  plChgCompo,
  CommCtrl;

{$R *.dfm}

//=============================================================================
//  フォームのOnCreateイベント処理
//  TTreeViewの派生クラス(コンポーネント)を生成
//  すぐにテストできるようにテスト用のデータをセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  i      : Integer;
  j      : Integer;
  k      : Integer;
  LText1 : String;
  LText2 : String;
  LNode1 : TTreeNode;
  LNode2 : TTreeNode;
begin
  //フォーム作成時にコンポーネントの入れ替え
  plTreeView1 := TplTreeView(ChangeComponent(TreeView1, TplTreeView));

  plTreeView1.Items.BeginUpdate;

  for i := 0 to 2 do begin
    LText1 := Format('%.5d',[i + 1]);
    LNode1 := TreeView1.Items.Add(nil, LText1);
    for j := 0 to 2 do begin
      LText2 := LText1 + '-' + Format('%.5d',[j + 1]);
      LNode2 := plTreeView1.Items.AddChild(LNode1, LText2);
      if i = 2 then begin
        for k := 0 to 2 do begin
          plTreeView1.Items.AddChild(LNode2, LText2 + '-' + Format('%.10d',[k + 1]));
        end;
      end;
    end;
  end;

  plTreeView1.FullExpand;
  plTreeView1.Items.EndUpdate;

  LNode1 := plTreeView1.Items.GetFirstNode;
  LNode1.MakeVisible;
  LNode1.Selected := True;

  ActiveControl := plTreeView1;
end;

{ TplTreeView }

//=============================================================================
//  WM_NCCALCSIZEメッセージ処理
//  スクロールバー表示のフラグを強制的に変更
//
//  定数名TVS_XXXの使用にはusesにCommCtrlが必要
//=============================================================================
procedure TplTreeView.WMNCCalcSize(var Message: TWMNCCalcSize);
var
  LStyle : Integer;
begin
  LStyle := GetWindowLong(Handle, GWL_STYLE);
  SetWindowLong(Handle, GWL_STYLE, LStyle or TVS_NOTOOLTIPS);

  inherited;
end;

end.