●Gen's FAQ集以外の日本語でのFAQ&Tips集
Gen's 不定期(^^;ローテク講座
いのですか?
[A]
いくつか方法がありますが、簡単なやり方を...
ポイントはTagプロパティの活用!!
1:呼び出し元のコンポーネントのTagプロパティの番号を参照する
procedure TForm1.AnyButtonClick(Sender:TObject);
begin
If TButton(Sender).Tag = 1 Then begin
{ボタン1の処理};
end;
end;
数が増えてくればcase文で分岐させればいいです。
2:呼び出し元コンポーネントのNameプロパティを参照する
If TButton(Sender).Name = 'Button1' Then begin
{ボタン1の処理};
end;
[補足]
上の例では、TButton型にキャストしていますが、これを汎用にするには
TComponent(Sender).Tag とやればいいです。VBのコントロール配列も
1の、Tagプロパティを使えば簡単に実現できます。「Tagプロパティは何
に使うんだろう?」と思っていた方には、目から鱗、知ってしまえばしょ
っちゅう使います。
[目次] [次に進む]
[A]
項目の追加にはAPIを使います。また、メニューの項目を識別するために
独自のメニューIDを定義します。システムメニューがクリックされた時は、
WM_SYSCOMMANDというメッセージが飛んできますので、これをキャッチし
て追加した項目に対するイベントハンドラを呼んでやります。
FormのPrivateセクションに、
Procedure WMSYSCOMMAND(var Msg : TWMSYSCOMMAND);Message WM_SYSCOMMAND;
と宣言しておいて、{*1}
implementation
Const
Menu_ID = 10; {追加メニューのメニューID}
{システムメニューに独自の項目を追加する}
procedure TForm1.FormCreate(Sender: TObject);
var
Mn : HMenu;
begin
{*2}Mn := GetSystemMenu(Handle,False);
If Not AppendMenu(Mn,MF_STRING,Menu_ID,'項目1') Then
ShowMessage('Error');
If Not InsertMenu(Mn,2, MF_BYPOSITION Or MF_STRING,Menu_ID+1,'項目2')
Then
ShowMessage('Error');
end;
{追加した項目に対するイベントハンドラの処理}
Procedure TForm1.WMSYSCOMMAND(var Msg : TWMSYSCOMMAND);
Begin
If Msg.CmdType = Menu_ID Then begin
{項目1をクリックした時の処理}
end;
If Msg.CmdType = Menu_ID + 1 Then begin
{項目2をクリックした時の処理}
end;
{*3}DefaultHandler(Msg);
End;
[補足]
{*1}この宣言のしかたは、他のMessageを捕捉する時も同じようなやり方
で宣言出来ますので覚えておいて損はないです。
{*2}Handleの所をApplication.Handleに変えるとアプリケーションのシ
ステムコマンドになります。
{*3}通常のシステムメニュー項目がクリックされた場合、その処理をす
るためののためのオマジナイ。
[Help]
GetSystemMenu,AppendMenu,InsertMenu,WM_SYSCOMMAND
[前に戻る] [目次] [次に進む]
[A]
そのアプリのウィンドウハンドル、及びダイアログのキャプションの名前が
分かっていれば比較的簡単にできます。他のアプリを制御する時は、
Application.ProcessMessagesでループさせる事が多いので以下そのような
コードを書きます。ダイアログのキャプションが '警告' だとすると
SomeAppWHnd: HWnd;{制御するアプリのウィンドウハンドル}
DlgWHnd : HWnd; {ダイアログのウィンドウハンドル}
isDone {メッセージを送ったかどうかのフラグ}
isDone := false;
repeat
Application.ProcessMessages;
DlgWHnd := FindWindow(NIL,'警告');
if (DlgWHnd 0) and (not isDone) then begin
SendMessage(DlgWHnd,WM_COMMAND,IDCANCEL,0);
isDone := true;
end;
until SomeAppWHnd = 0;
わざわざフラグを立てているのは、Messageを送っても、直ちにダイアログが
終了するわけではない(そのダイアログの親が処理をする時間がかかる)の
で、何度もMessageを送らなくていいようにするためです。試しにフラグなし
でやってみると、ひたすらMessageを送っているのが分かります
[補足]
SendMessageを使わずに、EndDialogというAPIを使っても出来ます。但し、
ダイアログがモードレスの場合後ろに隠れてしまうだけで、終了している
わけではないようなので注意が必要です。通常は、こちらのやり方でいい
でしょう。
後、そのダイアログの親を知りたければAPIのGetParentを使います。
[help]
SendMessage、EndDialog、GetParent
[前に戻る] [目次] [次に進む]
[A]お薦めできる方法かどうかは分かりませんが...以下の方法を試してみてください。
ちょっとトリッキーなやり方ですが、TStringの持っているTObject配列を
使えば可能です。このデータは4バイトのポインタ型なので、整数を保持
させるためにキャストします。例えば、ListBoxに '項目1' という値とと
もに、12345という値を格納するには、
ListBox1.Items.AddObject('項目1',TObject(12345));
取り出す時には、上で追加した項目が先頭の場合、
LongInt( ListBox1.Items.Objects[0]);
^^^何番目の項目か
[補足]
保持する時はTObjectでキャスト、取り出す(参照する)時はTObjectが4バイト
のポインタ型だという点に気を付けてキャストするようにして下さい。
2つ以上の数字を格納したければ、例えば下のように自分でオブジェクトの
宣言をしてから使えばいいと思うのですが、これについてはもう少し実験し
てから書きます。
type
hogehoge = class(TObject)
ItemOne: Integer;
ItemTwo: Integer;
end;
[help]
TObject、TStringList、TString、型キャスト
[前に戻る] [目次]
[A]
FindComponentメソッドを使います。例えば
for i := 1 to 5 do
Form1.FindComponent('EditBox'+IntToStr(i)) as TEditBox do
Text := 'Default';
この例では、フォームにEditBoxが5つあるとして、そのすべてに'Default'
という文字列を入れます。Delphiでは、コンポーネントをパレットから貼
り付けていくと、デフォルトでEditBox1、EditBox2、・・・、と名前がついて
いきますので、これを利用します。
[補足]
また、FindComponentはTComponent型を返すので、2行目の所で目的の型
(この場合TEditBox)にキャストしてやってます。実際に使う時は例外など
にも対処するようにしておいてくださいね(^^)
関連したことで、ヘルプでComponentsプロパティ、ComponentCountsプロパティ
の使用例を見ると、色々使い方が浮かんでくると思います。
[help]
FindComponent
[前に戻る] [目次] [次に進む]
次のようなコードをコンパイルすると VK_N の部分が、「未定義の識別子
です」といって怒られます。なぜですか?VK_F10等はすんなり通ります。
OnKeyDown イベントのなかで
if Key = VK_N then begin
{Nキーが押された時の処理}
end;
[A]
'A'〜'Z'、'0'〜'9'のキーの仮想キーコードが実は定義されていません。
何故かは知りません。判別式の部分を以下のようにしてみて下さい。
Chr(Key) = 'N'
送られてくるKeyをcharにキャストするわけです。
[前に戻る] [目次] [次に進む]
[A]
APIのExtractIconを使います。次のコードを参考にして下さい。
var
Icon: TIcon;
begin
Icon := TIcon.Create;
Icon.Handle := ExtractIcon(hInstance, 'EXEファイル名', 0);
Image1.Picture.Icon := Icon; {^^^*1}
Icon.Free;
end;
{^^^*1}の部分について...
これを-1とするとそのファイルの中にあるアイコンの総数を得られるの
ですが、これをするとメモリを食い潰すというバグ付きのAPIなので気
を付けてください。決してDelphiが悪いのではありません。例のように
0を指定すると最初に登録されているアイコンのハンドルが得られます。
Win32でも同じAPIがありますが、バグがそのままかどうかは未確認です。
[前に戻る] [目次] [次に進む]
[A] リソースファイル(〜.RES)の名前をプロジェクトの名前(〜.DPR)と同じ にしているのが原因です。Delphiではプロジェクトと同じ名前のリソー スファイルをIDEが管理します。ですから、何か別の名前で保存して 下さい。 また、格納するリソースの名前は、すべて大文字で記述して下さい。 [前に戻る] [目次] [次に進む]
[A]
フォーカスの移動にはSendMessage(API)を使うと比較的簡単に行えます。
以下の例を参考にしてみて下さい。
{Enterキーで次のコントロールに移動}
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if Key = #13 then begin
SendMessage(MyForm.Handle, WM_NEXTDLGCTL, 0, 0);
Key := #0;
end;
if Key #0 then inherited KeyPress(Key);
end;
{上下キーで前後のコントロールに移動}
procedure TForm1.Edit1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
Direction: Word;
begin
if (Key = VK_UP) or (Key = VK_DOWN) then begin
if Key = VK_UP then Direction := 1
else Direction :=0;
SendMessage(Handle, WM_NEXTDLGCTL, Direction, 0);
end
else inherited KeyDown(Key, Shift);
end;
{制限字数になったら次のコントロールに移動}
procedure TForm1.Edit1Change(Sender: TObject);
begin
if Length(Edit1.Text) >= Edit1.MaxLength then
SendMessage(Handle, WM_NEXTDLGCTL, 0, 0);
end;
※長さの判定の所を以下のように変えて、複数のEditControlの
共有イベントにすれば制限字数の異なるEditControlがあっても
イベントハンドラの記述は一つですみます。
Length(TEdit(Sender).Text) >= TEdit(Sender).MaxLength
----------------------------------------------------------------
※OnKeyPressとOnKeyDownの違いについては、ヘルプを参照して
勉強して下さい。
※SendMessage(Handle, WM_NEXTDLGCTL, 0, 0);
の第3引数が0であれば次のコントロール、
1であれば前のコントロール
に移動できるようです。
[前に戻る] [目次] [次に進む]
[A]
フォームのPrivateメソッドとして次のような手続きを定義してそれを使ってみて下さい。
{delay相当の手続き(単位はミリ秒)}
procedure TForm1.Delay(ms:integer);
var
StartCount:longint;
begin
StartCount:=GetTickCount;
repeat
Application.ProcessMessages;
until ((GetTickCount-StartCount) >= Longint(ms));
end;
[前に戻る] [目次] [次に進む]
[A]
これは、Delphiのバグです。VCLソースを持っている方は、ataruさんの私家版FAQ
を参考にしてソースを書き換えて下さい。お持ちでない方は、
・ファイル名については、自分で、'\\'→'\'へ変換するようにプログラムを組む。
・Dialogが開けなくなる点については、以下のような一行を加えてみて下さい。
if OpenDialog1.Execute then begin
{処理・・・}
OpenDialog1.FileName := ''; {この行を加えます。}
end;
[前に戻る] [目次] [次に進む]
1.memoコンポーネントで、現在のキャレットの位置に、文字列を挿入す
るには
2.上記1.でファイルから読み込んだ時(ReadLnを使った時)など、改行
コードは読み込まれませんが、改行させたい時はどうすれば
3.指定の行番号にキャレットを移動させるには
4.現在キャレットのある行の行番号を求めるには
5.特定の行の色を変えるには
[A]
1.Memo1.SelText := Edit1.Text;
^^^^^^^^^^要するに挿入したい文字列(255文字以内)
Memo1.SetSelTextBuf(PChar型変数) (それ以上の文字数の場合)
とすれば出来ます。
2.Memo1.SelText := Edit1.Text+#13+#10;
^^^^^^^^この部分を追加します。
Chr(13)+chr(10)でも良いです。
3.Memo1.SelStart:=SendMessage(Memo1.Handle,EM_LineIndex,行番号,0);
としてやればOK
4.Memo1.SelStart:=SendMessage(Memo1.Handle,EM_LineFormChar,SelStart,0);
としてやればOK
5.MemoはWindowsの標準コントロールを使っているので、TMemoを使う限
り出来ません。delphi2.0ではTRichEditコントロールが有るので、こ
れを使えば出来ます。
[補足]
[help]
[前に戻る] [目次] [次に進む]
[A]
コマンドラインを調べるために以下の関数が使えます。
ParamCount ・・・ コマンドライン引数の個数(Word型)
ParamStr(n) ・・・ n番目のコマンドライン文字列(Strings型)
PramStr(0)は、Application.ExeNameと同じ事で、実行プログラムのフルパス
を返します。
あと、CmdLine(PChar型)と言う変数を参照すると、コマンドラインがそのまま
(スペース区切りなども含めて)調べられます。この変数はSystemユニットで
定義されているので、改めて宣言する必要はありません。
[例]
a:\bin\MyApp.Exe /A /B /C
としてプログラムを実行すると、これらのコマンドラインを調べるのには、
以下のような方法があります。
--- ParamCount,ParamStrを使った方法
{ParamCountの数だけ引数を調べる}
if ParamCount > 0 then
for i := 1 to ParamCount do begin
if ParamStr(i) = '/A' then {/Aがある時の処理}
if ParamStr(i) = '/B' then {/Bがある時の処理}
if ParamStr(i) = '/C' then {/Cがある時の処理}
end;
※実際には、このように引数がなかった場合の事も考慮して下さい。
--- CmdLineを使った方法
var
S : String;
{コマンドライン中に指定の文字列が含まれているかどうかを一気に調べる}
S := StrPas(CmdLine);
if Pos('/A',S) > 0 then {/Aがある時の処理}
if Pos('/B',S) > 0 then {/Bがある時の処理}
if Pos('/C',S) > 0 then {/Cがある時の処理}
[前に戻る] [目次] [次に進む]
[A] パラメータにSM_CYMENUを指定してWinAPIのGetSystemMetricsを使います。 ヘルプの総合検索で、GetSystemMetricsを引いてみるとその他にもいろ いろ載っています。 (注)単一行の高さしか取得できないので、ウィンドウの幅を最少に変えた 場合など、メニューが複数行に渡っている場合などは注意してください。 [キーワード] GetSystemMetrics [前に戻る] [目次] [次に進む]
[A] Findメソッドを使うためには、SortedプロパティをTrueにする必要が あります。Sortedプロパティがfalseの場合は、indexOfメソッドを使 ってください。 [キーワード] TStringList、indexOf、Find [前に戻る] [目次] [次に進む]
TDateTime型の変数に例えば96年8月10日を代入するためにプログラム中で
aDate := 96/8/10;
とやると日付が変になります。
[A]
TDateTime型はDouble型で、整数部分が日付、小数部分が時間となって
います。質問にあるような代入をすると96÷8÷10で1.2が代入されてし
まうので、おかしくなります。下記のようにStrToDate関数を使って代
入してください。
aDate := StrToDate('96/8/10');
[前に戻る] [目次] [次に進む]
1.セルの色付け、フォント、描画などを個別にするには...
2.表形式で表示したいだけで入力は必要ないとき、フォーカスを示す描画は必要ないのですが...
3.セルの表示内容を、ECXELに表示したい...
[A]
1.2.個別に描画したいセルが少ないときはDefaultDrawing
をTrueにしたままOnDrawCellイベントハンドラの中に記述すればOK。デフォ
ルトの描画に上書き描画することになります。
3.行ごとにセルの内容をTABあるいはカンマで区切ってテキスト形式でクリップ
ボードに コピー、そしてECXELでペーストしてやればOK(^^)。
[例]
procedure TForm1.StringGrid1OnDrawCell(Sender: TObject; Col,
Row: Longint; Rect: TRect; State: TGridDrawState);
begin
with StringGrid1.Canvas do begin
//フォントの色を設定
Font := StringGrid1.Font;
if (Col = 0) and (Row = 0) then
Font.Color := clBlack
else if Col = 1 then
Font.Color := clBlue
else begin
Font.Color := clBlack;
end;
// セルの色を設定
if (Col = 0) or (Row = 0) then
Brush.Color := StringGrid1.FixedColor
else if (gdFocused in State) or (State = []) then
Brush.Color := StringGrid1.Color;
// セルに書き込む
TextRect(Rect, Rect.Left + 2, Rect.Top + 2,
StringGrid1.Cells[Col, Row]);
end;
end;
//クリップボードにコピー
//String型が拡張されたのをいい事にこんな大胆な事が出来ちゃいます。
procedure CopyStringGrid(SG:TStringGrid);
var
i,j:integer;
tmpS:string;
begin
with SG do begin
tmpS := '行項目\列項目';
for j := 0 to RowCount - 1 do begin
for i := 0 to Rows[j].count -1 do
if i = 0 then
tmpS := tmpS + Rows[j].strings[i]
else if i in Rows[j].count -2 then
tmpS := tmpS + #9 + Rows[j].strings[i]
else
tmpS := tmpS + #9 + Rows[j].strings[i] +#13+#10;
end;
Clipboard.SetTextBuf(PChar(tmpS));
end;
end;
[前に戻る] [目次] [次に進む]
[A] 一般にEditコントロールを使うときにはLabelを一緒に使って何の入力 に使うかをユーザに示します。この時LabelのCaptionに (&S) を追 加、FocusControlにフォーカスを移したいEditコントロールを指定すれ ばこの場合 ALT+S キーでエディットコントロールにフォーカス移動できます。 [help] TLabel, FocusControl [前に戻る] [目次]
Cで良く使われている、1970年1月1日 00:00:00からの経過秒数で表わされた形式を Delphiで使うにはどうすればいいですか?
[A]
例えば上記の場合だと、1970年1月1日をDelphiのTDateTime型に直し、経
過秒数からTDateTime型の整数部(日数)と小数部(時刻)を計算すれば出来
ます。関数にすると以下のようになります。
function CTime2Date(DT:Longint):TDateTime;
begin
Result := EncodeDate(1970,1,1) //Cの基準日付を変換
+ (DT + 32400) div 86400 //日付の算出
+ (DT mod 86400) / 86400; //時刻の算出
end;
[補足]
32400はGMTとの時差(9時間)、86400は一日の秒数です。GMTとの時差が異
なる場合32400の部分を変えて下さい。また、基準日付の部分はDelphiの
バージョンによって(Delphiの中での基準日付が)異なるためEncodeDate
でやっておいたほうがいいと思います。
[help]
[前に戻る] [目次] [次に進む]
プログラムを組んでいて、実際に遭遇した事などを基準にして載せています。また、このFAQの中で内容が不正確な点、疑問点などがありましたらげん ysakai@mars.dtinet.or.jpまで。一応私のマシンで実際に走っているコードを載せていますが、このFAQにある内容を実行する時は各自の責任の下で行ってください。