|
Delphi Programming / Object Pascal
[掲載 2010年11月19日] [更新 2015年10月04日] Delphi サンプルプログラム集 |
947_エクスプローラをフォームに表示 |
動作確認等 |
Windows 7 U64(SP1) + Delphi XE(UP1) Pro
Delphi 2010 以降専用,Windows Vista 以降専用 |
|
947_ExplorerBrowser.zip [2,194 KB] 2015年10月04日版 (EXE 同梱) |
- 2010年11月23日
- サンプル[04]を追加
- サンプル [04] の作成用に,サンプル [02] [03] のコードを変更
- 2010年11月25日
- ILFree を CoTaskMemFree に変更
- 2010年12月27日
- フォルダツリーのサンプルコードの変更
インターフェイス部分を TCustomPanel のメンバーにして,Delphi らしいコードに変更
- 2011年01月03日
- 他のコードもインターフェイス部分を TCustomPanel のメンバーにした
- 2011年09月20日
- [1] [2] [3] インターフェイスを実行時に生成するコードに変更
- [2] [3] Delphi 2010 での参照カウンタの問題を修正 (Delphi XE, XE2 ではこの修正は不要)
- [1] [2] [3] スレッドの使用を止めて,OnIdle イベント処理に変更
- [2] Delphi 2010 での ShlObj.pas の修正方法をコメントに追加
- [2] [3] [4] Delphi XE2 (32 ビット, 64 ビットで動作確認
- 2015年10月04日
- Windows 7 U64(SP1) + Delphi XE(UP1) Pro で動作を再確認
|
[945_エクスプローラのテスト] では Windows の Shell32.dll の機能を利用して,エクスプローラの表示や表示フォルダの取得等のテストをしています.
フォルダのツリー表示やフォルダ内のファイルリストをアプリケーションに実装する場合,エクスプローラそのものを表示できれば,[814_コンテキストメニュー コンポ] や,ファイルリストの更新目的のための [815_フォルダの監視コンポーネント] は必要なくなります.これらは,エクスプローラそのものが持っている機能です.
Windows の shlwapi.dll が提供しているシェル名前空間関係のインターフェイスや関数を利用すると,エクスプローラそのものをフォーム上に直接表示して制御できます.
そこで,これらのインターフェイスを使用して,Windows Vista 以降の OS で機能をテストしてみました.サンプルコードが Microsoft の SDK にもありますが,ここでは [EternalWindows] と言うサイトの管理者に承諾を頂きましたので,[シェル / ExplorerBrowser] の記事のコードを Delphi に移植してみました.この参考サイトの記事に詳しい説明があります.各インターフェイスや関数類の機能は,コード内にMSDN の記事へのリンク先を書いておきました.参考にしてください.
参考サイトのコードでは,ウィンドウを生成し,そこにエクスプローラーのオブジェクトを表示するようになっていますが,このページのサンプルは,TCustomPanel を生成し,そこにエクスプローラのオブジェクトを表示するようにしています.フォームにはこの TCustomPanel を配置して表示します.
マイクロソフトでは,このページで使用しているインターフェイスを始め,いくつかのインターフェイスを提供していますが,筆者が確認したところでは,インターフェイスの組み合わせによっては正常に機能しなかったりすることがありました.つまり,カスタマイズは結構面倒でしょう.したがって,このページのサンプルは,あくまでも,このようなことが出来るという見本です.
これらのサンプルの機能,仕様のままで十分だ,と言うのであれば利用価値はあります.
これらのインターフェイスに,IShellBrowser というのがあります.このインターフェイスは,エクスプローラの右ペインのフォルダビューと呼ばれる部分で,指定フォルダ内のファイルのリスト表示するものです.残念ながら,Windows XP では下図のように,各ペインの表示制御ができないようです. |
|
図1
IShellBrowser による表示
- 表示ペインの制御ができない
- フォルダツリー表示ができない
[注意]
このサンプルのコードは添付していない |
01_エクスプローラをフォーム上に表示
( Delphi XE 以降のみ) |
[ExplorerBrowser の作成] の記事のコードを Delphi に移植したものです.コンポーネントとして利用する場合は,plExplorerBrowser.pas をコンポーネントとして登録してください.コンポーネントのアイコンは用意していません.下表のプロパティがあります.
アクセレーターキー操作が十分に機能しません.何かインターフェイスを追加して処理すれば可能かも知れませんが,調査していません. |
凡例 |
■ published
□ public
▲ ReadOnly |
■ SelectFolder |
String |
エクスプローラを表示した時に,選択状態にするフォルダの指定
右のフォルダビュー(ファイルリスト)にこのフォルダ内のファイルを表示する
空文字の時はデスクトップ
仮想フォルダは GUID 文字列で指定する
仮想フォルダの場合,取得は空文字となる
この値を設定しないと表示しない |
■ ViewMode |
Cardinal |
表示モードの値で,FVM_DETAILS (詳細表示-デフォルト) 等
この値を設定するには uses に ShlObj が必要 |
|
図2
テストプログラムの設計時画面
- TplExplorerBrowser は実行時生成
|
|
図3
[デスクトップ] ボタンクリック
- コードで各ペインの表示・非表示の設定が可能
- コンテキストメニューも表示できる
- ファイル類のドラッグ・ドロップも当然できる
|
|
図4
[ネットワーク] ボタンクリック |
|
図5
Windows Vista の場合
|
リスト1
コンポーネント TplExplorerBrowser のテストプログラム |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls, plExplorerBrowser;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Button5: TButton;
plExplorerBrowser1: TplExplorerBrowser;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//-============================================================================
// 表示開始
// デスクトップを選択
//-============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
plExplorerBrowser1.SelectFolder := '';
end;
//-============================================================================
// ドライブD:\を選択
//-============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
plExplorerBrowser1.SelectFolder := 'D:\';
end;
//-============================================================================
// コンピュータ(CSIDL_DRIVE)を選択
//-============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
plExplorerBrowser1.SelectFolder := '::{20D04FE0-3AEA-1069-A2D8-08002B30309D}';
end;
//-============================================================================
// ネットワーク(CSIDL_NETWORK)を選択
//-============================================================================
procedure TForm1.Button4Click(Sender: TObject);
begin
plExplorerBrowser1.SelectFolder := '::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}';
end;
//-============================================================================
// 現在選択中のフォルダを表示
// 仮想フォルダは空白
//-============================================================================
procedure TForm1.Button5Click(Sender: TObject);
begin
ShowMessage(plExplorerBrowser1.SelectFolder);
end;
end.
リスト2
実行時に生成して利用する場合の参考コード例 |
unit Unit1;
interface
uses
{$IF CompilerVersion < 23.0}
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls, plExplorerBrowser;
{$ELSE}
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls,
plExplorerBrowser, Vcl.StdCtrls;
{$IFEND}
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Button5: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
private
{ Private 宣言 }
FExlploer : TplExplorerBrowser;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//-============================================================================
// 表示開始
// デスクトップを選択
//-============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
if not Assigned(FExlploer) then begin
FExlploer := TplExplorerBrowser.Create(Self);
with FExlploer do begin
Parent := Self;
Width := Self.Width - 120;
Align := alRight;
Anchors := Anchors + [akLeft];
end;
end;
FExlploer.SelectFolder := '';
end;
//-============================================================================
// ドライブD:\を選択
//-============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
if not Assigned(FExlploer) then exit;
FExlploer.SelectFolder := 'D:\';
end;
//-============================================================================
// コンピュータ(CSIDL_DRIVE)を選択
//-============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
if not Assigned(FExlploer) then exit;
FExlploer.SelectFolder := '::{20D04FE0-3AEA-1069-A2D8-08002B30309D}';
end;
//-============================================================================
// ネットワーク(CSIDL_NETWORK)を選択
//-============================================================================
procedure TForm1.Button4Click(Sender: TObject);
begin
if not Assigned(FExlploer) then exit;
FExlploer.SelectFolder := '::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}';
end;
//-============================================================================
// 現在選択中のフォルダを表示
// 仮想フォルダは空白
//-============================================================================
procedure TForm1.Button5Click(Sender: TObject);
begin
if not Assigned(FExlploer) then exit;
ShowMessage(FExlploer.SelectFolder);
end;
end.
リスト3
コンポーネント TplExplorerBrowser のソースコード |
unit plExplorerBrowser;
interface
uses
{$IF CompilerVersion < 23.0}
Windows, Messages, SysUtils, Variants, Classes, Controls, ShlObj, ActiveX,
ExtCtrls, Graphics, Types, Forms;
{$ELSE}
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Controls, Winapi.ShlObj, Winapi.ActiveX, Vcl.ExtCtrls, Vcl.Graphics,
System.Types, Vcl.Forms;
{$IFEND}
type
TplExplorerBrowser = class(TCustomPanel,
IServiceProvider,
IExplorerPaneVisibility, IFolderFilter)
private
FSelectFolder : String;
FViewMode : Cardinal;
m_pExplorerBrowser : IExplorerBrowser;
procedure SetFSelectFolder(const Value: String);
protected
procedure WndProc(var Message: TMessage); override;
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
procedure SetParent(AParent: TWinControl); override;
procedure Paint; override;
procedure OnIdleEvent(Sender: TObject; var Done: Boolean);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function QueryService(const rsid, iid: TGuid; out Obj): HResult; stdcall;
function GetPaneState(const ep: EXPLORERPANE;
var peps: TExplorerPaneState): HRESULT; stdcall;
function ShouldShow(const psf: IShellFolder; pidlFolder: PItemIDList;
pidlItem: PItemIDList): HRESULT; stdcall;
function GetEnumFlags(const psf: IShellFolder; pidlFolder: PItemIDList;
var phwnd: HWND; var pgrfFlags: DWORD): HRESULT; stdcall;
published
property Caption;
property Align;
property Anchors;
property SelectFolder : String read FSelectFolder write SetFSelectFolder;
property ViewMode : Cardinal read FViewMode write FViewMode;
end;
fIUnknown_SetSite = function (punk: IUnknown; punkSite: IUnknown): HRESULT;
stdcall;
procedure Register;
implementation
var
IUnknown_SetSite : fIUnknown_SetSite;
{ TplExplorerBrowser }
//-============================================================================
// クラス(コンポーネント)の生成
// DLLとDLL内の関数のアドレスを取得
//-============================================================================
constructor TplExplorerBrowser.Create(AOwner: TComponent);
var
DLLHandle : THandle;
begin
inherited Create(AOwner);
DLLHandle := LoadLibrary('shlwapi.dll');
if DLLHandle <> 0 then begin
@IUnknown_SetSite := GetProcAddress(DLLHandle,'IUnknown_SetSite');
end;
m_pExplorerBrowser := nil;
BevelOuter := bvNone;
FViewMode := FVM_DETAILS;
Width := 650;
Height := 420;
end;
//-============================================================================
// クラス(コンポーネント)終了処理
//-============================================================================
destructor TplExplorerBrowser.Destroy;
begin
inherited;
end;
//-============================================================================
// ここでエクスプローラのインターフェイスオブジェクトを生成し初期化
//
// IExplorerBrowserインタフェイスオブジェクトは,ビジュアルなオブジェクトなの
// でOnCreateだと失敗する可能性がある.オーバライドしたSetPrentメソッド内
// で生成する
// SetPrentメソッドは,コンポの生成時,破棄の際等,複数回呼出されるので注意
//
// FOLDERSETTINGSのViewModeの値
// FOLDERVIEWMODE Enumeration
// http://msdn.microsoft.com/en-us/library/bb762510%28VS.85%29.aspx
//-============================================================================
procedure TplExplorerBrowser.SetParent(AParent: TWinControl);
var
rc : TRect;
fs : FOLDERSETTINGS;
ARect : TRect;
pFolderFilterSite : IFolderFilterSite;
punk : IInterface;
{$IF CompilerVersion < 23.0}
phdwp : Cardinal;
{$ELSE}
phdwp : NativeUInt;
{$IFEND}
begin
inherited;
if Parent = nil then exit;
if csDesigning in ComponentState then exit;
if m_pExplorerBrowser = nil then begin
//IExplorerBrowserインターフェイスのオブジェクトを生成して取得
if Failed(CoCreateInstance(CLSID_ExplorerBrowser,
nil,
CLSCTX_INPROC_SERVER,
IExplorerBrowser,
m_pExplorerBrowser)) then exit;
fs.ViewMode := FViewMode;
fs.fFlags := 0;
SetRectEmpty(rc);
if Failed(m_pExplorerBrowser.Initialize(Handle, rc, fs)) then exit;
m_pExplorerBrowser.SetOptions(EBO_SHOWFRAMES or EBO_ALWAYSNAVIGATE);
IUnknown_SetSite(m_pExplorerBrowser, Self);
//ShouldShowメソッドで表示フィルタを有効にするために必要
m_pExplorerBrowser.QueryInterface(IID_IFolderFilterSite, pFolderFilterSite);
//MDSNではSetFilterの引数は[in]であるがDelphi 2010ではvarとなっているので
//その対策.Delphi XEではconstになっている
punk := IFolderFilter(Self);
pFolderFilterSite.SetFilter(punk);
phdwp := 0;
ARect := Rect(0, 0, Width, Height);
m_pExplorerBrowser.SetRect(phdwp, ARect);
Application.OnIdle := OnIdleEvent;
end;
end;
//-============================================================================
// 透明だとコントロールの配置がしにくいのでclWhiteで塗りつぶす
// (コンポーネントとして登録して使用する場合の対策)
//-============================================================================
procedure TplExplorerBrowser.Paint;
var
ARect : TRect;
Flags : Longint;
begin
inherited Paint;
with Canvas do begin
Brush.Color := clWhite;
FillRect(ClientRect);
if Caption <> '' then begin
ARect := ClientRect;
Brush.Style := bsClear;
Font := Self.Font;
Flags := DT_SINGLELINE or DT_CENTER or DT_VCENTER;
DrawText(Handle, Caption, -1, ARect, Flags);
end;
end;
end;
//-============================================================================
// 本コントロール自身には何も描画しないのでバックグラウンド消去はしない
//-============================================================================
procedure TplExplorerBrowser.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
Message.Result := 0;
end;
//-============================================================================
// 本コントロールのウィンドプロシージャー
//-============================================================================
procedure TplExplorerBrowser.WndProc(var Message: TMessage);
var
ARect : TRect;
{$IF CompilerVersion < 23.0}
phdwp : Cardinal;
{$ELSE}
phdwp : NativeUInt;
{$IFEND}
begin
inherited WndProc(Message);
if Message.Msg = WM_SIZE then begin
if m_pExplorerBrowser <> nil then begin
phdwp := 0;
ARect := Rect(0, 0, Width, Height);
m_pExplorerBrowser.SetRect(phdwp, ARect);
end;
end;
end;
//-============================================================================
// 選択フォルダの設定
// プロパティSelectFolderの設定メソッド
//
// ここで設定したフォルダを選択状態にする.フォルダビュー(ファイルリスト)部に
// このフォルダ内のファイルをリストする
//-============================================================================
procedure TplExplorerBrowser.SetFSelectFolder(const Value: String);
var
pidl : PItemIDList;
begin
if @IUnknown_SetSite = nil then exit;
if (Value <> '') and (Value = FSelectFolder) then exit;
FSelectFolder := Value;
//空白ならデスクトップ
if FSelectFolder = '' then begin
if Failed(SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, pidl)) then exit;
end else begin
pidl := ILCreateFromPath(PChar(FSelectFolder));
if pidl = nil then exit;
end;
//表示開始
m_pExplorerBrowser.BrowseToIDList(pidl, SBSP_ABSOLUTE);
CoTaskMemFree(pidl);
end;
//-============================================================================
// キー操作監視開始
// フォルダビュー(ファイルリスト)部では一部機能しないようである
// また,アクセレーターキー操作は機能しない
//-============================================================================
procedure TplExplorerBrowser.OnIdleEvent(Sender: TObject; var Done: Boolean);
var
Amsg : Msg;
begin
if csDesigning in ComponentState then exit;
while True do begin
if GetMessage(AMsg, 0, 0, 0) = False then begin
PostQuitMessage(0);
break;
end;
TranslateMessage(Amsg);
DispatchMessage(Amsg);
end;
end;
//------------------ 以下インターフェイス関係関数類 ---------------------------
//-----------------------------------------------------------------------------
// IServiceProvider::QueryService
// http://msdn.microsoft.com/en-us/library/cc678966%28VS.85%29.aspx
//-----------------------------------------------------------------------------
function TplExplorerBrowser.QueryService(const rsid, iid: TGuid;
out Obj): HResult;
begin
Result := QueryInterface(iid, Obj);
end;
//-----------------------------------------------------------------------------
// IExplorerPaneVisibility::GetPaneState
// http://msdn.microsoft.com/en-us/library/bb761856%28VS.85%29.aspx
//
// このメソッド内で表示するペインの表示・非表示の変更が可能
// 表示するにはpepsの値をEPS_DEFAULT_ONにする
// 非表示で状態の変更不可にするにはEPS_DEFAULT_OFF or EPS_FORCEを代入する
// 検出は以下のようにできる
// if IsEqualGUID(ep, EP_NavPane) then begin
//
// --------------------------------------------------
// | EP_Commands |
// --------------------------------------------------
// | | | |
// | | | |
// | | | |
// | EP_NavPane | | EP_PreviewPane |
// | ナビゲータ | 常に表示 | ファイルの |
// | ペイン | | プレビュー領域 |
// | | | |
// | | | |
// | | | |
// --------------------------------------------------
// | EP_DetailsPane |
// --------------------------------------------------
//
//-----------------------------------------------------------------------------
function TplExplorerBrowser.GetPaneState(const ep: EXPLORERPANE;
var peps: TExplorerPaneState): HRESULT;
begin
peps := EPS_DEFAULT_ON;
Result := S_OK;
end;
//-----------------------------------------------------------------------------
// IFolderFilter::ShouldShow
// http://msdn.microsoft.com/en-us/library/bb775628%28v=VS.85%29.aspx
//
// 選択したフォルダ(フォルダビュー ファイルリストするフォルダ)のフルパス取得
// して,SelectFolderプロパティに代入
// ここでは隠し属性のファイルを非表示設定
//-----------------------------------------------------------------------------
function TplExplorerBrowser.ShouldShow(const psf: IShellFolder; pidlFolder,
pidlItem: PItemIDList): HRESULT;
var
pFullPath : array [0..MAX_PATH - 1] of Char;
Attrib : Cardinal;
begin
SHGetPathFromIDList(pidlFolder, pFullPath);
FSelectFolder := pFullPath;
psf.GetAttributesOf(1, pidlItem, Attrib);
if ((SFGAO_HIDDEN and Attrib) > 0) or ((SFGAO_GHOSTED and Attrib) > 0) then begin
Result := S_FALSE;
end else begin
Result := S_OK;
end;
end;
//-----------------------------------------------------------------------------
// IFolderFilter::GetEnumFlags
// http://msdn.microsoft.com/en-us/library/bb775624%28v=VS.85%29.aspx
//-----------------------------------------------------------------------------
function TplExplorerBrowser.GetEnumFlags(const psf: IShellFolder;
pidlFolder: PItemIDList; var phwnd: HWND; var pgrfFlags: DWORD): HRESULT;
begin
Result := S_OK;
end;
//=============================================================================
// コンポーネント登録情報
//=============================================================================
procedure Register;
begin
RegisterComponents('plXRAY', [TplExplorerBrowser]);
end;
end.
02_フォルダビュー ( ファイルリスト ) ペインをフォーム上に表示 |
[フォルダの切り替え] の記事のコードを Delphi に移植したものです.指定フォルダで,エクスプローラのフォルダビュー (ファイルリスト) ペインを表示します.ルートフォルダの設定機能を追加しています.コンポーネントとして利用する場合は,plExplorerFolderView.pas をコンポーネントとして登録してください.コンポーネントのアイコンは用意していません.下表のプロパティがあります. |
凡例 |
■ published
□ public
▲ ReadOnly |
■ RootFolder |
String |
ルートフォルダ
本コンポを利用する際の参照用の値
空文字の時はデスクトップ(デフォルト)
ルートフォルダはこの値か pidlRootFolder で指定する |
■ ExpandFolder |
String |
表示フォルダ
戻り値は現在展開しているフォルダのフルパスであるが,仮想フォルダの場合は空文字となる
この値か pidlExpandFolder を設定しないと表示しない |
□ pidlRootFolder |
PItemIDList |
ルートフォルダで,デスクトップからの項目識別子
ルートフォルダは,この値か RootFolder で指定する |
□ pidlExpandFolder |
PItemIDList |
表示フォルダ
デスクトップからの絶対値相当の項目識別子
この値か ExpandFolder を設定しないと表示しない |
■ ViewMode |
Cardinal |
表示モードの値で,FVM_DETAILS (詳細表示-デフォルト) 等
この値を設定するには uses に ShlObj が必要 |
function NavigateBack: Boolean
フォルダのダブルクリックまたは [Enter] で下の階層に移動した場合,[戻る] 動作を実行します.戻れるのは最初に表示したフォルダまでです.関数の実行に成功すると True を返します. |
function NavigateForward: Boolean
[進む] 動作を実行します.関数の実行に成功すると True を返します. |
OnExpandFolderChanged(Sender, pidlFolder, AFullPath)
引数 |
型 |
説明 |
Sender |
TObject |
コンポーネント自身 |
pidlFolder |
PItemIDList |
展開したフォルダの項目識別子
デスクトップからの絶対値相当 |
AFullPath |
String |
展開したフォルダのフルパス
仮想フォルダの場合は空文字 |
|
図6
テストプログラムの設計時画面 |
|
図7
実行例
- コンテキストメニューもエクスプローラで表示されるものと同じ
- [デスクトップ] をルートにした時の [コントロールパネル] には反応しない
|
リスト4
コンポーネント TplExplorerFolderView のテストプログラム |
unit Unit1;
interface
uses
{$IF CompilerVersion < 23.0}
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls, plExplorerFolderView;
{$ELSE}
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls,
plExplorerFolderView, Vcl.StdCtrls;
{$IFEND}
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
plExplorerFolderView1 : TplExplorerFolderView;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses ShlObj;
{$R *.dfm}
//-============================================================================
// ドライブDを表示
//-============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
plExplorerFolderView1.RootFolder := 'D:\';
plExplorerFolderView1.ExpandFolder := 'D:\';
end;
//-============================================================================
// [戻る]ボタン
//-============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
plExplorerFolderView1.NavigateBack;
end;
//-============================================================================
// [進む]ボタン
//-============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
plExplorerFolderView1.NavigateForward;
end;
//-============================================================================
// 表示形式の変更
// FVM_ICON等を使用するにはusesにShlObjが必要
//-============================================================================
procedure TForm1.Button4Click(Sender: TObject);
begin
plExplorerFolderView1.ViewMode := FVM_ICON;
end;
end.
リスト5
コンポーネント TplExplorerFolderView のソースコード |
unit plExplorerFolderView;
interface
uses
{$IF CompilerVersion < 23.0}
Windows, Messages, SysUtils, Variants, Classes, Controls, ShlObj, ActiveX,
ExtCtrls, Graphics, Types, Forms;
{$ELSE}
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Controls, Winapi.ShlObj, Winapi.ActiveX, Vcl.ExtCtrls, Vcl.Graphics,
System.Types, Vcl.Forms;
{$IFEND}
type
TpExplorerFolderViewChanged = procedure(Sender: TObject; pidlFolder: PItemIDList;
AFullPath: String) of object;
TplExplorerFolderView = class(TCustomPanel, IExplorerBrowserEvents)
private
FExpandFolder : String;
FRootFolder : String;
FpidlRootFolder : PItemIDList;
FpidlExpandFolder : PItemIDList;
FViewMode : Cardinal;
FOnExpandFolderChanged : TpExplorerFolderViewChanged;
m_dwCookie : DWORD;
m_pShellView : IShellView;
m_pExplorerBrowser : IExplorerBrowser;
procedure SetFExpandFolder(const Value: String);
procedure SetFRootFolder(const Value: String);
procedure SetFViewMode(const Value: Cardinal);
procedure SetFpidlExpandFolder(const Value: PItemIDList);
procedure SetFpidlRootFolder(const Value: PItemIDList);
protected
procedure WndProc(var Message: TMessage); override;
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
procedure Paint; override;
procedure SetParent(AParent: TWinControl); override;
procedure OnIdleEvent(Sender: TObject; var Done: Boolean);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function NavigateBack: Boolean;
function NavigateForward: Boolean;
property pidlRootFolder : PItemIDList read FpidlRootFolder
write SetFpidlRootFolder;
property pidlExpandFolder : PItemIDList read FpidlExpandFolder
write SetFpidlExpandFolder;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
function OnNavigationPending(pidlFolder: PItemIDList): HRESULT; stdcall;
function OnViewCreated(const psv: IShellView): HRESULT; stdcall;
function OnNavigationComplete(pidlFolder: PItemIDList): HRESULT; stdcall;
function OnNavigationFailed(pidlFolder: PItemIDList): HRESULT; stdcall;
published
property Caption;
property Align;
property Anchors;
property ExpandFolder : String read FExpandFolder write SetFExpandFolder;
property RootFolder : String read FRootFolder write SetFRootFolder;
property ViewMode : Cardinal read FViewMode write SetFViewMode;
property OnExpandFolderChanged : TpExplorerFolderViewChanged
read FOnExpandFolderChanged
write FOnExpandFolderChanged;
end;
procedure Register;
implementation
{ TplExplolreFolderView }
//-============================================================================
// クラス(コンポーネント)の生成
//-============================================================================
constructor TplExplorerFolderView.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
BevelOuter := bvNone;
FViewMode := FVM_THUMBNAIL;
FRootFolder := '';
FpidlRootFolder := nil;
FpidlExpandFolder := nil;
m_dwCookie := 0;
m_pShellView := nil;
m_pExplorerBrowser := nil;
Width := 400;
Height := 300;
end;
//-============================================================================
// クラス(コンポーネント)終了処理
//-============================================================================
destructor TplExplorerFolderView.Destroy;
begin
if m_dwCookie <> 0 then m_pExplorerBrowser.Unadvise(m_dwCookie);
if FpidlRootFolder <> nil then CoTaskMemFree(FpidlRootFolder);
if FpidlExpandFolder <> nil then CoTaskMemFree(FpidlExpandFolder);
inherited;
end;
//-============================================================================
// ここでエクスプローラのインターフェイスオブジェクトを生成し初期化
//
// ここで使用するインタフェイスオブジェクトは,ビジュアルなオブジェクトなので
// OnCreateだと失敗する可能性があるので.オーバライドしたSetPrentメソッド内で
// 生成する
// SetPrentメソッドは,コンポの生成時,破棄の際等,複数回呼出されるので注意
//
// FOLDERSETTINGS構造体のViewModeとfFlagsの値によって表示形式の変更ができる
//
// FOLDERSETTINGSのViewModeの値
// FOLDERVIEWMODE Enumeration
// http://msdn.microsoft.com/en-us/library/bb762510%28VS.85%29.aspx
//-============================================================================
procedure TplExplorerFolderView.SetParent(AParent: TWinControl);
var
rc : TRect;
hr : HRESULT;
fs : FOLDERSETTINGS;
ARect : TRect;
{$IF CompilerVersion < 23.0}
phdwp : Cardinal;
{$ELSE}
phdwp : NativeUInt;
{$IFEND}
psbe : IExplorerBrowserEvents;
begin
inherited;
if Parent = nil then exit;
if csDesigning in ComponentState then exit;
if m_pExplorerBrowser = nil then begin
//ExplorerBrowserを作成するには,CoCreateInstanceにCLSID_ExplorerBrowserを指
//定して,ExplorerBrowserを識別するIExplorerBrowserを取得する
if Failed( CoCreateInstance(CLSID_ExplorerBrowser,
nil,
CLSCTX_INPROC_SERVER,
IExplorerBrowser,
m_pExplorerBrowser)) then exit;
//Initializeを呼び出して取得したExplorerBrowserを初期化
fs.ViewMode := FViewMode;
fs.fFlags := FWF_NOHEADERINALLVIEWS;
SetRectEmpty(rc);
if Failed(m_pExplorerBrowser.Initialize(Handle, rc, fs)) then exit;
//MDSNではAdviseの第1引数は[in]であるがDelphi 2010ではvarとなっているので
//その対策.Delphi XEではconstになっている
//Delphi 2010でど動作しない場合は,ShlObj.pasをLibフォルダにコピーして,Advise
//関数の第1引数をconstにする
psbe := IExplorerBrowserEvents(Self);
hr := m_pExplorerBrowser.Advise(psbe, m_dwCookie);
if Failed(hr) then exit;
phdwp := 0;
ARect := Rect(0, 0, Width, Height);
m_pExplorerBrowser.SetRect(phdwp, ARect);
Application.OnIdle := OnIdleEvent;
end;
end;
//-============================================================================
// 透明だとコントロールの配置がしにくいのでclWhiteで塗りつぶす
// (コンポーネントとして登録して使用する場合の対策)
//-============================================================================
procedure TplExplorerFolderView.Paint;
var
ARect : TRect;
Flags : Longint;
begin
inherited Paint;
with Canvas do begin
Brush.Color := clWhite;
FillRect(ClientRect);
if Caption <> '' then begin
ARect := ClientRect;
Brush.Style := bsClear;
Font := Self.Font;
Flags := DT_SINGLELINE or DT_CENTER or DT_VCENTER;
DrawText(Handle, Caption, -1, ARect, Flags);
end;
end;
end;
//-============================================================================
// ルートフォルダの設定
// このフォルダより上の階層のフォルダは表示しない
//-============================================================================
procedure TplExplorerFolderView.SetFRootFolder(const Value: String);
var
pidl : PItemIDList;
begin
if (FRootFolder <> Value) or (FpidlRootFolder = nil) then begin
FRootFolder := Value;
//空白ならデスクトップ
if FRootFolder = '' then begin
SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, pidl);
end else begin
pidl := ILCreateFromPath(PChar(FRootFolder));
end;
SetFpidlRootFolder(pidl);
if pidl <> nil then CoTaskMemFree(pidl);
end;
end;
//-============================================================================
// ルートフォルダの項目識別子による設定
// pidlRootFolderプロパティの設定メソッド
// このフォルダより上の階層のフォルダは表示しない
//-============================================================================
procedure TplExplorerFolderView.SetFpidlRootFolder(const Value: PItemIDList);
var
pFullPath : array [0..MAX_PATH - 1] of Char;
begin
if Value = nil then exit;
if ILIsEqual(Value, FpidlRootFolder) = False then begin
SHGetPathFromIDList(Value, pFullPath);
FpidlRootFolder := ILClone(Value);
FRootFolder := pFullPath;
end;
end;
//-============================================================================
// 表示するフォルダの設定
// ExpandFolderプロパティの設定メソッド
// 実際の値は,実装したインターフェイス関数で取得する
//-============================================================================
procedure TplExplorerFolderView.SetFExpandFolder(const Value: String);
var
pidl : PItemIDList;
begin
if (Value <> '') and (Value = FExpandFolder) then exit;
//空白ならデスクトップ
if Value = '' then begin
if Failed(SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, pidl)) then exit;
end else begin
pidl := ILCreateFromPath(PChar(Value));
if pidl = nil then exit;
end;
//項目識別子で表示フォルダ設定
SetFpidlExpandFolder(pidl);
if pidl <> nil then CoTaskMemFree(pidl);
end;
//-============================================================================
// 表示するフォルダの設定(項目識別子による設定)
// pidlExpandFolderプロパティの設定メソッド
//-============================================================================
procedure TplExplorerFolderView.SetFpidlExpandFolder(const Value: PItemIDList);
begin
if Value = nil then exit;
if FpidlRootFolder = nil then SetFRootFolder('');
//表示開始
if ILIsEqual(Value, FpidlExpandFolder) = False then begin
m_pExplorerBrowser.BrowseToIDList(Value, SBSP_ABSOLUTE);
end;
end;
//-============================================================================
// 戻るメソッド
// 戻れるのは最初に表示したフォルダまで
//-============================================================================
function TplExplorerFolderView.NavigateBack: Boolean;
var
hr : HRESULT;
begin
Result := False;
if FpidlExpandFolder = nil then exit;
hr := m_pExplorerBrowser.BrowseToIDList(nil, SBSP_NAVIGATEBACK);
Result := hr = S_OK;
end;
//-============================================================================
// 進むメソッド
//-============================================================================
function TplExplorerFolderView.NavigateForward: Boolean;
var
hr : HRESULT;
begin
Result := False;
if FpidlExpandFolder = nil then exit;
hr := m_pExplorerBrowser.BrowseToIDList(nil, SBSP_NAVIGATEFORWARD);
Result := hr = S_OK;
end;
//-============================================================================
// 表示形式の変更
// ViewModeプロパティの設定メソッド
// アイコン表示の際に,カラムを非表示にするにはfFlagsを設定値を変更する
// http://msdn.microsoft.com/en-us/library/bb762510%28VS.85%29.aspx
// http://msdn.microsoft.com/en-us/library/bb762508%28VS.85%29.aspx
//-============================================================================
procedure TplExplorerFolderView.SetFViewMode(const Value: Cardinal);
var
fs : FOLDERSETTINGS;
begin
if FViewMode <> Value then begin
FViewMode := Value;
if m_pExplorerBrowser <> nil then begin
fs.ViewMode := FViewMode;
fs.fFlags := FWF_NOCOLUMNHEADER;
m_pExplorerBrowser.SetFolderSettings(fs);
if m_pShellView <> nil then m_pShellView.Refresh;
end;
end;
end;
//-============================================================================
// 本コントロール自身には何も描画しないのでバックグラウンド消去はしない
//-============================================================================
procedure TplExplorerFolderView.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
Message.Result := 0;
end;
//-============================================================================
// ウィンドプロシージャー
//-============================================================================
procedure TplExplorerFolderView.WndProc(var Message: TMessage);
var
ARect : TRect;
{$IF CompilerVersion < 23.0}
phdwp : Cardinal;
{$ELSE}
phdwp : NativeUInt;
{$IFEND}
begin
inherited WndProc(Message);
if (Message.Msg = WM_SIZE) then begin
if m_pExplorerBrowser <> nil then begin
phdwp := 0;
ARect := Rect(0, 0, Width, Height);
m_pExplorerBrowser.SetRect(phdwp, ARect);
end;
end;
end;
//-============================================================================
// キー関係の処理用
// これがないと,例えば[名前の変更]操作が正常に機能しない
// 設計時にはこれを実行しないようにする.実行するとエディタが正常に機能しない
//-============================================================================
procedure TplExplorerFolderView.OnIdleEvent(Sender: TObject; var Done: Boolean);
var
Amsg : Msg;
begin
if csDesigning in ComponentState then exit;
while True do begin
if GetMessage(AMsg, 0, 0, 0) = False then begin
PostQuitMessage(0);
break;
end;
if (m_pShellView = nil) or (m_pShellView.TranslateAccelerator(AMsg) <> S_OK)
then begin
TranslateMessage(Amsg);
DispatchMessage(Amsg);
end;
end;
end;
//------------------ 以下インターフェイス関係関数類 ---------------------------
//-----------------------------------------------------------------------------
// 参照カウンタの問題回避策
// Delphi 2010のみ.Delphi XE, XE2では不要
//-----------------------------------------------------------------------------
function TplExplorerFolderView._AddRef: Integer;
begin
Result := -1;
end;
//-----------------------------------------------------------------------------
// 参照カウンタの問題回避策
// Delphi 2010のみ.Delphi XE, XE2では不要
//-----------------------------------------------------------------------------
function TplExplorerFolderView._Release: Integer;
begin
Result := -1;
end;
//-----------------------------------------------------------------------------
// IExplorerBrowserEvents::OnNavigationPending
// http://msdn.microsoft.com/en-us/library/bb761890%28VS.85%29.aspx
//
// フォルダを切り替えようとすると,最初にこのOnNavigationPendingが呼ばれる
// S_OKを返すと新しいフォルダの中身が表示されてOnViewCreatedが発生
// その後にOnNavigationCompleteが呼ばれる
//
// 戻ることができない場合はOnNavigationPendingは呼ばれない
// ここではルートフォルダの階層下でないとフォルダ内の表示を変更しないようにし
// ている
// ILIsEqual関数で比較し,イベントを設定すれば,外部から[戻る]ボタンの非表示
// 等の制御が可能
//-----------------------------------------------------------------------------
function TplExplorerFolderView.OnNavigationPending(
pidlFolder: PItemIDList): HRESULT;
begin
if ILIsParent(FpidlRootFolder, pidlFolder, False) then begin
Result := S_OK;
end else begin
Result := E_NOTIMPL;
end;
end;
//-----------------------------------------------------------------------------
// IExplorerBrowserEvents::OnViewCreated
// http://msdn.microsoft.com/en-us/library/bb761892%28VS.85%29.aspx
//-----------------------------------------------------------------------------
function TplExplorerFolderView.OnViewCreated(const psv: IShellView): HRESULT;
begin
m_pShellView := psv;
Result := S_OK;
end;
//-----------------------------------------------------------------------------
// IExplorerBrowserEvents::OnNavigationComplete
// http://msdn.microsoft.com/en-us/library/bb761885%28VS.85%29.aspx
//
// フォルダ展開のイベント発行
//-----------------------------------------------------------------------------
function TplExplorerFolderView.OnNavigationComplete(
pidlFolder: PItemIDList): HRESULT;
var
pFullPath : array [0..MAX_PATH - 1] of Char;
begin
//イベントに引渡し
if ILIsEqual(FpidlExpandFolder, pidlFolder) = False then begin
SHGetPathFromIDList(pidlFolder, pFullPath);
FpidlExpandFolder := ILClone(pidlFolder);
FExpandFolder := pFullPath;
if Assigned(FOnExpandFolderChanged) then begin
FOnExpandFolderChanged(Self, FpidlExpandFolder, FExpandFolder);
end;
end;
Result := S_OK;
end;
//-----------------------------------------------------------------------------
// IExplorerBrowserEvents::OnNavigationFailed
// http://msdn.microsoft.com/en-us/library/bb761887%28VS.85%29.aspx
//-----------------------------------------------------------------------------
function TplExplorerFolderView.OnNavigationFailed(
pidlFolder: PItemIDList): HRESULT;
begin
Result := S_OK;
end;
//=============================================================================
// コンポーネント登録情報
//=============================================================================
procedure Register;
begin
RegisterComponents('plXRAY', [TplExplorerFolderView]);
end;
end.
[NameSpaceTreeControl の作成] の記事のコードを Delphi に移植したものです.指定したルートフォルダ以下をツリー表示します.コンポーネントとして利用する場合は,plExplorerFolderTree.pas をコンポーネントとして登録してください.コンポーネントのアイコンは用意していません.下表のプロパティがあります.
参考にしたページの最後の方に,IShellItemFilter の実装の説明がありますが,IShellItemFilter インターフェイスを実装すると,ルートフォルダの設定が機能しませんでした.参考サイトの記事にも説明がありますが,実装するインターフェイスの組合せによっては,正常に機能しないことがあるようです.現在のところ,ネット上でもエクスプローラの,この関係の記事は少ないため,実際にコーディングして確認するしかないようです.
INameSpaceTreeControlEvents と INameSpaceTreeControlCustomDraw インターフェイスの組合せは動作することは確認しています.
ツリーの各アイテムのイベント類の操作が必要であれば,INameSpaceTreeControlEvents インターフェイスを実装するといいかも知れません.
このサンプルのコードも,フォルダビュー (ファイルリスト) と同様,参考サイトの記事の C++ のコードをなるべく忠実に移植しましたが,2010年12月27日,Delphi らしいコードに変更しました.元となった C++ のコードは,Halbow 資料館で言えば,アプリケーションのウィンドウ生成も含まれたコードとなっています.
Delphl ではそのようなコードを作成しなくてもアプリケーションが作成できます.またインターフェイスの基本的な部分も Delphi が面倒を見てくれます.以下のコードでは,インターフェイスの関数類もコンポーネントのメンバーにしてみました.
|
凡例 |
■ published
□ public
▲ ReadOnly |
■ RootFolder |
String |
ルートフォルダ
これより下の階層のみをツリー表示する
仮想フォルダは GUID 文字列で与える
空文字の時はデスクトップ
この値か pidlRootFolder を設定しないと表示しない |
■ SelectFolder |
String |
選択フォルダ
設定はルートフォルダの階層下でないと選択しない
取得値は選択中のフォルダであるが,仮想フォルダは空文字 |
□ pidlRootFolder |
PItemIDList |
ルートフォルダの項目識別子
ルートフォルダは,この値か RootFolder で指定する
この値か RootFolder を設定しないと表示しない |
□ pidlSelectFolder |
PItemIDList |
選択フォルダ
デスクトップからの絶対値相当の項目識別子
設定はルートフォルダの階層下でないと選択しない |
▲ hwndTree |
HWND |
フォルダツリー表示枠のウインドウハンドル
実際の表示ハンドルではないので,このハンドルに対する操作はできないが,現在の位置と大きさは取得可能 |
OnRootFolderChanged(Sender, pidlFolder, AFullPath)
引数 |
型 |
説明 |
Sender |
TObject |
コンポーネント自身 |
pidlFolder |
PItemIDList |
ルートフォルダの項目識別子
デスクトップからの絶対値相当 |
AFullPath |
String |
ルートフォルダのフルパス
仮想フォルダの場合は空文字 |
OnSelectFolderChanged(Sender, pidlFolder, AFullPath)
引数 |
型 |
説明 |
Sender |
TObject |
コンポーネント自身 |
pidlFolder |
PItemIDList |
選択したフォルダの項目識別子
デスクトップからの絶対値相当 |
AFullPath |
String |
選択したフォルダのフルパス
仮想フォルダの場合は空文字 |
|
図8
テストプログラムの設計時画面 |
|
図9
デスクトップをルートフォルダに
- フォルダのみ表示している
INameSpaceTreeControl インターフェイスの AppendRoot メソッドの引数で変更可能
- [コントロールパネル] は展開はするが,起動はしない
|
|
図10
ネットワークをルートフォルダに |
リスト6
コンポーネント TplExplorerFolderTree のテストプログラム |
unit Unit1;
interface
uses
{$IF CompilerVersion < 23.0}
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls, plExplorerFolderTree;
{$ELSE}
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls,
Vcl.StdCtrls, plExplorerFolderTree;
{$IFEND}
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
plExplorerFolderTree1: TplExplorerFolderTree;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//-============================================================================
// クラスのオブジェクトを生成してデスクトップをルートフォルダに設定
//-============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
plExplorerFolderTree1.RootFolder := '';
end;
//-============================================================================
// ドライブDをフォルダツリー表示
//-============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
plExplorerFolderTree1.RootFolder := 'D:\';
end;
//-============================================================================
// ネットワーク(CSIDL_NETWORK)をフォルダリー表示
//-============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
plExplorerFolderTree1.RootFolder := '::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}';
end;
end.
リスト7
コンポーネント TplExplorerFolderTree のソースコード |
unit plExplorerFolderTree;
interface
uses
{$IF CompilerVersion < 23.0}
Windows, Messages, SysUtils, Variants, Classes, Controls, ShlObj, ActiveX,
ExtCtrls, Graphics, Types, Forms;
{$ELSE}
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Controls, Winapi.ShlObj, Winapi.ActiveX, Vcl.ExtCtrls, Vcl.Graphics,
System.Types, Vcl.Forms;
{$IFEND}
type
TpExplorerFolderTreeChanged = procedure(Sender: TObject; pidlFolder: PItemIDList;
AFullPath: String) of object;
TplExplorerFolderTree = class(TCustomPanel, INameSpaceTreeControlCustomDraw)
private
FRootFolder : String;
FSelectFolder : String;
FpidlRootFolder : PItemIDList;
FpidlSelectFolder : PItemIDList;
FOnRootFolderChanged : TpExplorerFolderTreeChanged;
FOnSelctFolderChanged : TpExplorerFolderTreeChanged;
FhwndTree : HWND;
m_dwTreeCookie : DWORD;
m_pNameSpaceTreeControl : INameSpaceTreeControl;
m_pShellItem : IShellItem;
procedure SetFRootFolder(const Value: String);
procedure SetFSelectFolder(const Value: String);
procedure SetFpidlSelectFolder(const Value: PItemIDList);
protected
procedure WndProc(var Message: TMessage); override;
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
procedure Paint; override;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
function PrePaint(hdc: HDC; var prc: TRect;
var plres: LRESULT): HRESULT; stdcall;
function PostPaint(hdc: HDC; var prc: TRect): HRESULT; stdcall;
function ItemPrePaint(hdc: HDC; var prc: TRect; var pnstccdItem: TNSTCCustomDraw;
var pclrText: COLORREF; var pclrTextBk: COLORREF;
var plres: LRESULT): HRESULT; stdcall;
function ItemPostPaint(hdc: HDC; var prc: TRect;
var pnstccdItem: TNSTCCustomDraw): HRESULT; stdcall;
procedure SetParent(AParent: TWinControl); override;
procedure OnIdleEvent(Sender: TObject; var Done: Boolean);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property pidlRootFolder : PItemIDList read FpidlRootFolder;
property pidlSelectFolder : PItemIDList read FpidlSelectFolder
write SetFpidlSelectFolder;
property hwndTree : HWND read FhwndTree;
published
property Caption;
property Align;
property Anchors;
property RootFolder : String read FRootFolder write SetFRootFolder;
property SelectFolder : String read FSelectFolder write SetFSelectFolder;
property OnRootFolderChanged : TpExplorerFolderTreeChanged
read FOnRootFolderChanged
write FOnRootFolderChanged;
property OnSelectFolderChanged : TpExplorerFolderTreeChanged
read FOnSelctFolderChanged
write FOnSelctFolderChanged;
end;
fIUnknown_GetWindow = function (punk: IUnknown; out phwnd: HWND): HRESULT; stdcall;
procedure Register;
implementation
const
CDRF_DODEFAULT = $00000000;
CDRF_NEWFONT = $00000002;
CDRF_SKIPDEFAULT = $00000004;
CDRF_DOERASE = $00000008;
CDRF_NOTIFYPOSTPAINT = $00000010;
CDRF_NOTIFYITEMDRAW = $00000020;
CDRF_NOTIFYSUBITEMDRAW = $00000020;
CDRF_NOTIFYPOSTERASE = $00000040;
CDRF_SKIPPOSTPAINT = $00000100;
CDIS_SELECTED = $0001;
CDIS_GRAYED = $0002;
CDIS_DISABLED = $0004;
CDIS_CHECKED = $0008;
CDIS_FOCUS = $0010;
CDIS_DEFAULT = $0020;
CDIS_HOT = $0040;
CDIS_MARKED = $0080;
CDIS_INDETERMINATE = $0100;
CDIS_SHOWKEYBOARDCUES = $0200;
CDIS_NEARHOT = $0400;
CDIS_OTHERSIDEHOT = $0800;
CDIS_DROPHILITED = $1000;
var
IUnknown_GetWindow : fIUnknown_GetWindow;
{ TplExplorerFolderTree }
//-============================================================================
// フォルダツリーオブジェクトを乗せる(表示する)TWinControlの生成
//-============================================================================
constructor TplExplorerFolderTree.Create(AOwner: TComponent);
var
DLLHandle : THandle;
begin
inherited Create(AOwner);
DLLHandle := LoadLibrary('shlwapi.dll');
if DLLHandle <> 0 then begin
@IUnknown_GetWindow := GetProcAddress(DLLHandle,'IUnknown_GetWindow');
end;
BevelOuter := bvNone;
FRootFolder := '';;
FSelectFolder := '';
FpidlRootFolder := nil;
FpidlSelectFolder := nil;
m_dwTreeCookie := 0;
m_pNameSpaceTreeControl := nil;
m_pShellItem := nil;
Width := 250;
Height := 300;
end;
//-============================================================================
// フォルダツリーオブジェクトを乗せる(表示する)TWinControlの解放処理
//-============================================================================
destructor TplExplorerFolderTree.Destroy;
begin
if m_dwTreeCookie <> 0 then m_pNameSpaceTreeControl.TreeUnadvise(m_dwTreeCookie);
if FpidlRootFolder <> nil then CoTaskMemFree(FpidlRootFolder);
if FpidlSelectFolder <> nil then CoTaskMemFree(FpidlSelectFolder);
inherited;
end;
//-============================================================================
// コンポのSetParentメソッド
// ここでエクスプローラのインターフェイスオブジェクトを生成
//
// INameSpaceTreeControインタフェイスオブジェクトは,ビジュアルなオブジェクト
// なのでOnCreateだと失敗する可能性があるので,オーバライドしたSetPrentメソッ
// ド内で生成する
// SetPrentメソッドは,コンポの生成時,破棄の際等,複数回呼出されるので注意
//-============================================================================
procedure TplExplorerFolderTree.SetParent(AParent: TWinControl);
var
rc : TRect;
begin
inherited;
if Parent = nil then exit;
if csDesigning in ComponentState then exit;
if m_pNameSpaceTreeControl = nil then begin
//INameSpaceControlインターフェイスのオブジェクトを生成して取得
if Failed(CoCreateInstance(CLSID_NamespaceTreeControl,
nil,
CLSCTX_INPROC_SERVER,
INameSpaceTreeControl,
m_pNameSpaceTreeControl)) then exit;
//取得したオブジェクトの初期化
SetRectEmpty(rc);
if Failed(m_pNameSpaceTreeControl.Initialize(
Handle,
rc,
NSTCS_HASEXPANDOS or
NSTCS_SHOWSELECTIONALWAYS or
NSTCS_BORDER or
NSTCS_TABSTOP)) then exit;
//オブジェクトの有効化(操作の通知を受け取り可能にする)
if Failed(m_pNameSpaceTreeControl.TreeAdvise(
INameSpaceTreeControlCustomDraw(Self),
m_dwTreeCookie)) then exit;
//フォルダツリーのハンドル(HwndTree)を取得
IUnknown_GetWindow(m_pNameSpaceTreeControl, FhwndTree);
Application.OnIdle := OnIdleEvent;
end;
end;
//-============================================================================
// 透明だとコントロールの配置がしにくいのでclWhiteで塗りつぶす
// (コンポーネントとして登録して使用する場合の対策)
// WM_ERASEBKGNDでバックグラウンドの消去を行っていないので,描画はすべてPaint
// メソッドで実行する必要がある
//-============================================================================
procedure TplExplorerFolderTree.Paint;
var
ARect : TRect;
Flags : Longint;
begin
inherited Paint;
with Canvas do begin
Brush.Color := clWhite;
FillRect(ClientRect);
if Caption <> '' then begin
ARect := ClientRect;
Brush.Style := bsClear;
Font := Self.Font;
Flags := DT_SINGLELINE or DT_CENTER or DT_VCENTER;
DrawText(Handle, Caption, -1, ARect, Flags);
end;
end;
end;
//-============================================================================
// ルートフォルダの設定
// RootFolderプロパティの設定メソッド
// このフォルダより上の階層のフォルダは表示しない
//-============================================================================
procedure TplExplorerFolderTree.SetFRootFolder(const Value: String);
//---------------------------------------------------------------------------
// 実際のルートフォルダ設定と,現在表示中のツリー削除
//---------------------------------------------------------------------------
function SetRoot_TreeControlHost(pidl: PItemIDList): Boolean;
var
pShellItem : IShellItem;
begin
Result := False;
if Failed(SHCreateItemFromIDList(pidl, IShellItem, pShellItem)) then exit;
//取得したm_pShellItemからルートフォルダを生成してツリー生成
//ここではフォルダ(SHCONTF_FOLDERS)のみの表示に設定
if Failed(m_pNameSpaceTreeControl.AppendRoot(
pShellItem,
SHCONTF_FOLDERS,
NSTCRS_VISIBLE or NSTCRS_EXPANDED,
nil)) then exit;
//既にルートフォルダの設定があれば削除
//最初に削除するとアイテムが正常表示しない場合がある
if m_pShellItem <> nil then begin
if Failed(m_pNameSpaceTreeControl.RemoveRoot(m_pShellItem)) then exit;
end;
//新しいShellItemオブジェクト
m_pShellItem := pShellItem;
pShellItem := nil;
Result := True;
end;
//---------------------------------------------------------------------------
var
pidl : PItemIDList;
begin
if @IUnknown_GetWindow = nil then exit;
if (Value <> '') and (Value = FRootFolder) then exit;
pidl := nil;
//空文字ならデスクトップをルートフォルダにする
//空文字以外の場合は,項目識別子を求めてそれに該当するフォルダをルートにする
if Value = '' then begin
SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, pidl);
end else begin
pidl := ILCreateFromPath(PChar(Value));
if pidl = nil then exit;
end;
//実際のルートフォルダの設定
if SetRoot_TreeControlHost(pidl) = False then begin
CoTaskMemFree(pidl);
exit;
end;
FRootFolder := Value;
FpidlRootFolder := ILClone(pidl);
//イベントに引渡し
if Assigned(FOnRootFolderChanged) then begin
FOnRootFolderChanged(Self, FpidlRootFolder, FRootFolder);
end;
CoTaskMemFree(pidl);
end;
//-============================================================================
// 選択フォルダの指定
// SelectFolderプロパティの設定メソッド
//-============================================================================
procedure TplExplorerFolderTree.SetFSelectFolder(const Value: String);
var
pidl : PItemIDList;
begin
if Value = '' then exit;
//フォルダのパス名から項目識別子を取得
pidl := ILCreateFromPath(PChar(Value));
if pidl = nil then exit;
if ILIsEqual(FpidlSelectFolder, pidl) then begin
CoTaskMemFree(pidl);
exit;
end;
SetFpidlSelectFolder(pidl);
CoTaskMemFree(pidl);
end;
//-============================================================================
// 選択フォルダの設定(項目識別子による設定)
// pidlSelectFolderプロパティの設定メソッド
//-============================================================================
procedure TplExplorerFolderTree.SetFpidlSelectFolder(const Value: PItemIDList);
var
pShellItem : IShellItem;
nsMask : NSTCITEMSTATE;
nsFlags : NSTCITEMSTATE;
begin
if Value = nil then exit;
if ILIsEqual(Value, FpidlSelectFolder) = False then begin
//ルートフォルダの階層下かを調査
if ILIsParent(FpidlRootFolder, Value, False) = False then begin
exit;
end;
//項目識別子からIShellItemオブジェクトを取得
if Failed(SHCreateItemFromIDList(Value, IShellItem, pShellItem)) then begin
exit;
end;
//取得したIShellItemを使用してSetItemStateメソッドを実行して選択
//nsMaskにNSTCIS_EXPANDEDを含めればサブフォルダも展開する
nsMask := NSTCIS_SELECTED;
nsFlags := nsMask;
m_pNameSpaceTreeControl.SetItemState(pShellItem, nsMask, nsFlags);
pShellItem := nil;
FpidlSelectFolder := ILClone(Value);
end;
end;
//-============================================================================
// チラつき防止
// 本コントロール自身には何も描画しないのでバックグラウンド消去はしない
//-============================================================================
procedure TplExplorerFolderTree.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
Message.Result := 0;
end;
//-============================================================================
// ウィンドウプロシージャー
// フォルダツリーの位置とサイズの設定等
//-============================================================================
procedure TplExplorerFolderTree.WndProc(var Message: TMessage);
begin
inherited WndProc(Message);
if Message.Msg = WM_SIZE then begin
if (FhwndTree <> 0) then begin
MoveWindow(FhwndTree, 0, 0, Width, Height, True);
end;
end;
end;
//-============================================================================
// キー関係の処理用
// これがないと,アクセラレータキーの動作が正しく行われない
// 設計時にはこれを実行しないようにする.実行するとエディタが正常に機能しない
//-============================================================================
procedure TplExplorerFolderTree.OnIdleEvent(Sender: TObject; var Done: Boolean);
var
Amsg : Msg;
begin
if csDesigning in ComponentState then exit;
while True do begin
if GetMessage(AMsg, 0, 0, 0) = False then begin
PostQuitMessage(0);
break;
end;
TranslateMessage(Amsg);
DispatchMessage(Amsg);
end;
end;
//------------------ 以下インターフェイス関係関数類 ---------------------------
//-----------------------------------------------------------------------------
// 参照カウンタの問題回避策
// Delphi 2010のみ.Delphi XE, XE2では不要
//-----------------------------------------------------------------------------
function TplExplorerFolderTree._AddRef: Integer;
begin
Result := -1;
end;
//-----------------------------------------------------------------------------
// 参照カウンタの問題回避策
// Delphi 2010のみ.Delphi XE, XE2では不要
//-----------------------------------------------------------------------------
function TplExplorerFolderTree._Release: Integer;
begin
Result := -1;
end;
//-----------------------------------------------------------------------------
// INameSpaceTreeControlCustomDraw::ItemPostPaint
// http://msdn.microsoft.com/en-us/library/bb761616%28v=VS.85%29.aspx
//-----------------------------------------------------------------------------
function TplExplorerFolderTree.ItemPostPaint(hdc: HDC; var prc: TRect;
var pnstccdItem: TNSTCCustomDraw): HRESULT;
begin
Result := S_OK;
end;
//-----------------------------------------------------------------------------
// INameSpaceTreeControlCustomDraw::ItemPrePaint
// http://msdn.microsoft.com/en-us/library/bb761617%28v=VS.85%29.aspx
//
// 選択したアイテムのテキスト色を変更
// 選択したフォルダに対するイベント
//-----------------------------------------------------------------------------
function TplExplorerFolderTree.ItemPrePaint(hdc: HDC; var prc: TRect;
var pnstccdItem: TNSTCCustomDraw; var pclrText, pclrTextBk: COLORREF;
var plres: LRESULT): HRESULT;
var
pShellItem : IShellItem;
pShellItemArray : IShellItemArray;
pidl : PItemIDList;
pFullPath : array [0..MAX_PATH - 1] of Char;
begin
if (pnstccdItem.uItemState and CDIS_FOCUS) > 0 then begin
pclrText := RGB(200, 0, 0);
plres := CDRF_NEWFONT;
end;
//選択したアイテムのフルパスを取得
//ただし仮想フォルダは空文字
if (pnstccdItem.uItemState and CDIS_SELECTED) > 0 then begin
if m_pNameSpaceTreeControl.GetSelectedItems(pShellItemArray) = S_OK then begin
if Succeeded( pShellItemArray.GetItemAt(0, pShellItem)) then begin
if Succeeded(SHGetIDListFromObject(pShellItem, pidl)) then begin
if ILIsEqual(FpidlSelectFolder, pidl) = False then begin
SHGetPathFromIDList(pidl, pFullPath);
FpidlSelectFolder := ILClone(pidl);
FSelectFolder := pFullPath;
//イベントに引渡し
if Assigned(FOnSelctFolderChanged) then begin
FOnSelctFolderChanged(Self, FpidlSelectFolder, FSelectFolder);
end;
end;
CoTaskMemFree(pidl);
end;
pShellItem := nil;
end;
pShellItemArray := nil;
end;
end;
Result := S_OK;
end;
//-----------------------------------------------------------------------------
// INameSpaceTreeControlCustomDraw::PostPaint
// http://msdn.microsoft.com/en-us/library/bb761618%28v=VS.85%29.aspx
//-----------------------------------------------------------------------------
function TplExplorerFolderTree.PostPaint(hdc: HDC; var prc: TRect): HRESULT;
begin
Result := S_OK;
end;
//-----------------------------------------------------------------------------
// INameSpaceTreeControlCustomDraw::PrePaint
// http://msdn.microsoft.com/en-us/library/bb761619%28v=VS.85%29.aspx
//-----------------------------------------------------------------------------
function TplExplorerFolderTree.PrePaint(hdc: HDC; var prc: TRect;
var plres: LRESULT): HRESULT;
begin
plres := CDRF_NOTIFYITEMDRAW;
Result := S_OK;
end;
//=============================================================================
// コンポーネント登録情報
//=============================================================================
procedure Register;
begin
RegisterComponents('plXRAY', [TplExplorerFolderTree]);
end;
end.
04_フォルダツリーとフォルダビューをフォーム上に表示 |
上のサンプルで使用したユニット,plExplorerFolderView.pas と plExplorerFolderTree.pas を使用してフォルダツリーとフォルダビュー(ファイルリスト)を表示します.いわゆる「ファイラー」という部類のアプリケーションになるのでしょうか.フォルダツリーとフォルダビューとの連携はイベントで行っています.
他のシェルコンポーネント類のように,フォルダツーとフォルダビューのコードを 1 つのユニットにまとめて,ユニット内で処理する方がいいかも知れません. |
|
図11
テストプログラムの設計時画面
- TPanel を配置し,その上に以下を配置
- TplExplorerFolderTree
- TSplitter(リサイズ用)
- TplExplorerFolderView
|
|
図12
実行して [デスクトップ]
- ツリー部の [コントロールパネル] は展開はするが,機能しない
- ファイルリストの [コントロールパネル] はクリックしても反応しない
|
|
図13
[D:\] |
リスト8
コンポーネント TplExplorerFolderView と TplExplorerFolderTree を組合わせたテスト |
unit Unit1;
interface
uses
{$IF CompilerVersion < 23.0}
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls, plExplorerFolderTree, plExplorerFolderView, ShlObj;
{$ELSE}
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls,
Vcl.StdCtrls, plExplorerFolderTree, plExplorerFolderView, Winapi.ShlObj;
{$IFEND}
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Panel1: TPanel;
plExplorerFolderTree1: TplExplorerFolderTree;
Splitter1: TSplitter;
plExplorerFolderView1: TplExplorerFolderView;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure plExplorerFolderTree1SelectFolderChanged(Sender: TObject;
pidlFolder: PItemIDList; AFullPath: string);
procedure plExplorerFolderView1ExpandFolderChanged(Sender: TObject;
pidlFolder: PItemIDList; AFullPath: string);
procedure plExplorerFolderTree1RootFolderChanged(Sender: TObject;
pidlFolder: PItemIDList; AFullPath: string);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
// -============================================================================
// フォルダツリーのルートフォルダをデスクトップに設定して表示
// フォルダビュー(ファイルリスト)はイベントで設定
//
// ドライブD:\をルートにした後にデスクトップをルートにすると,ドライブD:\は
// デスクトップの階層下なので,ドライブD:\が選択状態となる
// -============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
plExplorerFolderTree1.RootFolder := '';
end;
// -============================================================================
// ドライブD:\をルートフォルダに設定
// -============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
plExplorerFolderTree1.RootFolder := 'D:\';
end;
// -============================================================================
// ネットワーク(CSIDL_NETWORK)をフォルダリー表示
// -============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
plExplorerFolderTree1.RootFolder := '::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}';
end;
// -============================================================================
// フォルダツリーのルートフォルダ変更イベント
// フォルダビュー(ファイルリスト)のルートフォルダも変更しておく
//
// PItemIDListの利用にはusesにShlObjが必要
// -============================================================================
procedure TForm1.plExplorerFolderTree1RootFolderChanged(Sender: TObject;
pidlFolder: PItemIDList; AFullPath: string);
begin
plExplorerFolderView1.pidlRootFolder := pidlFolder;
end;
// -============================================================================
// フォルダツリーの選択フォルダ変更イベント
// フォルダビュー(ファイルリスト)の展開(Expand)フォルダも変更
// -============================================================================
procedure TForm1.plExplorerFolderTree1SelectFolderChanged(Sender: TObject;
pidlFolder: PItemIDList; AFullPath: string);
begin
plExplorerFolderView1.pidlExpandFolder := pidlFolder;
end;
// -============================================================================
// フォルダビューの展開フォルダ変更イベント
// フォルダツリーの選択フォルダも変更
// -============================================================================
procedure TForm1.plExplorerFolderView1ExpandFolderChanged(Sender: TObject;
pidlFolder: PItemIDList; AFullPath: string);
begin
plExplorerFolderTree1.pidlSelectFolder := pidlFolder;
end;
end.
|