|
Delphi Programming / Object Pascal
[掲載 2008年12月05日] [更新 2013年11月16日] Delphi サンプルプログラム集 |
802_フォルダの参照ダイアログとコールバック関数 |
動作確認等 |
Windows 7 U64(SP1) + Delphi XE Pro |
|
802_FolderBrowserCallback.zip [1,673 KB] 2013年11月16日版 (EXE 同梱) |
- 2008年12月07日
- コンポーネントのオプション (表示スタイル) をオブジェクトインスペクタで設定可能にした
- この変更によりテストプログラムのコードも変更
- 2010年10月10日
- ファイル関連ダイアログコンポーネントの整備に伴いコンポーネントの添付を中止し,[コンポーネント化] の項の説明を変更
- 2013年11月16日
- コンポーネントを再添付.それに伴いそのコンポーネントを使用したサンプルを追加
|
[801_フォルダの参照ダイアログ] でフォルダの参照ダイアログのテストをしましたが,表示位置の指定を行なったり,スタイルをカスタマイズするには,コールバック関数を使用します.
コールバック関数は,SHBrowseForFolder 関数の引数,TBrowseInfo 構造体のメンバで設定します.
この SHBrowseFolder 関数では,フォルダやファイルを,ウィンドウズのシェルの項目識別子というもので管理しています.この項目識別子をエクスプローラ等に表示している文字列に変換したり,逆に項目識別子に変換するコードが必要となってきます.
この項目識別子は,シェルメモリアロケータという特別なメモリ領域を使用します.このメモリ領域は SHGetMalloc 関数で割り当て,使用終了した項目識別子は IMalloc インターフェイスの Free メソッドで解放します.解放しないと使用メモリが増大していく場合があります.
本ページのサンプルは,下表を参考にして作成しています.TForm1 の Button1 クリックでダイアログを表示していますが,別のユニットに関数として作成しておけば,必要な時に呼出して使用することができます.
なお,Delphi では,uses に ShlObj や ComObj がある場合,特に OleInitialize,CoInitialize 関数等で COM の初期化を行う必要はありません (古い Delphi を除く).
このページのサンプルでは,TBrowseInfo 構造体のメンバの InitDir を表示開始時に選択状態にするフォルダ,X と Y を表示位置の指定に使用しています.
Windows Vista 以降に追加された特殊フォルダを扱う方法は,[801_フォルダの参照ダイアログ] の記事を参考にしてください. |
表1
SHBrowseForFolder の引数 TBrowseInfo 構造体のメンバ
関数の戻り値は,選択したフォルダの項目識別子 (型は PItemIDList) |
hwndOwner |
HWND |
ダイアログのオーナーのウィンドウハンドル |
pidlRoot |
PItemIDList |
ルートフォルダの項目識別子 |
pszDisplayName |
PAnsiChar |
選択したフォルダの名前 |
lpszTitle |
PAnsiChar |
ダイアログ上部に表示する文字列 |
ulFlags |
UINT |
ダイアログの表示形式等を指定する値の組合わせ
下表参照 |
lpfn |
TFNBFFCallBack |
コールバック関数のアドレス |
lParam |
LParam |
コールバック関数に渡すパラメータ
以下の TBFFRecord 構造体の各値で指定する
この構造体は Delphi にはないので定義して使用する
使い方は自由
type
PBFFRecord = ^TBFFRecord;
TBFFRecord = record
InitDir : PChar;
X : Integer;
Y : Integer;
end; |
iImage |
Integer |
選択フォルダのシステムイメージリスト内のインデックス |
表2
上の表の ulFlag の値で,ダイアログのスタイルを指定する定数値
全てが ShlObj.pas で定義されているわけではない.ない場合は自分で定義するか直接数値を指定
Windows のバージョンによっては使用できないものもある
この表の値は,Windows Vista 以降で使用可能 |
BIF_RETURNONLYFSDIRS |
$0001 |
ファイルシステムディレクトリのみを選択可能 |
BIF_DONTGOBELOWDOMAIN |
$0002 |
ドメインのネットワークフォルダを表示しない |
BIF_STATUSTEXT |
$0004 |
ダイアログツリー部のすぐ上に文字列を表示する
コールバック関数内で処理する必要がある |
BIF_RETURNFSANCESTORS |
$0008 |
ルート以下のファイル,フォルダのみ選択可能 |
BIF_EDITBOX |
$0010 |
名前編集用ボックスを表示 |
BIF_VALIDATE |
$0020 |
名前編集ボックスへの無効な文字列をチェックする
BIF_EDITBOX の指定がある時のみ有効
処理はコールバック関数内で行う |
BIF_NEWDIALOGSTYLE |
$0040 |
以下の新しいスタイルのダイアログを表示
リサイズ可能
ダイアログへのドラッグアンドドロップ
フォルダの再整理
ショートカットメニュー (コンテキスト)
新しいフォルダの作成 |
BIF_USENEWUI |
$0050 |
名前編集ボックスのある新しいスタイル
BIF_EDITBOX or BIF_NEWDIALOGSTYLE 指定と同じ |
BIF_BROWSEINCLUDEURLS |
$0080 |
URLを表示可能とする
BIF_USENEWUIとBIF_BROWSEINCLUDEFILES の指定必要
該当フォルダがサポートする場合のみ表示 |
BIF_UAHINT |
$0100 |
名前編集ボックスの代わりにヒントを表示
BIF_NEWDIALOGSTYLE の指定も必要 |
BIF_NONEWFOLDERBUTTON |
$0200 |
[新しいフォルダの作成] ボタンを表示しない
BIF_NEWDIALOGSTYLE の指定も必要 |
BIF_NOTRANSLATETARGETS |
$0400 |
選択アイテムがショートカットの場合,リンク先ではなく,ショートカット自身の項目識別子を返す |
BIF_BROWSEFORCOMPUTER |
$1000 |
コンピュータのみ選択可能とする |
BIF_BROWSEFORPRINTER |
$2000 |
プリンタのみ選択可能とする |
BIF_BROWSEINCLUDEFILES |
$4000 |
ファイルも表示して選択可能とする |
BIF_SHAREABLE |
$8000 |
共有リソースを表示して選択可能にする
BIF_USENEWUI の指定も必要 |
表3
コールパック関数の引数
関数名と引数の名前は任意だが,型とその順序は一致させる |
hwindow |
HWND |
ダイアログのハンドル |
uMsg |
UINT |
ダイアログが発行するメッセージで以下のいずれか
BFFM_INITIALIZED
ダイアログは初期設定の状態にある.lParam は 0
ダイアログの表示位置などが設定可能
BFFM_SELCHANGED
フォルダが選択された時に発生
lParam の値は選択されたフォルダの項目識別子
BFFM_VALIDATEFAILED
名前編集ボックスに無効な文字列が入力された場合に発生 |
lParam |
LPARAM |
uMsg の値によって異なる |
lpData |
LPARAM |
SHBrowseForFolder 関数の引数
TBrowseInfo 構造体のメンバ のアドレス |
|
図1
設計時画面 |
|
図2
実行してルートフォルダを選択
- CSIDL_PROGRAMS をルートフォルダにしてみる
|
|
図3
フォルダの参照ダイアログを表示
- 確かにプログラムとなっているが
- これが CSIDL_PROGRAMS なのか
- Vistaでは ?
Windows XP とは何か違う
|
リスト1
SHBrowseForFolder 関数を使用して,フォルダの参照ダイアログを表示 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
ComboBox1: TComboBox;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses ShlObj, ActiveX;
type
PBFFRecord = ^TBFFRecord;
TBFFRecord = record
InitDir : PChar;
X : Integer;
Y : Integer;
end;
{$R *.dfm}
//-----------------------------------------------------------------------------
// SHBrowseForFolder関数ののコールバック関数のコード例
//
// [引数]
// hWindow ダイアログのハンドル
// uMsg ダイアログが発行するメッセージ
// lParam uMsgに応じた値
// lpData 呼出し側のTBFFRecord構造体のポインタ
//-----------------------------------------------------------------------------
function plBrowseExCallBack(hWindow: HWND; uMsg: UINT; lParam: LPARAM;
lpData: LPARAM): Integer; stdcall;
var
PathName : array[0..MAX_PATH-1] of Char;
APath : String;
PBFFR : PBFFRecord;
ARect : TRect;
APosX : Integer;
APosY : Integer;
DlgWidth : Integer;
DlgHeight : Integer;
begin
case uMsg of
//ダイアログの表示開始初期状態
BFFM_INITIALIZED:
begin
PBFFR := pointer(lpData);
//初期選択フォルダ名が空でなければ,それを選択状態にする
//それにはダイアログウィンドウにBFFM_SETSELECTIONメッセージを送る
if lstrlen(PBFFR^.InitDir) > 1 then begin
SendMessage(hWindow,BFFM_SETSELECTION,1,Integer(PBFFR^.InitDir));
end;
//表示位置の設定
APosX := PBFFR^.X;
APosY := PBFFR^.Y;
//表示指定の座標値が共に-1であれば画面の中央に表示
//そうでないと時はAPosXとAPosYで指定する値
if (APosX < 0) and (APosY < 0) then begin
GetWindowRect(hWindow,ARect);
DlgWidth := ARect.Right-ARect.Left;
DlgHeight := ARect.Bottom-ARect.Top;
APosX := (GetSystemMetrics(SM_CXSCREEN) - DlgWidth) div 2;
APosY := (GetSystemMetrics(SM_CYSCREEN) - DlgHeight) div 2;
end;
SetWindowPos(hWindow, 0, APosX, APosY, 0, 0, SWP_NOSIZE or SWP_NOZORDER);
end;
//フォルダまたはファイルが選択された
BFFM_SELCHANGED:
begin
//選択しているフォルダの項目識別子をパス文字列に変換
//それをステータス表示部に表示
//TBrowseInfo構造体のulFlagsにBIF_STATUSTEXTを指定した時の処理方法例
SHGetPathFromIDList(PItemIDList(lParam), Addr(PathName));
APath := PathName;
SendMessage(hWindow,
BFFM_SETSTATUSTEXT,
0,
LongInt(PChar(ExtractFileName(APath))));
end;
end;
//このコールバック関数は常に0を返さなければいけない
Result := 0;
end;
//-----------------------------------------------------------------------------
// SHBrowseForFolder関数でフォルダの参照ダイアログを表示する
// 戻り値は選択したフォルダのフルパス文字列
//
// [引数]
// RootDir ルートフォルダ.CSIDL値で指定する
// InitDir 表示開始時に選択するフォルダ.フルパスの文字列で指定する
// Flags 表示スタイルの指定.BIF_XXXXの組合わせで指定する
// X ダイアログの表示位置.左上のX座標値
// Y 同上.左上のY座標値
//-----------------------------------------------------------------------------
function plBrowseForderEx(RootDir:Integer; InitDir: String; Flags: UINT;
X,Y:Integer): String;
var
AFileDir : String;
BFFR : TBFFRecord;
IDList : PItemIDList;
BrowseInfo : TBrowseInfo;
BPathName : array[0..MAX_PATH-1] of Char;
BDisplayName : array[0..MAX_PATH-1] of Char;
Malloc : IMalloc;
begin
SHGetMalloc(Malloc);
FillChar(BrowseInfo, SizeOf(BrowseInfo), #0);
//CSIDL値を項目識別子に変換
SHGetSpecialFolderLocation(Application.Handle, RootDir, IDList);
AFileDir := InitDir;
BFFR.InitDir := PChar(AFileDir);
BFFR.X := X;
BFFR.Y := Y;
BrowseInfo.hwndOwner := Application.Handle;
BrowseInfo.pidlRoot := IDList;
BrowseInfo.pszDisplayName := BDisplayName;
BrowseInfo.lpszTitle := PChar('フォルダの指定');
BrowseInfo.ulFlags := Flags;
BrowseInfo.lpfn := Addr(plBrowseExCallBack);
BrowseInfo.lParam := integer(Addr(BFFR));
IDList := SHBrowseForFolder(BrowseInfo);
if IDList <> nil then begin
//BrowseInfo.pszDisplayNameだとパス部分がない名前
SHGetPathFromIDList(IDList,Addr(BPathName));
Result := BPathName;
Malloc.Free(IDList);
end;
end;
//=============================================================================
// 上の関数をテストするコード
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
RootDir : Integer;
InitDir : String;
Flags : UINT;
PosX : Integer;
PosY : Integer;
begin
//ルートと初期選択フォルダ
//RootDir := 0; の時はデスクトップ
RootDir := 0;
if ComboBox1.Text <> '' then begin
if ComboBox1.ItemIndex >= 1 then begin
RootDir := Integer(ComboBox1.Items.Objects[ComboBox1.ItemIndex]);
end else begin
InitDir := ComboBox1.Text;
end;
end;
//表示開始位置
//共に-1だと画面中央
PosX := -1;
PosY := -1;
//表示スタイル
Flags := BIF_RETURNONLYFSDIRS or BIF_STATUSTEXT;
//次回はInitDirを選択フォルダにするためにInitDirに結果を取得
InitDir := plBrowseForderEx(RootDir, InitDir, Flags, PosX, PosY);
if InitDir <> '' then begin
ComboBox1.Text := InitDir;
Panel1.Caption := ' ' + InitDir;
end;
end;
//=============================================================================
// 特殊フォルダのCSIDL値をセット
// これをルートフォルダにする
// Vista以降にしか対応していないCSIDL_値があるので定数ではなく実値を格納
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
ComboBox1.Text := '';
ComboBox1.AutoComplete := False;
//ルートフォルダ用の値をセット
with ComboBox1.Items do begin
Clear;
//このプロジェクトのフォルダ
Add( ExtractFileDir(ParamStr(0)));
AddObject('CSIDL_DESKTOP', TObject($0000));
AddObject('CSIDL_INTERNET', TObject($0001));
AddObject('CSIDL_PROGRAMS', TObject($0002));
AddObject('CSIDL_CONTROLS', TObject($0003));
AddObject('CSIDL_PRINTERS', TObject($0004));
AddObject('CSIDL_PERSONAL', TObject($0005));
AddObject('CSIDL_FAVORITES', TObject($0006));
AddObject('CSIDL_STARTUP', TObject($0007));
AddObject('CSIDL_RECENT', TObject($0008));
AddObject('CSIDL_SENDTO', TObject($0009));
AddObject('CSIDL_BITBUCKET', TObject($000A));
AddObject('CSIDL_STARTMENU', TObject($000B));
AddObject('CSIDL_MYDOCUMENTS', TObject($000C));
AddObject('CSIDL_MYMUSIC', TObject($000D));
AddObject('CSIDL_MYVIDEO', TObject($000E));
AddObject('CSIDL_DESKTOPDIRECTORY', TObject($0010));
AddObject('CSIDL_DRIVES', TObject($0011));
AddObject('CSIDL_NETWORK', TObject($0012));
AddObject('CSIDL_NETHOOD', TObject($0013));
AddObject('CSIDL_FONTS', TObject($0014));
AddObject('CSIDL_TEMPLATES', TObject($0015));
AddObject('CSIDL_COMMON_STARTMENU', TObject($0016));
AddObject('CSIDL_COMMON_PROGRAMS', TObject($0017));
AddObject('CSIDL_COMMON_STARTUP', TObject($0018));
AddObject('CSIDL_COMMON_DESKTOPDIRECTORY', TObject($0019));
AddObject('CSIDL_APPDATA', TObject($001A));
AddObject('CSIDL_PRINTHOOD', TObject($001B));
AddObject('CSIDL_LOCAL_APPDATA', TObject($001C));
AddObject('CSIDL_ALTSTARTUP', TObject($001D));
AddObject('CSIDL_COMMON_ALTSTARTUP', TObject($001E));
AddObject('CSIDL_COMMON_FAVORITES', TObject($001F));
AddObject('CSIDL_INTERNET_CACHE', TObject($0020));
AddObject('CSIDL_COOKIES', TObject($0021));
AddObject('CSIDL_HISTORY', TObject($0022));
AddObject('CSIDL_COMMON_APPDATA', TObject($0023));
AddObject('CSIDL_WINDOWS', TObject($0024));
AddObject('CSIDL_SYSTEM', TObject($0025));
AddObject('CSIDL_PROGRAM_FILES', TObject($0026));
AddObject('CSIDL_MYPICTURES', TObject($0027));
AddObject('CSIDL_PROFILE', TObject($0028));
AddObject('CSIDL_SYSTEMX86', TObject($0029));
AddObject('CSIDL_PROGRAM_FILESX86', TObject($002A));
AddObject('CSIDL_PROGRAM_FILES_COMMON', TObject($002B));
AddObject('CSIDL_PROGRAM_FILES_COMMONX86', TObject($002C));
AddObject('CSIDL_COMMON_TEMPLATES', TObject($002D));
AddObject('CSIDL_COMMON_DOCUMENTS', TObject($002E));
AddObject('CSIDL_COMMON_ADMINTOOLS', TObject($002F));
AddObject('CSIDL_ADMINTOOLS', TObject($0030));
AddObject('CSIDL_CONNECTIONS', TObject($0031));
AddObject('CSIDL_COMMON_MUSIC', TObject($0035));
AddObject('CSIDL_COMMON_PICTURES', TObject($0036));
AddObject('CSIDL_COMMON_VIDEO', TObject($0037));
AddObject('CSIDL_RESOURCES', TObject($0038));
AddObject('CSIDL_RESOURCES_LOCALIZED', TObject($0039));
AddObject('CSIDL_COMMON_OEM_LINKS', TObject($003A));
AddObject('CSIDL_CDBURN_AREA', TObject($003B));
AddObject('CSIDL_COMPUTERSNEARME', TObject($003D));
AddObject('CSIDL_PROFILES', TObject($003E));
end;
end;
end.
上のコードでは,ダイアログの表示部分を関数にしていますが,この部分をコンポーネントにしておけば便利です.そこでコンポーネントにしてみました.
本サンプルは,作成したコンポーネントのテストプログラムです.コンポボックスで選択した特殊フォルダをルートフォルダにして,ダイアログを表示するだけです.コンボボックスにフォルダ名を入力すると,そのフォルダをルートフォルダにして表示します.
コンポーネントは,実行時に生成しています.添付の Readme.txt で確認してください. |
|
図4
設計時画面 |
|
図5
実行例
- CSIDL_PROGRAM_FILES_COMMON をルートフォルダにした場合
|
リスト2
SHBrowseForFolder 関数を使用したコンポーネントのテスト |
//=============================================================================
// 作成したフォルダ参照ダイアログコンポーネントのテストプログラム
// ルートフォルダをコンボボックスから選択
//
// 表示位置の取得はフォルダ選択時に行っている
// したがって,フォルダ選択後に移動しても位置は記録されない
// ダイアログを[キャンセル]で閉じた場合も位置の記録はしない
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
AIndex : Integer;
Browser : TplBrowseFolderEx;
begin
//コンポーネント生成
Browser := TplBrowseFolderEx.Create(Self);
try
//ルートと初期選択フォルダ
if ComboBox1.Text <> '' then begin
AIndex := ComboBox1.ItemIndex;
if AIndex >= 1 then begin
Browser.RootSpecialDir := Integer(ComboBox1.Items.Objects[AIndex]);
end else begin
Browser.RootDir := ComboBox1.Text;
Browser.InitDir := ComboBox1.Text + '\AAA\BBB';
end;
end;
//表示位置
if APosX > 0 then Browser.Left := APosX;
if APosy > 0 then Browser.Top := APosy;
//表示スタイル
Browser.Options := [bifReturnOnlyFSDirs];
//ダイアログを表示して[OK]で閉じたら
if Browser.Execute then begin
//表示位置を記憶しておき,次回この位置に表示する
APosX := Browser.Left;
APosY := Browser.Top;
Panel1.Caption := ' ' + Browser.Directory;
end;
finally
FreeAndNil(Browser);
end;
end;
//=============================================================================
// 特殊フォルダのCSIDL値をセット
// これをルートフォルダにする
// Vista以降にしか対応していないCSIDL_値があるので定数ではなく実値を格納
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
//最初のサンプルと同じ
end;
03_作成したコンポーネントで BIF_XXX オプションの動作確認 |
これも作成したコンポーネントのテストです.
TBrowseInfo 構造体のメンバの ulFlags によって,ダイアログのスタイルがどうなるかを確認するためのものです.ulFlags の値は,BIF_XXXXX という定数名です.値の意味については,本ページの最初の方の表を参考にしてください.
コンポーネントは,実行時に生成しています.添付の Readme.txt で確認してください. |
|
図6
設計時画面 |
|
図7
実行例
- ルートフォルダは,このサンプルの EXE があるフォルダ
- BIF_EDITBOX と BIF_BROWSEINCLUDEFILES を指定
- 上部に編集ボックスが配置されていて,ファイルも表示されている
|
リスト3
SHBrowseForFolder 関数を使用したコンポーネントのテスト
BIF_XXXX の動作確認用 |
//=============================================================================
// 作成したフォルダ参照ダイアログコンポーネントのテストプログラム
// ルートフォルダをコンボボックスから選択する
// BIF関係オプションの組み合わせ動作の確認用
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
AIndex : Integer;
begin
//ルートと初期選択フォルダ
if ComboBox1.Text <> '' then begin
AIndex := ComboBox1.ItemIndex;
if AIndex >= 1 then begin
plBrowser.RootSpecialDir := Integer(ComboBox1.Items.Objects[AIndex]);
end else begin
plBrowser.RootDir := ComboBox1.Text;
plBrowser.InitDir := ComboBox1.Text + '\AAA\BBB';
end;
end;
//表示位置
plBrowser.Left := -1;
plBrowser.Top := -1;
//表示スタイルのオプション
plBrowser.Options := [];
with plBrowser do begin
if CheckBox1.Checked then Options := Options + [bifReturnOnlyFSDirs];
if CheckBox2.Checked then Options := Options + [bifDontGoBelowDomain];
if CheckBox3.Checked then Options := Options + [bifStatusText];
if CheckBox4.Checked then Options := Options + [bifReturnFSAncestors];
if CheckBox5.Checked then Options := Options + [bifEditBox];
if CheckBox6.Checked then Options := Options + [bifValidate];
if CheckBox7.Checked then Options := Options + [bifNewDialogStyle];
if CheckBox8.Checked then Options := Options + [bifUseNewUI];
if CheckBox9.Checked then Options := Options + [bifBrowseIncludeURLs];
if CheckBox10.Checked then Options := Options + [bifUAHint];
if CheckBox11.Checked then Options := Options + [bifNonNewFolderButton];
if CheckBox12.Checked then Options := Options + [bifNoTranslatetarGets];
if CheckBox13.Checked then Options := Options + [bifBrowseForComputer];
if CheckBox14.Checked then Options := Options + [bifBrowseForPrinter];
if CheckBox15.Checked then Options := Options + [bifBrowseIncludeFiles];
if CheckBox16.Checked then Options := Options + [bifShareable];
end;
//ダイアログを表示して[OK]で閉じたら
if plBrowser.Execute then begin
Panel1.Caption := ' ' + plBrowser.Directory;
end;
end;
//=============================================================================
// 特殊フォルダのCSIDL値をセット
// これをルートフォルダにする
// Vista以降にしか対応していないCSIDL_値があるので定数ではなく実値を格納
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
//コンポーネント生成
plBrowser := TplBrowseFolderEx.Create(Self);
//以下,最初のサンプルと同じ
end;
作成したコンポーネントのソースコードです.TComponent からの継承コンポーネントです.追加したプロパティとメソッド類は,以下表の通りとなっています.
最初のサンプルとは違い,特殊フォルダ以外もルートフォルダにできるようにしています.
ダイアログの表示位置は,ダイアログでフォルダを選択した時に検出しています.したがって,フォルダを選択した後に位置を変更しても無効です.
当サイトでは,同様の機能のコンポーネントを配布していますが,提供しているコンポーネントは,古い Delphi でも動作可能にするために,項目識別子関係のコードが違っています.また,ダイアログの位置の設定には,サブクラスという手法を使用しています. |
Directory |
String |
読出し専用
Excute メソッドを実行して [OK] でダイアログを閉じた時に返される値で,選択したディレクトリのフルパス |
Title |
String |
ダイアログ上部に表示するタイトル文字列 |
RootDir |
String |
ルートフォルダの指定
特殊フォルダ以外を指定する場合に使用する |
RootSpecialDir |
Integer |
ルートフォルダの指定
特殊フォルダを CSIDL 値で指定する
RootDir を指定している場合は,RootDir の設定が有効となる |
InitDir |
String |
表示開始時に選択するフォルダのフルパス文字列
特殊フォルダでの指定はできない |
Top |
Integer |
ダイアログの表示位置の左上の Y 座標値
指定がない場合は画面中央 |
Left |
Integer |
ダイアログの表示位置の左上の X 座標値
指定がない場合は画面中央 |
Options |
TplBrowseOps |
表示スタイルの値を BIF_XXXXX の組み合わせで指定する
BIF_XXXXX の使用には uses に ShlObj が必要 |
Execute |
関数 |
フォルダ選択ダイアログを表示する
フォルダを選択すると True
[キャンセル] でダイアログを閉じると False を返す |
リスト4
SHBrowseForFolder 関数を使用したフォルダの参照ダイアログのコンポーネント |
unit plBrowseFolderEx;
interface
uses
SysUtils, Classes, Windows, Forms;
type
//指定可能なオブションの要素
//オブシェクトインスペクタで設計時に設定可能とするため定義
TplBrowseOp =(bifReturnOnlyFSDirs,
bifDontGoBelowDomain,
bifReturnFSAncestors,
bifStatusText,
bifEditBox,
bifValidate,
bifNewDialogStyle,
bifUseNewUI,
bifBrowseIncludeURLs,
bifUAHint,
bifNonNewFolderButton,
bifNoTranslatetarGets,
bifBrowseForComputer,
bifBrowseForPrinter,
bifBrowseIncludeFiles,
bifShareable);
//指定可能なオプションを示す集合体
//上のTplBrowsOptionExは複数選択可能な集合体とする
TplBrowseOps = set of TplBrowseOp;
TplBrowseFolderEx = class(TComponent)
private
{ Private 宣言 }
FDirectory: String;
FTitle : String;
FRootDir : String;
FRootSpecialDir : Integer;
FInitDir : String;
FTop : Integer;
FLeft : Integer;
FOptions : TplBrowseOps;
protected
{ Protected 宣言 }
public
{ Public 宣言 }
constructor Create(AOwner: TComponent); override;
function Execute:Boolean;
property Directory : String read FDirectory;
property RootDir : String read FRootDir write FRootDir;
property RootSpecialDir : Integer read FRootSpecialDir write FRootSpecialDir;
property InitDir : String read FInitDir write FInitDir;
property Top : Integer read FTop write FTop;
property Left : Integer read FLeft write FLeft;
published
{ Published 宣言 }
property Title : String read FTitle write FTitle;
property Options : TplBrowseOps read FOptions write FOptions;
end;
procedure Register;
implementation
uses ShlObj, ActiveX, Dialogs;
//-----------------------------------------------------------------------------
// コンポーネント登録情報
//-----------------------------------------------------------------------------
procedure Register;
begin
RegisterComponents('Samples', [TplBrowseFolderEx]);
end;
{ TplBrowseFolderEx }
type
PBFFRecord = ^TBFFRecord;
TBFFRecord = record
InitDir : PChar;
X : Integer;
Y : Integer;
end;
var
APosX : Integer;
APosY : Integer;
//ダイアログの各オプションに対応する集合型
//Vista以外で使用することを考えて実値で定義
BrowseOptions: array [TplBrowseOp] of Integer=($0001, //BIF_RETURNONLYFSDIRS
$0002, //以下略
$0004,
$0008,
$0010,
$0020,
$0040,
$0050,
$0080,
$0100,
$0200,
$0400,
$1000,
$2000,
$4000,
$8000);
//-----------------------------------------------------------------------------
// フルパス名を項目識別子に変換する関数
// 特殊フォルダ(CSIDL以外)もルートフォルダで指定可能なようにするため装備
//-----------------------------------------------------------------------------
function GetPItemIDFromFullPath(ShFolder:IShellFolder ;Path:String): PItemIDList;
var
Len : Cardinal;
Flags : Cardinal;
begin
Flags := 0;
Len := Length(Path);
ShFolder.ParseDisplayName(Application.Handle,
nil,
PWideChar(StringToOleStr(Path)),
Len,
Result,
Flags);
end;
//=============================================================================
// コンポーネントの初期化
//=============================================================================
constructor TplBrowseFolderEx.Create(AOwner: TComponent);
begin
inherited;
//初期状態の表示位置はデスクトップの真中
FTop := -1;
FLeft := -1;
FRootSpecialDir := CSIDL_DESKTOP;
FOptions := [bifReturnOnlyFSDirs];
end;
//-----------------------------------------------------------------------------
// フォルダー選択ダイアログのコールバック関数
// 表示位置の戻り値は,フォルダ選択後に移動した値は無効
//-----------------------------------------------------------------------------
function plBrowseExCallBack(hWindow: HWND; uMsg: UINT; lParam: LPARAM;
lpData: LPARAM): Integer; stdcall;
var
PBFFR : PBFFRecord;
ARect : TRect;
DlgWidth : Integer;
DlgHeight : Integer;
begin
case uMsg of
//ダイアログの表示開始初期状態
BFFM_INITIALIZED:
begin
PBFFR := pointer(lpData);
//初期選択フォルダ名が空でなければ,それを選択状態にする
//それにはダイアログウィンドウにBFFM_SETSELECTIONメッセージを送る
if lstrlen(PBFFR^.InitDir) > 1 then begin
SendMessage(hWindow, BFFM_SETSELECTION, 1, Integer(PBFFR^.InitDir));
end;
//表示位置の設定
//実際にはAPosX,APosYはグローバル変数なのでPBFFRを経由しなくてもよい
APosX := PBFFR^.X;
APosY := PBFFR^.Y;
//表示指定の座標値が共に-1であれば画面の中央に表示
//そうでないと時はAPosXとAPosYで指定する値
if (APosX < 0) and (APosY < 0) then begin
GetWindowRect(hWindow, ARect);
DlgWidth := ARect.Right - ARect.Left;
DlgHeight := ARect.Bottom - ARect.Top;
APosX := (GetSystemMetrics(SM_CXSCREEN) - DlgWidth) div 2;
APosY := (GetSystemMetrics(SM_CYSCREEN) - DlgHeight) div 2;
end else begin
end;
SetWindowPos(hWindow, 0, APosX, APosY, 0, 0, SWP_NOSIZE or SWP_NOZORDER);
end;
//フォルダまたはファイルが選択された
BFFM_SELCHANGED:
begin
//終了後にダイアログの位置(左上)を取得するためにフォルダを選択する度に
//その位置を監視
GetWindowRect(hWindow, ARect);
APosX := ARect.Left;
APosY := ARect.Top;
end;
end;
//このコールバック関数は常に0を返さなければいけない
Result := 0;
end;
//=============================================================================
// 本コンポの実行メソッド
// TOpenDialogのようにExecuteとしてみた
//
// 本コンポーネントの全プロパティ
//
// Directory Excuteメソッドを実行して[OK]でダイアログを閉じた時に返される
// ディレクトリのフルパス.読出し専用
// Title ダイアログ上部に表示するタイトル文字列
// RootDir ルートフォルダの指定.特殊フォルダ以外を指定する場合に設定
// RootSpecialDir ルートフォルダの指定.特殊フォルダをCSIDL値で指定する
// RootDirの設定が優先
// InitDir 表示開始時に選択するフォルダのフルパス文字列
// 特殊フォルダでの指定は,このコンポーネントでは不可
// Top ダイアログの表示位置(左上のY座標値).指定なしの時は画面中央
// Left ダイアログの表示位置(左上のX座標値).指定なしの時は画面中央
// Options 表示スタイルの値をBIF_XXXXXで指定する
// BIF_XXXXXにはusesにShlObjが必要
//=============================================================================
function TplBrowseFolderEx.Execute: Boolean;
var
DesktopShFolder : IShellFolder;
AFileDir : String;
BFFR : TBFFRecord;
IDList : PItemIDList;
BrowseInfo : TBrowseInfo;
Option : TplBrowseOp;
BPathName : array[0..MAX_PATH-1] of Char;
BDisplayName : array[0..MAX_PATH-1] of Char;
Malloc : IMalloc;
begin
Result := False;
SHGetMalloc(Malloc);
FillChar(BrowseInfo, SizeOf(BrowseInfo), #0);
//RootDirプロパティが空でない時はそれをルートとする
//そうでない時はRootSepecialDirを使用する
if FRootDir <> '' then begin
//デスクトップのシェルフォルダオブジェクト取得
//シェルではデスクトップが大元のルートとなる
SHGetDesktopFolder(DesktopSHFolder);
//それを用いて通常のフルパス文字列を項目識別子に変換
IDList := GetPItemIDFromFullPath(DesktopShFolder, FRootDir);
end else begin
//CSIDL値を項目識別子に変換
SHGetSpecialFolderLocation(Application.Handle, FRootSpecialDir, IDList);
end;
AFileDir := FInitDir;
BFFR.InitDir := PChar(AFileDir);
BFFR.X := FLeft;
BFFR.Y := FTop;
BrowseInfo.hwndOwner := Application.Handle;
BrowseInfo.pidlRoot := IDList;
BrowseInfo.pszDisplayName := BDisplayName;
BrowseInfo.lpszTitle := PChar(FTitle);
BrowseInfo.lpfn := Addr(plBrowseExCallBack);
BrowseInfo.lParam := Integer(Addr(BFFR));
//オプションの設定
BrowseInfo.ulFlags := 0;
for Option := Low(Option) to High(Option) do begin
if Option in FOptions then begin
BrowseInfo.ulFlags := BrowseInfo.ulFlags or Cardinal(BrowseOptions[Option]);
end;
end;
IDList := SHBrowseForFolder(BrowseInfo);
if IDList <> nil then begin
//BrowseInfo.pszDisplayNameだとパス部分がない名前
SHGetPathFromIDList(IDList,
Addr(BPathName));
FLeft := APosX;
FTop := APosY;
Result := True;
FDirectory := BPathName;
Malloc.Free(IDList);
end;
end;
end.
|