Delphi Programming / Object Pascal
[掲載 2004年02月14日] [更新 2012年04月09日] Delphi サンプルプログラム集
016_プリンタ設定ダイアログの遠隔操作
動作確認等
Windows 7 U64(SP1) + Delphi XE Pro
016_PrinterSetupDialog.zip [3,262 KB] 2012年04月09日版 (EXE 同梱)
2009年10月13日
プリンタ設定コンポーネントの記事から移動
2012年04月09日
サンプルコードと記事の修正・整備
プリンタの設定ダイアログの遠隔操作に関するサンプルコードです。
これらは、他のダイアログの遠隔操作の参考ともなるものです。ダイアログのハンドルとコントロールの ID 番号を使用して操作します。ただし、本ページのコードは、同一プロセス内で表示するダイアログでないと操作できない操作があります。
Windows API やフック関数、サブクラス化の説明はしていません。必要であれば、参考書や関係サイトなどで調べて下さい。
Delphi の [印刷] ダイアログには、オプションプロパティに多くの設定項目があります。以下のような遠隔操作が必要な場面はあまりないと思われますが、必要な場面もあるかも知れません。
ダイアログの設定などの操作は、[015_ プリンタ設定関係ダイアログ API の使用方法] のように、ダイアログ関係の Windows API を使用しても可能です。
ダイアログ上のコントロールを無効化または非表示にする例です。ユーザに操作してもらいたくないボタンやコンボボックスを使用不可にする場合の参考です。
図1
設計時画面
図2
変更したダイアログ
プリンタ名コンボボックス使用不可
[プロパティ] ボタン使用不可
[キャンセル] ボタン使用不可
(絶対設定しないとダメ?)
[ネットワーク] ボタン非表示
リスト1
ダイアログ上のコントロールの無効化や非表示
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
StdCtrls, Dialogs;
type
TForm1 = class (TForm)
Button1: TButton;
PrinterSetupDialog1: TPrinterSetupDialog;
procedure Button1Click(Sender: TObject);
procedure PrinterSetupDialog1Show(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.DFM}
// =============================================================================
// プリンタの設定ダイアログを表示
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
PrinterSetupDialog1.Execute;
end ;
// =============================================================================
// PrinterSetupDialog1のOnShowイベント処理
//
// プリンタセットアップダイアログを表示したらハンドルを取得して各コントロール
// の使用不可と非表示を設定。
// ダイアログの閉じるボタン[×]はキャンセルと同じ機能なので無効となる。
// =============================================================================
procedure TForm1.PrinterSetupDialog1Show(Sender: TObject);
var
WndDialog : HWND;
WndCotrol : HWND;
begin
// ダイアログのハンドル
WndDialog := Form1.PrintersetupDialog1.Handle;
// [プロパティ]ボタンのハンドルを取得して無効化
WndCotrol := GetDlgItem(WndDialog, 1025 );
EnableWindow(WndCotrol, False);
// [キャンセル]ボタンのハンドルを取得して無効化
WndCotrol := GetDlgItem(WndDialog, 2 );
EnableWindow(WndCotrol, False);
// プリンタ名選択コンボボックスのハンドルを取得して無効化
WndCotrol := GetDlgItem(WndDialog, 1136 );
EnableWindow(WndCotrol, False);
// ネットワークボタンのハンドルを取得して非表示にする
WndCotrol := GetDlgItem(WndDialog, 1037 );
ShowWindow(WndCotrol, SW_HIDE);
end ;
end .
02_ ダイアログのボタンクリックで自前のコードを実行
ダイアログのボタンに別の機能を持たせたい時の参考コードです。
次のコードでは、マウスの動作をフックすることによって実現しています。この様な処理で使用するフック関数は、基本的に戻り値が変更可能なものでないと実現できません。フック関数には、DLL 内に置かないと動作しないもの、同一スレッド内に置くものと、目的に応じてどちらでも構わないものがあります。
ダイアログ上のボタン操作のコード関係については、次の項目、[03_ダイアログのサイズを変更してボタンを追加] も参考にしてください。
図3
設計時画面
図4
実行画面
[プロパティ] ボタンをクリックすると全く別のダイアログが現れる
[キャンセル] ボタンはコードなし
(クリックしても何も起こらない)
リスト2
ダイアログのボタンクリックの動作を、WH_MOUSE フック関数を使用して変更
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
StdCtrls, Dialogs;
type
TForm1 = class (TForm)
Button1: TButton;
PrinterSetupDialog1: TPrinterSetupDialog;
procedure Button1Click(Sender: TObject);
procedure PrinterSetupDialog1Show(Sender: TObject);
procedure PrinterSetupDialog1Close(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
// フック関数のコールバック関数を、コード内の任意の場所で使用するために、
// interface部で定義しておく
function WatchProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall ;
var
Form1: TForm1;
implementation
{$R *.DFM}
var
MouseHook : HHOOK = 0 ;
WndProperty : HWND;
WndCancel : HWND;
// =============================================================================
// プリンタセットアップダイアログを表示
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
PrinterSetupDialog1.Execute;
end ;
// =============================================================================
// PrinterSetupDialog1のOnShowイベント処理
//
// プリンタセットアップダイアログを表示したらハンドルを取得してWH_MOUSEフック
// 関数をインストール。また[プロパティ]ボタンのアクセレータキーを無効にするた
// めにキャプションを変更
// =============================================================================
procedure TForm1.PrinterSetupDialog1Show(Sender: TObject);
var
WndDialog : HWND;
begin
// ダイアログのハンドル
WndDialog := Form1.PrintersetupDialog1.Handle;
// [プロパティ]ボタンのハンドル取得
WndProperty :=GetDlgItem(WndDialog,1025 );
// [キャンセル]ボタンのハンドル取得
WndCancel := GetDlgItem(WndDialog,2 );
// [プロパティ]ボタンのキャプションを変更
// アクセラレータキーの定義部分を削除
SetWindowText(WndProperty, PChar('プロパティ' ));
// フック関数をダイアログのスレッドとしてインストール
MouseHook := SetWindowsHookEx(WH_MOUSE,
@WatchProc,
hInstance,
GetWindowThreadProcessID(WndDialog, nil ));
end ;
// =============================================================================
// PrinterSetupDialog1のOnCloseベント処理
//
// ダイアログを閉じる時は、フック関数をアンインストール
// =============================================================================
procedure TForm1.PrinterSetupDialog1Close(Sender: TObject);
begin
if MouseHook <> 0 then begin
UnhookWindowsHookEx(MouseHook);
MouseHook := 0 ;
end ;
end ;
// -----------------------------------------------------------------------------
// WH_MOUSEフック関数
// ここではボタンの左クリック時のみ処理
// したがって、該当コントロールにフォーカスがある時の[Enter]では既定の動作となる
// アプリケションで処理する場合はフック関数の戻値をResult=-1とする
//
// MouseHook フック関数のハンドル
// WndProperty プロパティボタンのハンドル
// WndCancel キャンセルボタンのハンドル
// はグローバル変数
// -----------------------------------------------------------------------------
function WatchProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall ;
var
Msg : PMouseHookStruct;
begin
Result := 0 ;
if nCode < 0 then begin
Result := CallNextHookEx(MouseHook, nCode, wParam, lParam);
exit;
end ;
Msg := PMouseHookStruct(lParam);
// nCodeがHC_ACTIONの時のみ操作可能
if nCode = HC_ACTION then begin
// ハンドルが[プロパティ]ボタンの時
if Msg.hwnd = WndProperty then begin
if (wParam = WM_LBUTTONUP) then begin
Application.MessageBox(
'[プロパティ]ボタンは使用できません。 ' ,
PChar('プロパティの設定' ),
MB_ICONINFORMATION or MB_OK);
Result := -1 ;
end ;
end ;
// ハンドルが[キャンセル]ボタンの時は単に無効にするだけ
if Msg.hwnd = WndCancel then begin
if (wParam = WM_LBUTTONDOWN) then begin
Result := -1 ;
end ;
end ;
end ;
CallNextHookEx( MouseHook, nCode, wParam, lParam);
end ;
end .
上のコードでは、マウスのフック関数を使用しましたが、以下の様に、ボタン専用のコールバック関数で処理する方法もあります。この場合、[プロパティ] ボタン専用なので便利な場合もあります。
なお、上のコードでは、WM_LBUTTONDOWN と WM_LBUTTONUP の両方を使用していますが、Windows の標準動作では、マウスのボタンを放した時、つまり WM_LBUTTONUP の時に動作します。
リスト3
ダイアログのボタンクリックの動作を、WindowProc のサブクラス化で変更
copy code
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
StdCtrls, Dialogs;
type
TForm1 = class (TForm)
Button1: TButton;
PrinterSetupDialog1: TPrinterSetupDialog;
procedure Button1Click(Sender: TObject);
procedure PrinterSetupDialog1Show(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
// サブクラス関数の定義
function WatchProc(hWindow: HWND; uMsg: UINT; wParam: WPARAM;
lParam: LPARAM): LRESULT; stdcall ;
var
Form1: TForm1;
implementation
{$R *.DFM}
var
OriginalProc : Pointer;
// =============================================================================
// プリンタセットアップダイアログを表示
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
PrinterSetupDialog1.Execute;
end ;
// =============================================================================
// PrinterSetupDialog1のOnShowイベント処理
//
// プリンタセットアップダイアログを表示したらハンドルを取得してそのハンドルを
// 元に[プロパティ]ボタンのサブクラス関数を定義
// =============================================================================
procedure TForm1.PrinterSetupDialog1Show(Sender: TObject);
var
WndDialog : HWND;
WndProperty : HWND;
begin
// ダイアログのハンドル
WndDialog := PrintersetupDialog1.Handle;
// [プロパティ]ボタンのハンドル取得
WndProperty :=GetDlgItem(WndDialog, 1025 );
// [プロパティ]ボタンのキャプションを変更
SetWindowText(WndProperty,PChar('プロパティ' ));
// ボタンのWindowProcをサブクラス化
// 元のWindowProcをOrignalProcに保存しWatchProcを新WindowProcに
OriginalProc := Pointer(SetWindowLong(WndProperty,
GWL_WNDPROC,
NativeInt(@WatchProc)));
end ;
// -----------------------------------------------------------------------------
// [プロパティ]ボタンのサブクラス化関数
// ここではボタンの左クリック時のみ処理
// -----------------------------------------------------------------------------
function WatchProc(hWindow: HWND; uMsg: UINT; wParam: WPARAM;
lParam: LPARAM): LRESULT; stdcall ;
begin
case uMsg of
WM_LBUTTONUP:
begin
ShowMessage('[プロパティ]ボタンをクリック' );
// メッセージを処理したら1を返す
Result := 1 ;
exit;
end ;
end ;
// 通常の処理を継続
Result := CallWindowProc(OriginalProc, hWindow, uMsg, wParam, lParam);
end ;
end .
ダイアログのサイズを大きくし、そこにボタンを追加します。
追加したボタンのサブクラスを作成して、その中でクリックを検出して処理します。ボタンクリックの処理は、ローカルフックでメッセージを捕らえる方法も考えられます。
Delphi のフォーム上にコントロールを配置して、自前でダイアログを作成する場合であれば、実行時にボタンを生成する際に、そのイベントにメソッドを割当てたり、そのボタンのクラスのイベントで処理することができます。
一方、このサンプルのように、Windows が提供しているダイアログ上に配置したボタンの場合、イベントを定義して使用することもできますが、Delphi で使用可能なイベント全てが使えるわけではありません。例えば OnClick には反応しません。
図5
設計時画面
図6
追加ボタンをクリック
ダイアログの高さを高くして
[新規ボタン] というボタンを追加
リスト4
ダイアログのサイズを変更して、ボタンを追加
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
StdCtrls, Dialogs, Buttons;
type
TForm1 = class (TForm)
Button1: TButton;
PrinterSetupDialog1: TPrinterSetupDialog;
procedure Button1Click(Sender: TObject);
procedure PrinterSetupDialog1Show(Sender: TObject);
procedure PrinterSetupDialog1Close(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
MYButton : TButton;
OriginalProc : TWndMethod;
procedure SubClassProc(var Message:TMessage);
end ;
var
Form1: TForm1;
implementation
uses DebugWndUnit;
{$R *.DFM}
// =============================================================================
// [新規ボタン]用のサブクラス関数
// =============================================================================
procedure TForm1.SubClassProc(var Message: TMessage);
var
MousePos : TPoint;
RectWnd : TRect;
KeyState : TKeyboardState;
begin
OriginalProc(Message);
if (Message.Msg = WM_LBUTTONDOWN) then begin
Application.MessageBox('追加したボタンが押されました。 ' ,
PChar('新規設定' ),
MB_ICONINFORMATION or MB_OK);
end ;
end ;
// =============================================================================
// プリンタセットアップダイアログを表示
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
PrinterSetupDialog1.Execute;
end ;
// =============================================================================
// PrinterSetupDialog1のOnShowイベント処理
//
// プリンタセットアップダイアログを表示したらボタンを作成
// 追加したボタンにID番号も割り振る(何かのテストで、IDでアクセス可能なように)
// =============================================================================
procedure TForm1.PrinterSetupDialog1Show(Sender: TObject);
var
WndDialog : HWND;
WndNetBtn : HWND;
RectDialog : TRect;
DialogHeight : Integer;
DialogWidth : Integer;
RectBtn : TRect;
begin
// ダイアログのハンドル取得
WndDialog := Form1.PrintersetupDialog1.Handle;
// ダイアログの位置とサイズを取得
GetWindowRect(WndDialog, RectDialog);
DialogHeight := RectDialog.Bottom - RectDialog.Top;
DialogWidth := RectDialog.Right - RectDialog.Left;
// ダイアログの高さ方向のサイズを変更
SetWindowPos(WndDialog,
0 ,
0 ,
0 ,
DialogWidth,
DialogHeight + 40 ,
SWP_NOMOVE or SWP_SHOWWINDOW);
// [ネットワーク]ボタンのハンドル取得
WndNetBtn := GetDlgItem(WndDialog, 1037 );
// その位置とサイズを取得
GetWindowRect(WndNetBtn, RectBtn);
// ボタンを新規作成
MyButton := TButton.Create(nil );
// ボタンの動作はサブクラスで対応
OriginalProc := MyButton.WindowProc;
MyButton.WindowProc := SubClassProc;
// Parentの設定.Parentを指定しないと表示しない
MyButton.ParentWindow := WndDialog;
// 作成したボタンの位置とサイズを設定
// [ネットワーク]ボタンの少し下に位置
// 高さと幅は[ネットワーク]ボタンと同じにする
RectBtn.TopLeft := MyButton.ScreenToClient(RectBtn.TopLeft);
RectBtn.BottomRight := MyButton.ScreenToClient(RectBtn.BottomRight);
MyButton.Left := RectBtn.Left;
MyButton.Top := RectBtn.Bottom + 5 ;
MyButton.Width := RectBtn.Right - RectBtn.Left;
MyButton.Height := RectBtn.Bottom - RectBtn.Top;
MyButton.Caption := '新規ボタン' ;
// MyButtonのID値を9000にする
SetWindowLong(MyButton.Handle, GWL_ID, 9000 );
end ;
// =============================================================================
// PrinterSetupDialog1のOnCloseベント処理
//
// [プリンタの設定]ダイアログを閉じる時は、作成したボタンを破棄
// =============================================================================
procedure TForm1.PrinterSetupDialog1Close(Sender: TObject);
begin
if Assigned(MYButton) then FreeAndNil(MYButton);
end ;
end .
上のコードでは、WM_LBUTTONDOWN メッセージを処理していますが、実際のアプリケーションでボタンをクリックするという動作は、
マウスの左ボタン押して、放すとクリックとなる
マウスの左ボタンを押したまま、そのボタン以外の場所で放すと何もしない
ボタンにフォーカスがある時は、[Enter] キーでクリックと同じ動作となる
となっています。実際、Delphi の OnClick イベントもこのような動作となっています。
これを実装したのが次のコードです。単に [Enter] キーを検出して処理するだけですと、追加したボタンの [Enter] キーで表示されるダイアログを、[Enter] キーで閉じると、また [Enter] キーを押した時と同じ動作となってしまいます。[Enter] キーで操作する限り、プログラムが永遠に終了しなくなってしまいます。この対策もしています。
この他、アクセラレータキー (ニーモニック) を処理するには、ダイアログ本体側でメッセージを処理することになります。
TEdit 等を配置した場合は、Delphi の一般的な方法と同じです。TEdit の生成の前に、例えば TLabel を生成して、それにアクセラレータキーを設定します。そうすると、そのキー操作で TEdit にフォーカス移動します。これはタブ順が TLabel の次に TEdit になることを利用します。
このように、
本格的に実装するとなると結構面倒なことが分かります。このようなテストをしてみると、Delphi の VCL は、実にうまくできていることを実感させられます。
リスト5
ボタンクリック動作を、Windows の動作仕様と同じにするコード例
copy code
// =============================================================================
// [新規ボタン]用のサブクラス関数
//
// 以下の対策をしたコード
// (1) 左ボタンを押下したまま、ボタン以外の場所で放した時は動作しない
// (2) [Enter]キーの押下も検出する
// (3) MessageBoxの[OK]を[Enter]で終了した場合の、MessageBoxの再表示を防止
// =============================================================================
procedure TForm1.SubClassProc(var Message: TMessage);
var
MousePos : TPoint;
RectWnd : TRect;
KeyState : TKeyboardState;
begin
OriginalProc(Message);
if not Assigned(MYButton) then exit;
GetCursorPos(MousePos);
GetWindowRect(MYButton.Handle, RectWnd);
GetKeyboardState(KeyState);
if ((Message.Msg = WM_LBUTTONUP) and (PtInRect(RectWnd, MousePos))) or
((Message.WParam = VK_RETURN) and (KeyState[VK_RETURN] > 0 )) then begin
KeyState[VK_RETURN] := 1 ;
SetKeyboardState(KeyState);
Application.MessageBox('追加したボタンが押されました。 ' ,
PChar('新規設定' ),
MB_ICONINFORMATION or MB_OK);
end ;
end ;
ダイアログで設定した項目の値を取得するサンプルです。
ダイアログの OnClose イベント内で取得します。ここでは、プリンタ名のコンボボックスの値、用紙サイズのコンボボックスの値と、印刷の向きのラジオボタンの状態を取得して表示します。
図7
設計時画面
図8
ダイアログを閉じた後の画面
設定内容がメモに表示されている
[Button1] はダイアログ表示用
リスト6
ダイアログで設定して値を取得するサンプルプログラム
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
StdCtrls, Dialogs, ExtCtrls;
type
TForm1 = class (TForm)
PrinterSetupDialog1: TPrinterSetupDialog;
Memo1: TMemo;
Panel1: TPanel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure PrinterSetupDialog1Close(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.DFM}
// =============================================================================
// プリンタセットアップダイアログを表示
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
PrinterSetupDialog1.Execute;
end ;
// =============================================================================
// PrinterSetupDialog1のOnCloseベント処理
// ダイアログを閉じる時に各コンボボックスの値を取得
// =============================================================================
procedure TForm1.PrinterSetupDialog1Close(Sender: TObject);
var
WndDialog : HWND;
PrinterName : array [0 ..127 ] of Char;
PaperName : array [0 ..127 ] of Char;
BinName : array [0 ..127 ] of Char;
WndCheckOrient : HWND;
begin
Memo1.Clear;
// ダイアログのハンドルを取得
WndDialog := PrintersetupDialog1.Handle;
// プリンタ名の文字列を取得してTMemoに表示
GetDlgItemText(WndDialog, 1136 , PrinterName, SizeOf(PrinterName));
Memo1.Lines.Add(String (PrinterName));
// 用紙サイズ名を取得してTMemoに表示
GetDlgItemText(WndDialog, 1137 , PaperName, SizeOf(PaperName));
Memo1.Lines.Add(String (PaperName));
// 給紙方法の文字列を取得してTMemoに表示
GetDlgItemText(WndDialog, 1138 , BinName, SizeOf(BinName));
Memo1.Lines.Add(String (BinName));
// [印刷の向き]のハンドルを取得
WndCheckOrient := GetDlgItem(WndDialog,1056 );
// 印刷の向きのラジオボタンチェック状態を調査し表示
if SendMessage(WndCheckOrient, BM_GETCHECK, 0 , 0 ) = BST_CHECKED then begin
Memo1.Lines.Add('印刷の向き 縦' );
end else begin
Memo1.Lines.Add('印刷の向き 横' );
end ;
end ;
end .
こんどは、ダイアログが閉じた時ではなく、ダイアログで設定を変更すると、その変更した値をリアルタイムで取得するサンプルです。
フック関数の WH_CALLWNDPROCRET を、ローカルフックで使用して処理しています。
図9
設計時画面
図10
実行画面
設定内容を変更すると...
変更した設定内容を逐次表示する
[Button1] はダイアログ表示用
リスト7
ダイアログの設定内容をリアルタイムで取得するサンプル
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
StdCtrls, Dialogs, ExtCtrls;
type
TForm1 = class (TForm)
PrinterSetupDialog1: TPrinterSetupDialog;
Memo1: TMemo;
Panel1: TPanel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure PrinterSetupDialog1Show(Sender: TObject);
procedure PrinterSetupDialog1Close(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
// フック関数のコールバック関数を、コード内の任意の場所で使用するために、
// interface部で定義しておく
function WatchProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall ;
var
Form1: TForm1;
implementation
{$R *.DFM}
var
HookHandle : HHOOK = 0 ;
WndDialog : HWND;
// =============================================================================
// プリンタセットアップダイアログを表示
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
PrinterSetupDialog1.Execute;
end ;
// =============================================================================
// PrinterSetupDialog1のOnShowイベント処理
//
// プリンタセットアップダイアログを表示したらハンドルを取得して
// WH_CALLWNDPROCRETフック関数をインストール。
// =============================================================================
procedure TForm1.PrinterSetupDialog1Show(Sender: TObject);
begin
Memo1.Clear;
// ダイアログのハンドルを取得
WndDialog := PrintersetupDialog1.Handle;
// フック関数をダイアログのスレットとしてインストール
HookHandle := SetWindowsHookEx(WH_CALLWNDPROCRET,
@WatchProc,
hInstance,
GetWindowThreadProcessID(WndDialog, nil ));
end ;
// =============================================================================
// PrinterSetupDialog1のOnCloseベント処理
//
// ダイアログを閉じる時は、フック関数をアンインストール
// =============================================================================
procedure TForm1.PrinterSetupDialog1Close(Sender: TObject);
begin
if HookHandle<>0 then begin
UnhookWindowsHookEx(HookHandle);
HookHandle := 0 ;
end ;
end ;
// -----------------------------------------------------------------------------
// WH_CALLWNDPROCRETフック関数
// -----------------------------------------------------------------------------
function WatchProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM):
LRESULT; stdcall ;
var
Msg : PCWPRetStruct;
PrinterName : array [0 ..127 ] of Char;
PaperName : array [0 ..127 ] of Char;
BinName : array [0 ..127 ] of Char;
begin
Result :=0 ;
if nCode < 0 then begin
Result := CallNextHookEx(HookHandle, nCode, wParam, lParam);
exit;
end ;
Msg := PCWPRetStruct(lParam);
// nCodeがHC_ACTIONの時のみ操作可能
if nCode = HC_ACTION then begin
// コンボボックスの選択状態が変化した時だけ処理
// 上位16bitに通知コードが入っている
if HIWORD(Msg.wParam) = CBN_SELCHANGE then begin
// 下位16bitにコントロールのIDが入っている
// プリンタ名を変更すると用紙サイズと給紙装置名も変わる可能性が
// あるのでそれらも表示
if LOWORD(Msg.wParam) = 1136 then begin
GetDlgItemText(WndDialog, 1136 , PrinterName, SizeOf(PrinterName));
GetDlgItemText(WndDialog, 1137 , PaperName, SizeOf(PaperName));
GetDlgItemText(WndDialog, 1138 , BinName, SizeOf(BinName));
Form1.Memo1.Lines.Add((PrinterName));
Form1.Memo1.Lines.Add((PaperName));
Form1.Memo1.Lines.Add((BinName));
// 用紙サイズ
end else if LOWORD(Msg.wParam) = 1137 then begin
GetDlgItemText(WndDialog, 1137 , PaperName, SizeOf(PaperName));
Form1.Memo1.Lines.Add(String (PaperName));
// 給紙装置
end else if LOWORD(Msg.wParam) = 1138 then begin
GetDlgItemText(WndDialog, 1138 , BinName, SizeOf(BinName));
Form1.Memo1.Lines.Add(String (BinName));
end ;
end ;
end ;
CallNextHookEx(HookHandle, nCode, wParam, lParam);
end ;
end .
06_ ダイアログの項目値をコードで選択 ( 項目値の削除 )
ダイアログの項目をコードで設定する例です。
ついでにコンボボックスのリストも制御して、'A5' または 'A4' という文字が含まれる用紙サイズだけをコンボボックスのリストにしてしまいます。
図11
設計時画面
図12
実行してダイアログ表示
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
StdCtrls, Dialogs, Buttons;
type
TForm1 = class (TForm)
Button1: TButton;
PrinterSetupDialog1: TPrinterSetupDialog;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
procedure PrinterSetupDialog1Show(Sender: TObject);
procedure FormShow(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.DFM}
// =============================================================================
// 選択するプリンタ名をセット
// この名前を含むプリンタ名をコンボボックスから自動選択する(先頭一致検索)
// =============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
Edit1.Text := 'EPSON VP' ;
end ;
// =============================================================================
// プリンタセットアップダイアログを表示
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
PrinterSetupDialog1.Execute;
end ;
// =============================================================================
// PrinterSetupDialog1のOnShowイベント処理
//
// 以下を自動設定
// (1) ダイアログを表示したら指定プリンタ名をリストから選択
// (2) 用紙サイズ名のA5またA4以外のサイズは、コンボボックスのリストから削除
// (3) 印刷の向きは横とする
// =============================================================================
procedure TForm1.PrinterSetupDialog1Show(Sender: TObject);
var
WndDialog : HWND;
WndPrinterNameCombo : HWND;
WndPaperNameCombo : HWND;
WndOrientation : HWND;
StrSearch : String ;
AIndex : Integer;
i : Integer;
ACount : Integer;
ALen : Integer;
pStringText : PChar;
begin
// ダイアログのハンドルを取得
WndDialog := PrinterSetupDialog1.Handle;
// プリンタ名選択コンボボックスのハンドル取得
WndPrinterNameCombo := GetDlgItem(WndDialog, 1136 );
// 検索するプリンタ名(先頭一致検索)
StrSearch := Trim(Edit1.Text);
AIndex := SendMessage(WndPrinterNameCombo,
CB_FINDSTRING,
-1 ,
LongInt(PChar(StrSearch)));
// 見つかったらそれを選択状態にする
if AIndex >= 0 then begin
SendMessage(WndPrinterNameCombo, CB_SETCURSEL, AIndex, 0 );
end ;
// 用紙サイズ選択コンボボックスのハンドルを取得
WndPaperNameCombo := GetDlgItem(WndDialog, 1137 );
// その項目数を取得
ACount := SendMessage(WndPaperNameCombo, CB_GETCOUNT, 0 , 0 );
// 削除を伴う処理は降順で行う
for i := ACount - 1 downto 0 do begin
// メモリ確保のために文字列の長さを取得して、メモリ確保
ALen := SendMessage(WndPaperNameCombo, CB_GETLBTEXTLEN, i, 0 );
pStringText := StrAlloc(ALen + 1 );
try
// i番目の項目の文字列を取得
SendMessage(WndPaperNameCombo, CB_GETLBTEXT, i, LongInt(pStringText));
StrSearch := pStringText;
finally
StrDispose(pStringText);
end ;
// 用紙サイズ名にA5かA4が含まれていない場合は項目から削除
if ((Pos('A4' , StrSearch) = 0 ) and (Pos('A5' , StrSearch) = 0 )) then begin
SendMessage(WndPaperNameCombo, CB_DELETESTRING, i, 0 );
end ;
end ;
// 最初の項目を選択状態にしておく
SendMessage(WndPaperNameCombo, CB_SETCURSEL, 0 , 0 );
// [印刷の向き]の[横]のラジオボタンのハンドルを取得してクリック
WndOrientation := GetDlgItem(WndDialog, 1057 );
SendMessage(WndOrientation, BM_CLICK, 0 , 0 );
end ;
end .
ダイアログのコントロールの ID の値は、[645_ ウィンドウの階層のツリー表示] のようなプログラムで調べることができます。
一般的なウィンドウの場合には、ウィンドウのハンドル値も、この ID 値もシステムが自動で割り当てます。ウィンドウを起動、表示するたびに違う値となります。ウィンドウのハンドルの値は変えることはできませんが、ID の値は、[660_他のアプリへ文字列送信 (Delphi 同士)] のサンプルのように、任意の値に設定することができます。
自分で作成するアプリケーションで、任意の値に設定しておくと、後でそのアプリなりフォームのコントロールを制御したい時に便利です。