Delphi Tips
>> Index
● 01/12 ファイルを作らずメモリ上で動作する TTable コンポーネント
● 12/08 SubDetailが印刷できない
● 03/04 マウスがコントロールから出たことを検知する
● 03/04 エディットコントロールで右寄せ表示
● 09/24 Delphi7 のコンボボックスの空文字列でエラー
● 08/22 非ビジュアルコンポーネントの Left, Top 座標を取得
● 06/27 コンボボックスにブラウザのURL入力のような自動補完機能を付けたい
● 06/27 TMemoで、先頭行/最終行/指定行へ移動する。
● 06/25 OnDblClick発生時にOnMouseDownはいらない!
● 06/25 ComboBoxのDropDownListの幅を変更する
● 06/25 自己実行形式の動画
● 06/25 フォルダもファイルも選べるダイアログ
● 06/25 ボタン間を矢印キーで移動させるロジック
● 06/25 RichEdit を印刷したい
● 05/17 2つの TMemo のスクロールを同期させる
● 05/17 2つの TRichEdit のスクロールを同期させる
● 05/17 TreeView で D&D 中の描画処理
● 02/09 TListView の ... 表示をなくす
● 06/11 ListView をスクロールする
● 01/02 TField.EditMask の y2k 回避
● 01/02 StringGrid の内容更新を高速に行う
● 10/07 OnExit ハンドラで次にフォーカスを受け取るコントロールを知る
● 09/27 入力された漢字のひらがなを取り出す
● 09/27 アプリケーションを常駐させてタスクトレイに登録したい
● 09/24 TOpenDialogで、Executeメソッド実行時に表示位置を変えたい。
● 09/16 StringGrid でセル編集終了のイベントを得たい
● 09/16 TStringGrid の列の ReSize イベントの取得
● 09/15 TRichEdit.Add でリソース不足
● 09/15 TPageControl に OnDblClick を追加する
● 09/15 TTreeView で5つ以上のオーバレイ・イメージを使う
● 09/13 TFontDialog で標準以外のサイズを選択肢に表示したい
● 09/11 Editコントロールで入力を数値専用にする
● 09/11 TMemo のキャレットを非表示にする
● 09/11 エディットコントロールにポップアップウィンドウをつけたい
● 09/09 DLL 内で TDBCtrlGrid を使うときの注意事項
● 09/09 TPageControl でタブをドラッグして順序入れ替え
● 09/08 IME に未確定文字列を入力
● 09/08 ショートカットキーに'+'を使う。
● 09/07 右寄せ・数値入力可能なEditコンポーネントを作りたい。
● 09/06 ButtonのCaptionで改行を使って文字を複数段で表示したい
● 09/06 キーボードでボタンを押したとき、ボタンをちゃんと沈ませたい
● 09/06 カーソルキーでボタン(TButton)のフォーカス移動をやめさせたい
● 09/06 エディットコントロールにコンボボックスのようなボタンをつけたい
● 09/05 ドッカブルメニュー(ツールバー形式のメニュー)を実現したい。
● 09/01 RichEditでHTMLタグを色・書式付き表示をする
● 08/31 LinesプロパティエディタでTabを入力する
● 08/31 RichEdit の全パラグラフのタブ幅を設定する
● 08/31 TRichEdit.Paragraph.Tab[] の設定(Tab位置の設定)
● 08/28 データコントロールのDataLinkオブジェクトを取得する方法(ReadOnlyプロパティのバグの解決法)
● 08/26 セルのテキストをドラッグイメ−ジにしてグリッド(TStringGrid)でドラッグ&ドロップを行う
● 08/26 ツリービューのノードをドラッグ&ドロップで移動させる
● 08/26 書式付きメモ型項目へアクセスしたい
● 08/14 ショートカットキーのキー名を独自に設定する
● 05/28 TFileListBox にファイル名が重複して表示されてしまう
● 05/19 コンボボックスのリスト部分の幅を指定する
● 05/19 TRichEdit の内容を高速に変更する
● 05/01 右の項目の幅が固定されるTStatusBar
● 04/29 TCoolBarを用いたツールバーの位置保存
● 02/11 IME 入力で読み仮名を取得する
● 02/11 DBGrid のスクロールバーを非表示に
● 02/11 半角カナを確定無しで直接入力させる
● 02/11 長すぎる TreeView アイテムを補完するチップヘルプを出さなくする
● 02/11 下の図柄がすける透明パネル
● 02/11 API を使って縦書きなどのフォントを指定する
● 02/11 表示中のポップアップメニューを消す
● 02/11 TSplitter をドラッグ中にヒント文字列が表示された時の不具合
● 02/08 超高速 ListView
● 02/08 ステータスバーにプログレスバーを置きたい
● 02/08 PopupMenu に MainMenu のサブ項目をそのまま表示する
● 02/08 StringGrid で マウスのある Cell 内容に応じた Hint を出したい
● 02/08 自作コントロールで IME 入力時の変換候補をキャレット位置に表示したい
● 02/08 TDDEClientConvで最初の行しか実行されない?
● 02/08 TUpDown の不具合
● 02/08 TPageControl を TabPosition=tpBottom にすると不具合
● 02/08 超高速タイマーコンポーネント(サブミリ秒)
● 02/08 StringGridで選択セルのハイライト表示を無くしたい
● 02/08 フォームの印刷時にComboBoxの内容が印刷されない
● 02/08 TrueTypeフォントからベクタ情報を得る
● 02/08 TDriveComboBoxの内容の更新
● 02/08 TMemoで入力*行数*を制限したい
● 02/08 TListViewで列を指定してソート
● 02/08 TStringGridのソート
● 02/08 StringGrid/DBGrid でのセル編集の動作を細かく指定する
最終更新: 6523 日前
0129 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.4 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2007/01/12 RAN 編集
ファイルを作らずメモリ上で動作する TTable コンポーネント
>前どこかで TTable 互換でメモリ上で動作するコンポーネントを見かけたのです
>が、ご存知な方いらっしゃいませんか? (その時にダウンロードしておけばよかっ
>た...)
[1]
Delphi Super Pageに昔からあるコンポーネントです。
BDEのインメモリテーブルと一時テーブルの機能を使ったものです。
http://sunsite.icm.edu.pl/delphi/
(2007年現在はhttp://delphi.icm.edu.pl/authors/a0000561.htm に移動)
ファイル名:inmemory.zip および inmem32.zip
※サンプル付きです。
Delphi1.0用のほうを、Delphi3で使用するときの修正点は以下のとおりです。32ビット版が出ているのでもう必要無いのかな?
#Inmemユニット:
# TypeMapで配列の最後に「, fldCURSOR」を追加。
# SubTypeMapで配列の最後に「, 0」を追加。
#TempTblユニット:
# TypeMapで配列の最後に「, fldCURSOR」を追加。
# SubTypeMapで配列の最後に「, 0」を追加。
※Register手続きがありませんので、追加してください。
>これがあればテンポラリのテーブルをいちいちディスクに作成しなくても済んで
>しまうため(結果的に作られるとしても)、便利なのですが...
そうなんですが、デバッグのとき、値を確認しにくいと思いますよ。
[2]
> ひょっとして http://delphideli.com/dlnbde.htm にある TMemTable のことでしょうか。
僕が前見たのは確かシェアウエアだったのですが、これはフリーでしかもソース付き。嬉しい限りです。
[3]
最近のDelphi では標準でMyBase が利用できます。
http://hp.vector.co.jp/authors/VA028375/delphi/db.html が分かりやすいです。
参照: [Delphi-ML:22981] [Delphi-ML:22983] <データベース> <DataAccess>
0357 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2005/12/08 三輪一雄 rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2005/12/08 <> 編集
SubDetailが印刷できない
QRBandでBandTypeをrbSubDetailに設定しても、テーブルのフィールドが印刷されません。この方法で作成するとDataSetを指定するプロパティーがありません。
必ずQRSubDetailでバンドを作成します。
参照: <QReport> [d DelphiML]
0101 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2004/03/04 osamu 編集
マウスがコントロールから出たことを検知する
マウスカーソルがコントロールに入ったり、出たりする瞬間を捕らえる方法です。
CM_MOUSEENTER/CM_MOUSELEAVE メッセージを使う方法
[Delphi-ML:9631]
マウスの出入りを検出できるコンポーネントを作ってしまう方法
[Delphi-ML:9645]
アイドル処理で、検出する方法
[Delphi-ML:9820]
等が有ります。
9631 はフォームに直接貼り付けられたコントロールにしか使えないので注意して下さい。
コントロールをサブクラス化するコンポーネントを使っても簡単にできるかもしれません。
http://www.delphianworld.com/direct.html?id=SY0054
SubClassVCL というやつです。
参照: [Delphi-ML:14031] <その他コンポーネント関連>
0204 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/16 osamu rev 1.4 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2004/03/04 osamu 編集
エディットコントロールで右寄せ表示
>TEditで表示したい内容が数字なため右寄せにしたいと
>思っています。しかし、調べた限りでは右に寄ってく
>れません。
Delphian World などで既存のコンポーネントを入手するのが一番楽チンですね。( http://www.delphianworld.com/ )
自分で作る場合:
Win98とそれ以前のWinとでは作り方が異なります。
これはWindows98が右寄せEditをサポートしたからです。
『Win98で右寄せ・数値入力可能なEditコンポーネントを作る』
というTipsを参照ください。
こちらでは簡単に完璧に右寄せEditが作れませす。
Win98以前のWinでは
Editの代わりにTMemoを使い、1LineのTMemoをEditに見せかけます。
TMemoを配置して
1、Alignment Property を taRightJustify にする。
2、WordWrap Property を False にする。
WantReturns Property を False にして改行を入力させないようにしても
Ctrl+JやCtrl+Returnを押すと改行コードが入力されてしまいます。
そこで改行された時に改行を解除する動作をコーディングします。
procedure TForm1.Memo1KeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
SelStartBuf: Integer;
S: string;
begin
if Memo1.SelLength>0 then exit;
SelStartBuf := Memo1.SelStart;
S := Memo1.Text;
if pos(#13,S)<>0 then//改行コードのCRを削除
begin
Delete(S,pos(#13,S),1);
Dec( SelStartBuf );
end;
if pos(#10,S)<>0 then
begin
Delete(S,pos(#10,S),1);//改行コードLFを削除
Dec( SelStartBuf );
end;
Memo1.Text := S;
Memo1.SelStart := SelStartBuf;
(*
また、TMemoを右寄せにしているとWordWrap:=falseが効果ありません。
文字が増えるとどうしても改行されてしまいます。
それに対応して改行されたらその時の文字数を保持して左寄せに変えます。
LengthShowはFormのメンバーとして変数宣言してください。
*)
if Memo1.Lines[1]<>'' then//改行されたなら
begin
SelStartBuf := Memo1.SelStart ;
Memo1.Alignment := taLeftJustify;
Memo1.SelStart := SelStartBuf;
LengthShow := Length(Memo1.Lines[0]);//LengthShow変数に
end;//表示可能文字数を保持
(*
文字数が減ったら元に戻さなければいけません。
*)
if LengthShow<>0 then//LengthShowに保持数があるなら
if Length(Memo1.Lines[0])<LengthShow then//文字が減ったら
begin
SelStartBuf := Memo1.SelStart;
Memo1.Alignment := taRightJustify;
Memo1.SelStart := SelStartBuf;
end;
end;//KeyUpイベント終わり
単純に、改行された時の文字数=表示の限界、とみなして
その文字数以下になれば右寄せに変えるという動作ですので
プロポーショナルフォントの場合では文字幅が異なりますのでちょっと変な動作になりそう。
また、左寄せの状況で文字を削除していくと左寄せという事がばれる動作をします。
更に、キー入力以外でテキストを代入するような動作は考慮していません。
その辺は、自分で作ってね。出来たら、このTipsを更新してくだされ。
参照: [Delphi-ML:9195] [Delphi-ML:41622] <Standard>
0344 D1D2D3D4D5D6D7 3.195 98 作成: 2003/07/03 osamu rev 1.4 B1B3B4B5B6 B7 NT3 NT4 2K XP 更新: 2003/09/24 osamu 編集
Delphi7 のコンボボックスの空文字列でエラー
Delphi7 では、コンボボックスに空文字列を入れておくと、
ComboBox1.Items.Add('');
この Item を Items[0] などとして参照した時にアクセスバイオレーションエラーが発生してしまいます。
これは、以前の Delphi が Item 保持領域として 4096 などの固定値を使っていたのに対して、Delphi7 からは CB_GETLBTEXTLEN を使ってバッファを動的に確保するようになったためで、それ自体は正しいことなのですが、Item の長さが 0 の時に正しくバッファが確保されないためにエラーが発生してしまいます。
エラーの詳しい内容は、[Delphi-ML:77352][Delphi-ML:77360][Delphi-ML:77366]を見てみてください。回避方法としては、Amaito さんの [Delphi-ML:77357] のように、
StdCtrls.pasをプロジェクトと同じフォルダにコピーして、2325行を
//if Len <> CB_ERR then
if Len > 0 then
とするのが良いようです。(CB_ERR は負の値なので、Len > 0 で Len=0 と Len=CB_ERR との両方の場合に対応しています)
#[Delphi-ML:72969] でもすでに同じ問題が報告されていました。
## 実は編集後のコードにもちょっとした仕様バグが含まれています。
## TListBox.Items などと動作を合わせるのであれば、CB_ERR
## の場合には空文字列を返すのではなく例外を投げるべきだと
## 思います。
参照: [Delphi-ML:72969] [Delphi-ML:77352] [Delphi-ML:77357] [Delphi-ML:77360] [Delphi-ML:77366] <バグ> <Standard>
0342 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/06/27 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/08/22 osamu 編集
非ビジュアルコンポーネントの Left, Top 座標を取得
> 設計時にフォーム上に貼り付けた
> 非ビジュアルコンポーネント(TTable,TQuery)の
> フォーム上の位置を取得したいんですけれどもできるんでしょうか?
procedure TComponent.WriteLeft(Writer: TWriter);
procedure TComponent.WriteTop(Writer: TWriter);
の2つのメソッドを見てみると、DesignInfo プロパティの上半分、
下半分がそれぞれ Top, Left に対応するみたいですね。
TheLeft:= LongRec(ACompo.DesignInfo).Lo;
TheTop := LongRec(ACompo.DesignInfo).Hi;
で取り出せるようです。
参照: [Delphi-ML:77269] <その他コンポーネント関連>
0171 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/03/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/27 osamu 編集
コンボボックスにブラウザのURL入力のような自動補完機能を付けたい
ひきさんのページにサンプルコードを見つけることができます。ひきさんに感謝しつつ、ご利用下さい。
[Delphi壁の穴]-[その一:Delphiを覗く]
http://hp.vector.co.jp/authors/VA009712/take/delphi/kabedel.htm#autocomplete
参照: <Standard>
0172 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/03/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/27 osamu 編集
TMemoで、先頭行/最終行/指定行へ移動する。
ひきさんのページにサンプルソースを見つけることができます。ひきさんに感謝しつつ活用しましょう。
[Delphi壁の穴]-[その1:Delphiを覗く]
http://hp.vector.co.jp/authors/VA009712/take/delphi/kabedel.htm#memomove
参照: <Standard>
0019 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
OnDblClick発生時にOnMouseDownはいらない!
ダブルクリック発生をOnDblClickで捕まえたいのに、1度目のクリックでOnMouseDownが発生してしまう。
ダブルクリックとシングルクリックの両方を捕まえるにはどうするか?
procedure TForm1.FormMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var t:LongInt;
begin
//
// シングル/ダブルクリックの判定
//
if ssDouble in Shift then begin
DoubleClickOccurred[Button]:=True;
end else begin
DoubleClickOccurred[Button]:=False;
t:=GetTickCount+GetDoubleClickTime;
while GetTickCount<t do begin
Application.ProcessMessages;
if DoubleClickOccurred[Button] then
Exit;
end;
end;
if ssDouble in Shift then begin
case Button of
mbLeft: ;
mbMiddle: ;
mbRight: ;
end;
end else begin
case Button of
mbLeft: ;
mbMiddle: ;
mbRight: ;
end;
end;
end;
ここで、
DoubleClickOccurred:array [TMouseButton] of Boolean;
は、TForm1のメンバ変数です。
シングルクリックの際の動作でチョット遅れた感じがするのはしかたないですね。
参照: [Delphi-ML:7386] [Delphi-ML:7392] [Delphi-ML:7457] <その他コンポーネント関連>
0043 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
ComboBoxのDropDownListの幅を変更する
エディット部分はそのままで、ドロップダウンする部分の幅だけを変更したければ、
ComboBox1.Perform(CB_SETDROPPEDWIDTH, 幅, 0);
とします。
「幅」はピクセル単位となります。
参照: [Delphi-ML:19165] <Standard>
0340 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/06/25 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
自己実行形式の動画
Exe 内部に動画データをバイナリデータのリソースにしてコンパイルします。D5でのリソースファイルの作り方について:
http://halbow.cool.ne.jp/Notes/N008.html
例えば、'clock.avi' をリソースにする場合、リソーススクリプトファイル(MyRes.rc) は以下のようになります。
MyAVI AVI "c:\WINNT\clock.avi"
このリソースを使って以下のようにして、うまくいきました。
procedure TForm1.Button1Click(Sender: TObject);
var
RS:TResourceStream;
begin
RS := TResourceStream.Create(hInstance,'MyAVI','AVI');
try
RS.SaveToFile(ExtractFilePath(ParamStr(0))+'MyAVI.avi');
finally
RS.Free;
end;
if FileExists(ExtractFilePath(ParamStr(0))+'MyAVI.avi') then
with MediaPlayer1 do begin
Filename := ExtractFilePath(ParamStr(0))+'MyAVI.avi';
DeviceType := dtAVIVideo;
Notify := false;
Open;
Notify := true;
Play;
end;
end;
procedure TForm1.MediaPlayer1Notify(Sender: TObject);
begin
MediaPlayer1.Close;
if FileExists(ExtractFilePath(ParamStr(0))+'MyAVI.avi') then
begin
DeleteFile(ExtractFilePath(ParamStr(0))+'MyAVI.avi');
ShowMessage('AviFile has Deleted!');
end;
end;
参照: [Delphi-ML:76119] <その他Windows関連> <System> <Windows>
0339 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/06/25 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
フォルダもファイルも選べるダイアログ
Q.デスクトップ 〜 右クリック 〜 新規作成 〜 ショートカットの時に「参照」ボタンを押しますと、Caption は「フォルダの参照」とかになっていながらフォルダもファイルも選べるダイアログが出てきます。こいつを使用したいのです。
A.ShBrowseForFolder で BIF_BROWSEINCLUDEFILES を指定すると良いようです。
with BrowseInfo do begin
hwndOwner := FhParent;
pidlRoot := nil;
pszDisplayName := BDisplayName;
lpszTitle := 'フォルダを選択してください';
ulFlags := BIF_STATUSTEXT or BIF_RETURNONLYFSDIRS or
BIF_BROWSEINCLUDEFILES; // ここを追加
lpfn := @BrowseFolderProc;
lParam := integer(@BFFR);
end;
参照: [Delphi-ML:76043] <Dialogs>
0337 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/06/25 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
ボタン間を矢印キーで移動させるロジック
Q.フォーム上にボタンがたくさんあり、上下左右の矢印キーによって、実際のボタンの配置から期待される順序でフォーカスを動かしたいのですが。
A.設計時の手間はかかるのですが、各ボタンを個別にパネルの上に乗せてしまえば、矢印キーによる通常のフォーカス移動を無効にできるので、ボタンの OnKeyUp イベント内で移動先を設定できると思うのですがいかがでしょうか。
参照: [Delphi-ML:75689] <Standard>
0336 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/06/25 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
RichEdit を印刷したい
Delphi の Demo にサンプルコードが含まれています。Delphi5 であれば、
Program Files\Borland\Delphi5\Demos\Richedit\richedit.dpr
に RichEdit の印刷ルーチンがあります。
参照: [Delphi-ML:54315] <印刷> <Win95>
0321 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2002/05/17 osamu rev 1.4 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2002/05/17 osamu 編集
2つの TMemo のスクロールを同期させる
Q:
memo1 のスクロールバーを動かすと、memo2 も同じようにスクロールさせたいです。
A:[Delphi-ML:67004] Halbow さん
垂直スクロールバーについてだけ、回答します。Memo1 と Memo2 は Width や Height 、Font.Size など同一のプロパティーであると仮定しています。
TRichEdit で同様のことをする方法が[Tips:322]にあります。
type
TForm1 = class(TForm)
Memo1: TMemo;
Memo2: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
public
Memo1Proc:TWndMethod;
MKEY:WORD;
procedure Memo1SuclassProc(var Msg:TMessage);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
Memo1Proc := Memo1.WindowProc;
Memo1.WindowProc := Memo1SuclassProc;
end;
procedure TForm1.Memo1SuclassProc(var Msg: TMessage);
begin
Memo1Proc(Msg);
case Msg.Msg of
WM_VSCROLL:Memo2.Dispatch(Msg);
CN_COMMAND:
if (Msg.WParamHi = EN_VSCROLL) then begin
case MKEY of
VK_DOWN:Memo2.Perform(EM_LINESCROLL,0,1);
VK_UP :Memo2.Perform(EM_LINESCROLL,0,-1);
VK_HOME:if (GetAsyncKeyState(VK_CONTROL) and $8000) <> 0 then begin
Memo2.SelStart := 0;
Memo2.Perform(EM_SCROLLCARET,0,0);
end;
VK_END:if (GetAsyncKeyState(VK_CONTROL) and $8000) <> 0 then begin
Memo2.SelStart := Length(Memo2.Text);
Memo2.Perform(EM_SCROLLCARET,0,0);
end;
end;
MKEY := 0;
end;
CN_KEYDOWN:MKEY := Msg.WParam;
end;
end;
参照: [Delphi-ML:67004] <Standard> [Tips:322]
0322 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2002/05/17 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2002/05/17 osamu 編集
2つの TRichEdit のスクロールを同期させる
2つのTMemo のスクロールを同期させるやり方が[Tips:321]にありますが、実は TRichEdit だと同じようにしてもうまく行きません。
解決方法は、やはり Halbow さんの[Delphi-ML:67313]にあります。
うまく行かないのは、TRichEdit では CN_COMMAND を通じたスクロール通知メッセージ EN_VSCROLL が発行されないためです。EM_SETEVENTMASK というメッセージを送って、LParam に ENM_SCROLL を設定すると、親に EN_VSCROLL を通知するようになります。
親にイベントを通知するかどうかをプログラムで設定するためのこのようなマスクについて、[Delphi-ML:67328]にさらに詳しい説明があります。
public
RichEdit1Proc:TWndMethod;
MKEY:WORD;
procedure RichEdit1SuclassProc(var Msg:TMessage);
procedure SyncVScroll;
procedure PageDown;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
richedit;
procedure TForm1.FormCreate(Sender: TObject);
begin
RichEdit1Proc := RichEdit1.WindowProc;
RichEdit1.WindowProc := RichEdit1SuclassProc;
end;
procedure TForm1.FormShow(Sender: TObject);
var
MASK:LPARAM;
begin
MASK := RichEdit1.Perform(EM_GETEVENTMASK,0,0);
RichEdit1.Perform(EM_SETEVENTMASK,0,MASK or ENM_SCROLL);
end;
procedure TForm1.RichEdit1SuclassProc(var Msg: TMessage);
begin
RichEdit1Proc(Msg);
case Msg.Msg of
WM_VSCROLL:RichEdit2.Dispatch(Msg);
CN_COMMAND:begin
if (Msg.WParamHi = EN_VSCROLL) then
case MKEY of
VK_DOWN,VK_UP,VK_PRIOR:SyncVScroll;
VK_NEXT:PageDown;
VK_HOME,VK_END:
if (GetAsyncKeyState(VK_CONTROL) and $8000) <> 0 then
SyncVScroll;
end;
end;
CN_KEYDOWN:MKEY := Msg.WParam;
end;
end;
procedure TForm1.SyncVScroll;
var
Msg:TWMScroll;
begin
Msg.Msg := WM_VSCROLL;
Msg.Pos := GetScrollPos(RichEdit1.Handle,SB_VERT);
Msg.ScrollBar := 0;
Msg.ScrollCode := SB_THUMBTRACK;
RichEdit2.Dispatch(Msg);
Msg.ScrollCode := SB_THUMBPOSITION;
RichEdit2.Dispatch(Msg);
Msg.ScrollCode := SB_ENDSCROLL;
RichEdit2.Dispatch(Msg);
end;
procedure TForm1.PageDown;
var
Msg:TWMScroll;
begin
Msg.Msg := WM_VSCROLL;
Msg.Pos := 0;
Msg.ScrollBar := 0;
Msg.ScrollCode := SB_PAGEDOWN;
RichEdit2.Dispatch(Msg);
end;
参照: [Delphi-ML:67313] [Delphi-ML:67328] <Win95> <Standard> [Tips:321]
0316 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2002/05/17 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2002/05/17 osamu 編集
TreeView で D&D 中の描画処理
TreeView のアイテムをドラッグ・ドロップする際に、挿入位置をグラフィカルに表示するためのサンプルコードです。[Delphi-ML:67348] Halbow さん作。
procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
var
AnItem: TTreeNode;
r:TRect;
begin
AnItem := TreeView1.GetNodeAt(X, Y);
if not Assigned(AnItem) then exit;
r := AnItem.DisplayRect(true);
with TreeView1.Canvas do begin
Brush.Color := TreeView1.Color;
Pen.Color := clRed;
Font := TreeView1.Font;
TextRect(r,r.Left+2,r.Top+1,AnItem.Text);
if (GetAsyncKeyState(VK_CONTROL) and $8000) = 0 then begin
Brush.Style := bsClear;
Rectangle(r.Left,r.Top,r.Right,r.Bottom)
end else begin
MoveTo(r.Left+1,r.Bottom-1);
LineTo(r.Right-1,r.Bottom-1);
end;
end;
end;
参照: [Delphi-ML:67348] <Win95>
0314 D1 D2 D3 D4 D5 D6 D7 3.195 98 作成: 2002/02/09 鈴木充 rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2002/02/09 鈴木充 編集
TListView の ... 表示をなくす
> 自作ソフトにTListViewを使っているのですが、ViewStyleをvsListにした場
> 合、実行時にListItemのCaptionを編集して元のCaptionより長くなった場合、
> 入りきらない部分が"..."表示になるのですが、これをすべて表示するように
> するにはどうすればよいのでしょうか?
ListView1.Perform(CM_RECREATEWND, 0, 0);
で表示されるはずですが、選択状態・スクロール状態等は解除されるので必要に
応じて状態を復元する処理を行ってください。
参照: [Delphi-ML:64465] <Win95>
0312 D1D2 D3 D4 D5 D6 D7 3.195 98 作成: 2001/06/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2001/06/11 osamu 編集
ListView をスクロールする
> データセット後に任意の行を表示させたいのですが、
> TopItem プロパティだと read のみで変更できません。
> TStringGrid では TopRow プロパティにて行う事ができますが、
> TListView では何か良い方法はあるのでしょうか?
ListView1->Selected->MakeVisible(false or true);
でスクロールすることができます。
参照: [builder:31260] <Win95>
0296 D1D2D3D4 D5 D6 D7 3.195 98 作成: 2000/01/02 osamu rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2000/01/02 osamu 編集
TField.EditMask の y2k 回避
> DBEditで日付入力させるのですが、関連付けているTTable側の
> 日付フィールドのTFieldのEditMaskプロパティで
>
> !99/99/00;1;_
>
> ・・とマスクをかけています。
> すると、西暦を下2桁で打つマスクが有効になるのですが
> 2000/05/05 の意で 00/05/05 とか打つと、期待した
> 2000/05/05にならずに、1900/05/05と表示されてしまいます。
OnCreate イベントハンドラ内で
TwoDigitYearCenturyWindow := 50;
ShortDateFormat := 'yyyy/mm/dd';
の設定を行うと,現在の年が1999年のとき,1999年の前後50年である
1949/1/1 から 2048/12/31 の100年を変換の対象として扱います.
ただし D4 からの機能のようです。
詳しくはヘルプか以下のサイトをご覧になられるとよいと思います.
http://www.inprise.com/devsupport/y2000/
【追記】
Delphi 5 になってから TwoDigitYearCenturyWindow グローバル変数の初期値が「0」から「50」に変更されました。UI 簡略化等で西暦2桁年を使っているような場合、TwoDigitYearCenturyWindow 変数を設定しないまま日付型に変換しているような処理では、処理結果がD4以前と変わる場合があります。
ヘルプの記述(初期値は0)は誤っていますので、ご注意ください。
参照: [Delphi-ML:37908] [Delphi-ML:43093] <データベース> <DataControls>
0190 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/05/19 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2000/01/02 osamu 編集
StringGrid の内容更新を高速に行う
| 少々データ量が大きいので時間を稼ぐため、画面表示の Update を
| 停止したいのですが TStringGrid に BeginUpdate/EndUpdate は
| 無いようです。
【解法1】
描画更新の一次停止
SendMessage(StringGrid1.Handle, WM_SETREDRAW, 0, 0);
描画更新の再開
SendMessage(StringGrid1.Handle, WM_SETREDRAW, 1, 0);
StringGrid1.Refresh;
この方法は、すべての TWinControl に対して有効です。
【解法2】
描画更新の一次停止
StringGrid1.Rows[0].BeginUpdate;
描画更新の再開
StringGrid1.Rows[0].EndUpdate;
Rows[0] は Rows[1] でも何でも構いませんが、停止と開始で同じ添字を使わなければなりません。Cols[x] も使えます。一つの Rows[x] または Cols[x] に対して BeginUpdate すると StringGrid のすべての表示更新が停止になります。
BeginUpdate は TStrings の仮想メンバ関数なので、TMemo.Lines とか TListBox.Items などで BeginUpdate を使って TMemo/TListBox などの変更を高速に行うこともできます。
【メモ】
描画更新の再開を必ず行うために、BeginUpdate/EndUpdate は try/finally で保護した方が良いです。
StringGrid1.Rows[0].BeginUpdate;
try
// 更新処理
finally
StringGrid1.Rows[0].EndUpdate;
end;
また、BeginUpdate を繰り返し呼んだ場合には EndUpdate を同じ回数呼ばなければ描画は再開されません。さらに、BeginUpdate よりも多く EndUpdate を呼んでしまうとおかしなことになってしまうので注意が必要です。
参照: [Delphi-ML:38146] [builder:19539] <Additional>
0280 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/10/07 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/10/07 osamu 編集
OnExit ハンドラで次にフォーカスを受け取るコントロールを知る
> OnExit 内で、この後フォーカスが移動するコンポーネントを
> 知ることは出来るのでしょうか?
私はこうやってます。
procedure XXXXXExit(Sender:TObject);
begin
:
If ActiveControl = 該当のコンポーネント then
:
end;
参照: [Delphi-ML:34067] <その他コンポーネント関連>
0201 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/13 西坂良幸 rev 1.4 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/27 K.Takaoka 編集
入力された漢字のひらがなを取り出す
WM_IME_COMPOSITIONメッセージをとらえて、ImmGetCompositionString関数を処理するのですが、受け取るのはコントロールの場合が多いので、少し工夫が必要です。
この例は、フォームに2つのTEditがあり、
TEdit1で漢字入力すると、TEdit2で入力に利用した平仮名がとれます。
ふりがなを取得するには別の方法を利用します.
// interface部
type
TForm1 = class(TForm)
Edit1: TEdit; // 漢字変換を行う
Edit2: TEdit; // フリガナを受け取る
procedure FormDestroy(Sender: TObject);
procedure FormShow(Sender: TObject);
private
FDefEditProc: TWndMethod;
procedure EditWndProc(var Message: TMessage);
public
end;
// implementation部
uses imm;
procedure TForm1.FormCreate(Sender: TObject);
begin
// 本来のWndProcを待避する
FDefEditProc := Edit1.WindowProc;
// 新しいWndProcを設定
Edit1.WindowProc := EditWndProc;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
// 恐いので元に戻す
Edit1.WindowProc := FDefEditProc;
end;
// 新しいEdit1のWndProc--ここでメッセーージを捕らえる
procedure TForm1.EditWndProc(var Message: TMessage);
var
IMC: HIMC;
Len: integer;
Str: string;
begin
with Message do
begin
if (Msg = WM_IME_COMPOSITION)
and ((LParam and GCS_RESULTREADSTR) <> 0) then
begin
IMC := ImmGetContext(Edit1.Handle);
Len := ImmGetCompositionString(IMC, GCS_RESULTREADSTR, nil, 0);
SetLength(Str, Len + 1);
ImmGetCompositionString(IMC, GCS_RESULTREADSTR, PChar(Str), Len + 1);
ImmReleaseContext(Edit1.Handle, IMC);
SetLength(Str, Len);
Edit2.Text := Str;
// Edit2.Text := Edit2.Text + Str; // でもよい
// イベントを作成してもよい
end;
FDefEditProc(Message);
end;
end;
実際は、メッセージを捕まえるコンポーネントのイベントなどで、必要なコントロールに渡すのがいいでしょうか。
参照: [Delphi-ML:41353] [Delphi-ML:41355] <System> <その他コンポーネント関連>
0207 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/19 西坂良幸 rev 1.10 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/27 西坂良幸 編集
アプリケーションを常駐させてタスクトレイに登録したい
コンポーネントをつかう人が多いんでしょうね。MLでは少ない話題です。このようなAPLを何十本も作るんでなければ、直接コーディングしてみませんか。
要領は、APIのShell_NotifyIcon関数を使うことです。TaskTrayへの登録と削除は簡単にできます。
頭を悩ますのは、どんなアイコンを使うのかと、TaskTrayに送られてくるメッセージを自分で決めなければならないことです。
const
WM_MY_TRAYICON = WM_APP + $300; // 適当です
// ProcessMesageに配慮−−無くても良い
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
// WM_QUIT メッセージを受け取る時
if Application.Terminated then
begin
ShowWindow(Application.Handle, SW_HIDE);
Visible := False;
Action := caNone;
end;
end;
// アイコンをトレイに登録−−ここではメインアイコンを使用
procedure TForm1.CreateTaskBarIcon;
var
NotifyData: TNotifyIconData;
begin
with NotifyData do
begin
cbSize := SizeOf(TNotifyIconData);
Wnd := hWndTrayIcon;
uID := 0;
uFlags := NIF_MESSAGE or NIF_ICON or NIF_TIP;
szTip := 'Traybar Tip';
hIcon := Application.Icon.Handle;
uCallbackMessage := WM_MY_TRAYICON;// 独自に定義
end;
Shell_NotifyIcon( NIM_ADD, @NotifyData );
end;
// アイコンを削除
procedure TForm1.DeleteTaskBarIcon;
var
NotifyData: TNotifyIconData;
begin
with NotifyData do
begin
cbSize := SizeOf(TNotifyIconData);
Wnd := hWndTrayIcon;
uID := 0;
end;
Shell_NotifyIcon( NIM_DELETE, @NotifyData );
end;
// 起動直後はトレイにアイコン表示のみ
procedure TForm1.FormCreate(Sender: TObject);
begin
// uTaskBarRecrate は TForm1 の private に UINT 型で宣言
// 新しい UtilWindow を作成し、ブロードキャストメッセージのみを受け取らせる
uTaskBarRecreate := RegisterWindowMessage('TaskbarCreated');
// hWndTrayIcon は TForm1 の private に HWND 型で宣言
// メッセージ送信先を指定
hWndTrayIcon := AllocateHWnd(TaskTrayWndProc);
CreateTaskBarIcon;
ShowWindow(Application.Handle,SW_HIDE);
Application.ShowMainForm := False;
end;
// 終了した時にトレイのアイコンを削除
procedure TForm1.FormDestroy(Sender: TObject);
begin
DeleteTaskBarIcon;
DeallocateHWnd(hWndTrayIcon);
end;
// タスクトレイに来るWM_MY_TRAYICONメッセージを受信
procedure TForm1.TaskTrayWndProc(var Msg: TMessage);
var
ps: TPoint;
begin
Case Msg.LParam of
WM_LBUTTONDBLCLK:
begin
Visible := true;
ShowWindow(Application.Handle,SW_SHOW);
//ShowWindow(Application.Handle,SW_RESTORE);
end;
WM_RBUTTONUP:
begin
GetCursorPos(ps);
SetForegroundWindow(Handle);
// フォームにポップアップメニューがあるとする
PopupMenu1.Popup(ps.x,ps.y);
PostMessage(Handle, WM_NULL, 0,0);
end;
else
// タスクバーが移動・再構築された場合に消えたアイコンを再生成
if (Msg.LParam = LongInt(uTaskBarRecreate)) then
CreateTaskBarIcon;
end;
end;
AllocateHWnd 関数の戻り値のウィンドウハンドルを利用すれば他のメッセージ受信先/送信先として利用できるので、TrayIcon のメッセージ送信先に利用しました。
参照: [Delphi-ML:2536] [Delphi-ML:11821] [Delphi-ML:35250] <System> <Windows> <フォーム>
0141 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/24 西坂良幸 編集
TOpenDialogで、Executeメソッド実行時に表示位置を変えたい。
TOpenDialogは、OnShowイベントの段階では、位置がすでに決まっているようです。調べてみると、何回かWM_NOTIFYが送られますが、NMHDR構造体のCodeメンバーがCDN_INITDONEになるダイアログの初期化時を捕まえるとうまくいくようです。(ただし、これでも最初のExecute時しか効果ありません。D4のみ?)
以下のような拡張ダイアログコンポが紹介されています。
// 定義部
type
TQueryDlgPosEvent = procedure(Sender: TObject; var pt: TPoint) of object;
ThtOpenDialogEx = class(TOpenDialog)
private
FQueryDlgPos: TQueryDlgPosEvent;
protected
procedure DefaultHandler(var Message); override;
procedure DoQueryDlgPos; virtual;
published
property OnQueryDlgPos: TQueryDlgPosEvent read FQueryDlgPos write FQueryDlgPos;
end;
// 実装部n
procedure ThtOpenDialogEx.DefaultHandler(var Message);
begin
inherited;
with TWMNotify(Message) do
if (Msg = WM_NOTIFY) then
if (NMHdr^.Code = CDN_INITDONE) then
DoQueryDlgPos;
end;
procedure ThtOpenDialogEx.DoQueryDlgPos;
var
DlgHandle: THandle;
Rect: TRect;
pt: TPoint;
begin
if Assigned(FQueryDlgPos) then
begin
DlgHandle := GetParent(Handle);
GetWindowRect(DlgHandle, Rect);
pt.x := (GetSystemMetrics(SM_CXSCREEN) - Rect.Right + Rect.Left) div 2;
pt.y := (GetSystemMetrics(SM_CYSCREEN) - Rect.Bottom + Rect.Top) div 3;
FQueryDlgPos(Self, pt);
SetWindowPos(DlgHandle, HWND_TOP, pt.X, pt.Y, 0, 0,
SWP_NOSIZE or SWP_NOZORDER);
end;
end;
// テスト
procedure TForm1.Button1Click(Sender: TObject);
var
OpenDialogEx:ThtOpenDialogEx;
begin
OpenDialogEx:=ThtOpenDialogEx.Create(self);
try
with OpenDialogEx do
begin
OnQueryDlgPos := QueryDlgPos;
Execute;
end;
finally
OpenDialogEx.Free;
end;
end;
procedure TForm1.QueryDlgPos(Sender: TObject; var pt: TPoint);
begin
Pt.x := 800;
pt.y := 600;
end;
追記:
DefaultHandlerメソッドのかわりに、WndProcメソッドをオーバーライドしてもほぼ同じ結果が得られます。
以下は、イベントの代わりに、InitialLeft, InitialTop、というプロパティを設定しています。
procedure ThtOpenDialogEx.WndProc(var Message: TMessage);
var
DlgHandle: THandle;
begin
inherited WndProc(Message);
if (Message.Msg = WM_NOTIFY) then
if (TWMNOTIFY(Message).NMHdr^.Code = CDN_INITDONE)
and not ((FInitialLeft < 0) or (FInitialTop < 0)) then
begin // 負数のときは処理しない
DlgHandle := GetParent(Handle);
SetWindowPos(DlgHandle, 0, FInitialLeft, FInitialTop, 0, 0, SWP_NOSIZE or SWP_NOZORDER);
end;
end;
参照: [Delphi-ML:12600] [Delphi-ML:24640] <ダイアログ> <Dialogs>
0263 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/16 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/16 osamu 編集
StringGrid でセル編集終了のイベントを得たい
StringGrid で表計算のような動作をさせようとすると、セル編集終了のイベントで、関係し合うセルの再計算を行いたくなります。OnSetEditText というイベントが利用できそうなのですが、このイベントはユーザのキー入力の一文字ごとに発生するため使えません。
で、少なくとも Delphi3 では、以下のようにすると、セル編集終了時を検出することができます。
これは、セル編集終了時に、OnSetEditText が2度続けて同じ Value 値で呼び出されるという現象を利用しています。Delphi の今後のバージョンで動作が保証されるわけではありませんので、注意が必要です。また、Options に goAlwaysShowEditor が含まれている場合には、Enter キーで値を確定することができないという不具合(?)が生じます。
procedure TForm1.StringGrid1SetEditText(Sender: TObject; ACol, ARow: Integer;
const Value: String);
const
MagicValue = 'd0308|ybh<_lfds$t083q()'#1#5; // 入力値としてありえない値
PreviousEditorValue: string = MagicValue; // C でいう static 変数の代り
begin
if Value<>PreviousEditorValue then begin // まだ編集中
PreviousEditorValue:= Value;
Exit;
end;
// 編集終了時には、同じ値が二度続けて送られてくる
case ACol of
0: ;
1: ; // ここで入力後の処理
2: ;
end;
// 次回のために絶対にありえない文字列を代入
PreviousEditorValue:= MagicValue;
end;
参照: [Delphi-ML:42663] <Additional>
0262 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/16 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/16 osamu 編集
TStringGrid の列の ReSize イベントの取得
ColWidthsChanged を overrideすることで、実現できます。
■interface
TStringGrid_Ex = class(TStringGrid)
private
FOnColWidthsChange:TNotifyEvent;
ptotected
procedure ColWidthsChanged; override;
published
property OnColWidthsChange:TNotifyEvent
read FOnColWidthsChange
write FOnColWidthsChange;
end;
■implementation
procedure TStringGrid_Ex.ColWidthsChange;
begin
inherited;
if Assigned(FOnColWIdthsChange) then
FOnColWidthsChange(Self);
end;
■利用方法
procedure TForm1.Grid1OnColWidthsChange(Sender:TObject);
var
idx :Longint;
begin
//条件
// Grid1.ColCount = Grid2.ColCountであること
if Sender = Grid1 then begin
for idx:=0 to Grid1.ColCount-1 do begin
Grid2.ColWidths[idx] := Grid1.ColWidths[idx];
end;
end else begin
for idx:=0 to Grid2.ColCount-1 do begin
Grid1.ColWidths[idx] := Grid2.ColWidths[idx];
end;
end;
end;
end;
同様に、RowHeighsChangedもoverrideできます。
参照: [Delphi-ML:31921] <Additional>
0258 D1D2 D3 D4 D5 D6 D7 3.195 98 作成: 1999/09/15 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3NT4 2K XP 更新: 1999/09/15 osamu 編集
TRichEdit.Add でリソース不足
> 現在、通信関係のソフトを開発しているのですが、RichEdit の Add メソッドで
> EOutOfResources(リソース不足)の例外が発生するようになってしまい
> ました。
>
> RichEdit の MaxLength には、20000000 の値を入れており、まだそんなに
> Add していない状態で発生します。
Lines.Add メソッドで読み込んでいるということですね。
私も同じトラブルを経験しましたが、Lines.Add の代わりに、一旦 TMemoryStream に書き出してから、LoadFromStream メソッドを使うという方法で回避しました。
参照: [Delphi-ML:34075] <Win95>
0257 D1 D2 D3 D4 D5 D6 D7 3.195 98 作成: 1999/09/15 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3NT4 2K XP 更新: 1999/09/15 osamu 編集
TPageControl に OnDblClick を追加する
> PageControl や TabControl にクリックイベントやダブルクリックイベントを
> 付けたいのですがどのようにしたらよろしいのでしょうか?
以下の派生クラスでお望みのイベントが生成されます。
# ただ、PageControl の仕様をよく調査しないで作ったので、
# もしかしたら Click関係で弊害があるかもしれません。
uses
Classes, Controls, ComCtrls;
type
TxxPageControl = class(TPageControl)
public
constructor Create(AOwner: TComponent); override;
published
property OnDblClick;
end;
implementation
constructor TxxPageControl.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ControlStyle := ControlStyle + [csClickEvents];
end;
参照: [Delphi-ML:34074] <Win95>
0256 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/15 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/15 osamu 編集
TTreeView で5つ以上のオーバレイ・イメージを使う
>TreeView で同時に指定可能なオーバーレイ・イメージの最大数は
>使用する ImageList のメソッド Overlay で設定可能な最大数
>(4つ、TOverlay の範囲内で指定) になると解釈しています。
>現在、この制限を越えた数のオーバーレイ・イメージを使いたいの
>ですが、何か良い方法は無いでしょうか?
Overlay は ComCtl32.dll が 4.70 以前は4個まで、4.71 以降で 15個までに拡張されたようです。4.70 以前の場合は、同時に4つまでしか使用できませんが、4.71 以降では、
ImageList_SetOverlayImage(ImageList1.Handle, 8, 5);
のように直接設定すれば OK です。(^^)/
参照: [Delphi-ML:32333] <Win95>
0253 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/13 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/13 osamu 編集
TFontDialog で標準以外のサイズを選択肢に表示したい
> ルビをうつフォントを指定しようと思い、TFontDialogで
> 片付けようと思いましたが、TFontDialogで、MSゴシック
> など8Pt以下のポイントが出てきません。指定できるように
> する方法をどなたかご存知でしょうか。
思いっきり強引ですけど
unit FontDialogEx;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs;
type
TFontDialogEx = class(TFontDialog)
protected
function TaskModalDialog(DialogFunc: Pointer;
var DialogData): Bool; override;
end;
procedure Register;
implementation
uses Dlgs, CommDlg;
const
WM_FONTDIALOGEX = WM_USER + 256;
str1: string = '4';
str2: string = '5';
str3: string = '6';
str4: string = '7';
var
OldHook: function(Wnd: HWnd; Msg: UINT; WParam: WPARAM;
LParam: LPARAM): UINT; stdcall;
function FontDialogHookEx(Wnd: HWnd; Msg: UINT; WParam: WPARAM;
LParam: LPARAM): UINT; stdcall;
begin
if (Msg = WM_COMMAND) and (LOWORD(WParam) = cmb1) then begin
PostMessage(Wnd, WM_FONTDIALOGEX, 0, 0);
end;
if Msg = WM_FONTDIALOGEX then begin
SendDlgItemMessage(Wnd, cmb3, CB_RESETCONTENT, 0, 0);
SendDlgItemMessage(Wnd, cmb3, CB_ADDSTRING, 0, LongInt(PCHAR(str1)));
SendDlgItemMessage(Wnd, cmb3, CB_ADDSTRING, 0, LongInt(PCHAR(str2)));
SendDlgItemMessage(Wnd, cmb3, CB_ADDSTRING, 0, LongInt(PCHAR(str3)));
SendDlgItemMessage(Wnd, cmb3, CB_ADDSTRING, 0, LongInt(PCHAR(str4)));
Result := 1;
exit;
end;
Result := OldHook(Wnd, Msg, WParam, LParam);
end;
function TFOntDialogEx.TaskModalDialog(DialogFunc: Pointer; var DialogData): Bool;
begin
OldHook := TChooseFont(DialogData).lpfnHook;
TChooseFont(DialogData).lpfnHook := FontDialogHookEx;
inherited TaskModalDialog(DialogFunc, DialogData);
end;
procedure Register;
begin
RegisterComponents('NkCtrls', [TFontDialogEx]);
end;
end.
一応動いてます(^^ フォント名毎に表示するサイズリストを変えたい場合は、cmb1 に選択されているフォント名を確認するコードが必要になるでしょう。
一瞬元のポイントサイズがコンボボックスに表示されてしまいますが今のところ他に良い手を思いつきません(^^;
参照: [Delphi-ML:42562] <ダイアログ> <Dialogs> <描画>
0223 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/28 おばQ rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/11 K.Takaoka 編集
Editコントロールで入力を数値専用にする
入力を数値のみにする場合は、Handleの生成を確認した後
SetWindowLong(Edit1.Handle, GWL_STYLE,
GetWindowLong(Edit1.Handle,GWL_STYLE) or ES_NUMBER);
でできます。
SetWindowLong による指定はウィンドウの再生成が行われた時に無効になりますので、Delphi4 のドッキングウィンドウや、Toolbar97 などのコンポーネントを同時に利用される場合には CreateParams をオーバーライドしたコンポーネントを作成するかフローティング状態の変更通知を受け取るたびに SetWindowLong を行う必要があります。
参照: [Delphi-ML:9195] [Delphi-ML:41622] <Standard>
0243 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/09 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/11 K.Takaoka 編集
TMemo のキャレットを非表示にする
> メモコンポーネントでキャレットを消したいのです。
以下のコードでうまくいっています
// TExMemo=class(TMemo)
// TExMemo.WMSetFocusはWM_SETFOCUSのメッセージハンドラ
Procedure TExMemo.WMSetFocus(var Msg:TWMKeyDown);
Begin
Inherited;
HideCaret(Handle);
End;
参照: [Delphi-ML:33802] <Standard>
0235 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/06 西坂良幸 rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/11 K.Takaoka 編集
エディットコントロールにポップアップウィンドウをつけたい
エディットコントロールにボタンをつけた、コンボボックスもどきは、カスタムコントロールでよく見かけます。ボタンを押すと小ウィンドウが開くヤツです。これはどのように作るのでしょうか。
このような小ウィンドウをインプレースコントロールと呼びます。
ポイントは、小ウィンドウの親をコントロールにすることと、独自のフォーカスを与えないことです。これさえ理解できれば後は、小ウィンドウのVisbleの切り替えで開いたり閉じたりします。
ここでは、プロパティを受け渡すことを無視し、ただ開閉だけをやってみます。(マウス右ボタンで開く)
// 定義部
TxEdit = class;
// インプレースコントロール ここではリストボックス
TinPlaceList = class(TListbox)
private
FEdit: TxEdit;
protected
procedure CreateWnd; override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
public
constructor CreateList(AOwner: TComponent; Edit: TxEdit);
end;
// ここでは、TEditから継承する
TxEdit = class(TEdit)
private
FInplaceList:TListbox;
protected
procedure Mousedown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
public
constructor Create(AOwner: TComponent);override;
destructor Destroy;override;
procedure DropDown;
end;
// 実装部
//コンストラクタでコントロールに接続させる
constructor TInplaceList.CreateList(AOwner: TComponent; Edit: TxEdit);
begin
inherited Create(AOwner);
// 小ウィンドウに親のポインタを持たせて接続する
FEdit := TxEdit(Edit);
Visible := false;
end;
// インプレースコントロールの生成とフォーカス制御
procedure TInplaceList.CreateWnd;
begin
inherited CreateWnd;
//親の変更を行う
if not (csDesigning in ComponentState) then
Windows.SetParent(Handle, 0);
//独自のフォカスメッセージを避ける
CallWindowProc(DefWndProc, Handle, WM_SETFOCUS, 0, 0);
end;
//とりあえずマウス(左右)で閉じることにする
procedure TInplaceList.MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
inherited MouseUp(Button, Shift, X, Y);
if Button = mbLeft then
begin
// プロパティの受け渡しをここらで行う
Hide;
end;
if Button = mbRight then
Hide;
end;
// コンストラクタで、インプレースコントロールを生成
constructor TxEdit.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FInplaceList := TInplaceList.CreateList(Self, Self);
with FInplaceList do
begin
Parent := Self;
TabStop := false;
Visible := false;
Top := FInplaceList.Top + Self.Height;
end;
end;
// デストラクタで念のため明示的に解放する
destructor TxEdit.Destroy;
begin
FInplaceList.free;
inherited Destroy;
end;
// ドロップダウンリストの開閉のメソッドを作成する
procedure TxEdit.DropDown;
var
xyPos: TPoint;
begin
if (FInplaceList <> nil) and not FInplaceList.Visible then
with FInplaceList do
begin
xyPos := Self.ClientToScreen(Point(0 + Self.Width - Width, Self.Height));
SetWindowPos(Handle, 0, xyPos.X, xyPos.Y, 0, 0, SWP_NOSIZE or SWP_NOACTIVATE);
Windows.SetFocus(Handle);
Visible := not Visible;
// プロパティの受け渡しをここらで行う
end;
Invalidate;
end;
// マウス右ボタンで開く
procedure TxEdit.Mousedown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
if Button = mbRight then
DropDown;
inherited Mousedown(Button, Shift, X, Y);
end;
TListBoxのかわりに、何でも使えます。TPanelなら、電卓、カレンダなどになりますね。TGridでもいいですね。
実際にカスタムコントロールを作るときは
・やはりボタンをつける
・キーボード入力にも対応させる
・必要なプロパティの受け渡しを行う
・ドロップダウンなどイベントを記述する
・ロストフォーカスで開きっぱなしにしない
などが必要でしょうか。
参照: [Delphi-ML:40241] <コンポーネント開発> <Standard>
0246 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/09 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/09 osamu 編集
DLL 内で TDBCtrlGrid を使うときの注意事項
>DLL化したアプリのフォームにDBCtrlGrid,DBCheckBoxを置いてローカルDB
>の内容を表示させているのですが、DLL化した途端にカレントレコードしか
>表示されなくなってしまいました。
この現象は以下の理由で発生します。
1.DLLの中では、何もしなければApplication.Handle=0です。
2.DBCtrlGridに配置されるコンポーネントの一部(DBCheckBoxを含む)はカレントレコード以外へ複製表示を行うために、Application.Handleを利用しています。
具体的にはDBCtrls.pasのTPaintControl.GetHandleで、次のようなコードで表示用の別ウインドウを生成しています。
with Params do
FHandle := CreateWindowEx(ExStyle, PChar(FClassName),
PChar(TWinControlAccess(FOwner).Text), Style or WS_VISIBLE,
X, Y, Width, Height, Application.Handle, 0, HInstance, nil);
Application.Handle=0 だと CreateWindowEXは失敗し、FHandle=0となるので複製表示が正常に行われないのです。
対処方法としては、Delphiヘルプの TApplication.Handle に書いてありますが、EXE ホストのメインウィンドウのウィンドウハンドルを DLL の Application.Handleに割り当てます。
別の方法としては、上記の TPaintControl.GetHandle の Application.Handle を FOwner.Handle に書き換えてもいいでしょう。
このスレッドではさらに、TApplication.MainForm.Handle を渡すべきか、TApplication.Handle を渡すべきか。また、TApplication.Handle を渡す際には、DLL 側の uses から Forms を削除しなければ、など、使い方に関する細かい注意点が議論されています。
参照: [Delphi-ML:33636] [Delphi-ML:33791] <データベース> <DataControls>
0241 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/09 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/09 osamu 編集
TPageControl でタブをドラッグして順序入れ替え
>マウスでTabSheet上のTabをドラッグして
>Tabの順序を交換するにはどうしたらよいのでしょうか?
面白そうなので、D3 でやってみました。
--- 以下 例文 ---
procedure TForm1.PageControl1MouseDown(Sender: TObject;...);
begin
PageControl1.BeginDrag(false);
end;
procedure TForm1.PageControl1DragOver(Sender, ...);
begin
if Sender is TPageControl then
Accept := true;
end;
procedure TForm1.PageControl1DragDrop(Sender, ...);
var
i: Integer;
r: TRect;
begin
if not (Sender is TPageControl) then
Exit;
with PageControl1 do
begin
for i := 0 to PageCount - 1 do
begin
Perform(TCM_GETITEMRECT, i, LPARAM(@r));
if PtInRect(r, Point(X, Y)) then
begin
if i <> ActivePage.PageIndex then
ActivePage.PageIndex := i;
Exit;
end;
end;
end;
end;
参照: [Delphi-ML:33411] <Win95>
0239 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/08 osamu 編集
IME に未確定文字列を入力
> プログラム中からIMEに未変換・未確定文字列として送りたいのですが。。。
>
> 例
> プログラムで渡す文字列 へいせい10ねん
> ↓
> IME 「へいせい10ねん」(未変換・未確定)
方法1
「へいせい10ねん」を「heisei10nenn」もしくは、
「ヘイセイ10ネン」(ほんとは半角)に変換し、キーボードイベントとして
sendkeysや、keybd_event APIでIMEに送る。
ローマ字変換か、カナ変換かは、ImmGetConversionStatus APIで
判断出来ます。
例
Edit1.SetFocus;
SendKeys('heisei10nenn',true);
方法2
ImmSetCompositionString APIでIMEに直接セットする。
例
var
IMC:HIMC;
BufLen:longint;
Buf:string;
begin
Edit1.SetFocus;
IMC:=ImmGetContext(Edit1.Handle);
Buf:='へいせい10ねん';
BufLen:=length(Buf);
//MS-IME98
ImmSetCompositionStringA(IMC,SCS_SETSTR,PChar(Buf),BufLen,nil,0);
//ATOK12
ImmSetCompositionStringW(IMC,SCS_SETSTR,PChar(Buf),BufLen,nil,0);
ImmReleaseContext(Edit1.Handle,IMC);
end;
MS-IME98とATOK12でテストしましたが、ATOK12の場合、UniCodeの方のAPIで実行しないとちゃんと動作しませんでした。
なぜなのかは、よくわかりません(^^;
#詳しくは、英語のヘルプやAPIの解説書等で調べてください。
参照: [Delphi-ML:32185] <その他Windows関連> <Windows> <Standard>
0003 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/08 osamu 編集
ショートカットキーに'+'を使う。
Delphi2.0 以上では、『!"#$%&'()=~|^{}`*+_?><』など、101のフルキー側(テンキーを除いた部分)で、Shiftと同時にキーを押さなければ入力できない文字はショートカットに使えない。
テンキーがついてたり、JISキーボードならばワンキーで押せるものもあるのに。
こういったキーを使うには、ShortCut プロパティにキーコードを直接セットすればいい。
たとえば、
Test2.ShortCut:=$6B; // <- + キーのキーコード
でも、これだとメニューの右端にキー名が表示されない。 (;_;)
対処方法は [Tips:4]。
参照: [Delphi-ML:17954] [Tips:4] <Standard> <メニュー>
0224 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/28 おばQ rev 1.1.1.5 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/07 西坂良幸 編集
右寄せ・数値入力可能なEditコンポーネントを作りたい。
Win98 以降では、次のようにすれば完璧な右寄せエディタが作成できます。
WindowsNT4 では SP3 (User32.Dll が 03/11/97 07:04p Intel, 03/11/97 06:14p Alpha) から利用できます。
type
TRgEdit = class(TEdit)
protected
procedure CreateParams(var params: TCreateParams); override;
end;
procedure TRgEdit.CreateParams(var params: TCreateParams);
begin
inherited CreateParams(params);
with Params do
Style := Style or ES_RIGHT;
end;
追記のバージョン以前、またDelhi3以前は、
Style := Style or WS_MULTILINE or ES_RIGHT and not WS_VSCROLL and not WS_AUTOVSCROLL ;
とします。
また、入力を数字専用される場合は、上記にES_NUMBERを論理和
させます。
以下は、Alignment、NumOnly の各プロパティを作成する例です
(FAlignment、FNumOnlyやプロパティ定義部分は省略しています)
procedure TRgEdit.CreateParams(var params: TCreateParams);
const
Alignments: array[TAlignment]of Word = (ES_LEFT, ES_RIGHT, ES_CENTER);
NumlOnlies: array[Boolean]of Word = (0, ES_NUMBER);
begin
inherited CreateParams(Params);
Params.Style := Params.Style
or Alignments[FAlignment]
or NumlOnlies[FNumlonly];
end;
procedure TRgEdit.SetAlignment(NewValue: TAlignment);
begin
if FAlignment <> NewValue then
begin
FAlignment := NewValue;
RecreateWnd;// CreateParamsを呼び出すのではないことに注意
end;
end;
procedure TRgEdit.SetNumlOnly(NewValue: Boolean);
begin
if FNumlOnly <> NewValue then
begin
FNumlOnly := NewValue;
RecreateWnd;
end;
end;
参照: [Delphi-ML:9195] [Delphi-ML:41622] <Standard>
0123 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/06 西坂良幸 編集
ButtonのCaptionで改行を使って文字を複数段で表示したい
Windows95では #13#10 挿入することにより思った通り表示できるのですが
WindowsNTの場合は改行されません。
procedure TForm1.Button1Click(Sender: TObject);
begin
SetWindowLong(Button1.Handle, GWL_STYLE, GetWindowLong(Button1.Handle, GWL_STYLE) or BS_MULTILINE);
Button1.Caption := 'ABC' + #13#10 + 'DEF';
end;
なお、コンポ−ネント化する場合はCreateParamsをオーバーライドして下さい。
この方法は、TButtonControl系(TRadioButton,TCheckBox)でほとんど使えますが、オーナードロー系(TBitBtn,TSpeedButtonなど)のボタンではできません。
また、以下のプロパティエディッタをインストールすれば、オブジェクトインスペクタで,改行コードを入力を'\n'ですることが出来るようになります。
// 定義部
type
// 複数行の入力を\nで受け入れるプロパティエディッタ
TMultCapProperty = Class(TCaptionProperty)
Public
Function GetValue: string; Override;
Procedure SetValue(const Value: string); Override;
End;
procedure Register;
// 実装部
// 置き換える関数
procedure ReplaceStr(var Source : string; Search, Replace : string);
function XPos(Source, Search : string):integer;
begin
if StrPos(PChar(Source), PCHar(Search)) = nil then
result := 0
else
result := StrPos(PChar(Source), PCHar(Search)) - PChar(Source) + 1;
end;
var
p, L1, L2 : Integer;
begin
L2 := Length(Search);
p := XPos(Source, Search);
while p <> 0 do
begin
L1 := Length(Source);
if p = 1 then
Source := Replace + Copy(Source, L2 + 1, L1)
else
Source := Copy(Source, 1, p - 1) + Replace +
Copy(Source, p + L2, L1);
p := XPos(Source, Search);
end;
end;
function TMultCapProperty.GetValue: string;
begin
Result := GetStrValue;
// 以下3つのパターンがある
ReplaceStr(Result, #13 + #10, '\n');
ReplaceStr(Result, #10, '\n');
ReplaceStr(Result, #13, '\n');
end;
procedure TMultCapProperty.SetValue(const Value: string);
var
Caption : string;
begin
Caption := Value;
ReplaceStr(Caption, '\n', #13);
SetStrValue(Caption);
end;
// プロパティエディタとして登録する
procedure Register;
begin
// TLabelのCaptionプロパティエディタの登録--'\n'で改行入力
RegisterPropertyEditor(TypeInfo(TCaption), TLabel; , 'Caption', TMultCapProperty);
// TButtonのCaptionプロパティエディタの登録--'\n'で改行入力
// ただし上記BS_MULTILINEが設定されていないとダメ
RegisterPropertyEditor(TypeInfo(TCaption), TButton; , 'Caption', TMultCapProperty);
end;
参照: [Delphi-ML:17735] [Delphi-ML:39679] [builder:6270] <コンポーネント開発> <Standard>
0063 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/06 西坂良幸 編集
キーボードでボタンを押したとき、ボタンをちゃんと沈ませたい
BM_SETSTATE メッセージを使うとできます。
コンポーネント化は、ちょっと長くなるので、詳細は [Delphi-ML:19607] を見てください。
以下、スペースキーでの例です。
procedure TForm1.Button1KeyPress(Sender: TObject; var Key: Char);
begin
if Key = #32 then
SendMessage(Button1.Handle,BM_SETSTATE,1,0);
end;
procedure TForm1.Button1KeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = 32 then
SendMessage(Button1.Handle,BM_SETSTATE,0,0);
end;
コーディングだけで呼ぶ場合は
SendMessage(Button1.Handle,BM_SETSTATE,1,0);
Button1.Click;
Sleep(100); // 適当に好みで決める
PostMessage(Button1.Handle,BM_SETSTATE,0,0);
参照: [Delphi-ML:19607] <Standard>
0237 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/06 西坂良幸 rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/06 西坂良幸 編集
カーソルキーでボタン(TButton)のフォーカス移動をやめさせたい
TButtonのOnKeyPress、OnKeyDownなどでやってみてもダメですね。これは、Windowsのボタンの仕様です。
フォームへのCM_DialogKeyメッセージをとらえて処理すると可能です。
procedure TForm1.CMDialogKey(var Msg:TCMDialogKey);
begin
case Msg.CharCode of
VK_UP,VK_DOWN,VK_LEFT,VK_RIGHT : Msg.CharCode:=0;
end;
inherited;
end;
このメッセ−ジはタブキー(VK_TAB)もとらえることが出来ます。
参照: [Delphi-ML:3691] [Delphi-ML:33541] <フォーム> <Standard>
0236 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/06 西坂良幸 rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/06 西坂良幸 編集
エディットコントロールにコンボボックスのようなボタンをつけたい
エディットをParentとするフォーカスを持たないTSpeedButtonを、貼り付けてやれば簡単です。
注意するのは、編集領域がボタンに重ならないようにEM_SETRECTNPを送ることですが、このメッセージが有効になるには、TEditのスタイルフラッグにES_MULTILINEを加えなければなりません。
以下の例は、ボタンを押せば単にメッセージボックスがでるだけのものです。
// 定義部
TxEdit = class(TEdit)
private
FButton: TSpeedButton;
FOnButtonClick: TNotifyEvent;
procedure SetEditRect;
procedure WMSize(var Message: TWMSize); message WM_SIZE;
protected
procedure ButtonClick (Sender: TObject);
procedure CreateParams(var Params: TCreateParams); override;
procedure CreateWnd; override;
public
constructor Create(AOwner: TComponent); override;
published
property OnButtonClick: TNotifyEvent read FOnButtonClick write FOnButtonClick;
end;
// 実装部
constructor TxEdit.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
// ボタンを作成する
FButton := TSpeedButton.Create (Self);
with FButton do
begin
Parent := Self;
Width := 18;
Height := Height - 4;
// リソースから ▼ のビットマップ(10×8程度×2)を読むのは省略
// NumGlyphs := 2;
// result.Glyph.LoadFromResourceName(HInstance,'????');
CurSor := crArrow;
OnClick := ButtonClick;
end;
end;
// スタイルフラッグにES_MULTILINEが必要です。
procedure TxEdit.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.Style := Params.Style or ES_MULTILINE;
end;
// ハンドルが生成されてからSetEditRectを呼ぶ
procedure TxEdit.CreateWnd;
begin
inherited CreateWnd;
SetEditRect;
end;
// エディット(編集)領域を再設定する(ボタンの部分を排除)
procedure TxEdit.SetEditRect;
var
Loc: TRect;
begin
SendMessage(Handle, EM_GETRECT, 0, LongInt(@Loc));
Loc.Right := ClientWidth - FButton.Width - 2;
SendMessage(Handle, EM_SETRECTNP, 0, LongInt(@Loc));
end;
// 常に左端にアジャストさせる
procedure TxEdit.WMSize(var Message: TWMSize);
begin
inherited;
if FButton <> nil then
begin
if NewStyleControls and Ctl3D then
FButton.SetBounds(Width - FButton.Width - 4, 0, FButton.Width, Height - 4)
else FButton.SetBounds (Width - FButton.Width, 1, FButton.Width, Height - 2);
SetEditRect;
end;
end;
// ボタンのクリックに対応するイベントを設定する
procedure TxEdit.ButtonClick (Sender: TObject);
begin
if Assigned(FOnButtonClick) then FOnButtonClick(self);
ShowMessage('ボタンが押されました')
// ここに必要な処理を書く
end;
この他、CM_EnabledChangeを捕まえて、EditとボタンのEnabledを同期させることが必要でしょうか。
参照: <コンポーネント開発> <Standard>
0198 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/07/09 osamu rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/05 西坂良幸 編集
ドッカブルメニュー(ツールバー形式のメニュー)を実現したい。
Delphi4でドッカブルメニューを実現するにはどうしたら良いのでしょうか?
IDEでは実現されているので、出来るとは思うのですが...。
アメリカのインプライズのサイトで、ドッカブルメニューのコンポが配られていますよ。使用した感じ日本語環境でも大丈夫そうです。まぁ、多分かなりの問い合わせがあったんでしょうね。
TMenubar -> http://www.borland.com/devsupport/delphi/downloads/index.html
このコンポーネントを使わない場合は、以下の手順で行います。[Delphi-ML:35243][Delphi-ML:35223] Oyats、fumika
・フォームにTCoolBarを配置し、そのTCoolBarの上にTToolBarを配置
・TToolBarのShowCaptionプロパティをTrue、FlatプロパティをTrue、EdgeBordersのedTopプロパティをFalseにする
・フォームにTMainMenuを配置
・TMainMenuのメニューエディッタで、作りたいメニューを作る(MenuItemの作成)
・TToolBarの上にTToolButtonを作成し、各TToolButtonのMenuItemプロパティにTMainMenuの各MenuItem(Parent)を対応させる
・各TToolButtonのGroupedプロパティをTrue、AutoSizeプロパティをTrueにする
・フォームのMenuプロパティをnilにする(メニュー表示を消す)
・フォームのOnShortCutイベントに次のハンドラを設定する
procedure TForm1.FormShortCut(var Msg: TWMKey; var Handled: Boolean);
begin
Handled := Handled or MainMenu1.IsShortCut(Msg);
end;
TMenuBarを使う人も
http://member.nifty.ne.jp/cosmic/delphi/tips_vcl.html#tmenubar
は覗いておきましょう。
参照: [Delphi-ML:32742] [Delphi-ML:35223] <Win95> <Standard>
0216 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/25 おばQ rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/01 おばQ 編集
RichEditでHTMLタグを色・書式付き表示をする
HTMLソースを表示する時タグだけ色分けや書式付きで表示されると便利です。
以下のソースを入力して、RichEditにHTMLソースを表示させて
ボタンを押すと、なかなか高速にタグだけに色がつきます。
テキストを検索してSelAttributesで色付けしてもいいのですが、
あまりにも遅いのでほとんどAPIを使います。
タグを入力してリアルタイムに色をつけることは少々ムズカシそうだったので
実装していません。こちらと組み合わせてみるのもよいでしょう。
そちらの方法は、自分で考えてね(^^;;;
//関数部
procedure RichEditHTMLTagPickUp(RichEdit: TRichEdit; TagColor: TColor);
function ColorToStringHex(Value: TColor): string;
begin
Result := '$00' + IntToHex(ColorToRGB(Value),6)
end;
var
mask: Longint;
CFmt: TCharFormat;
Str: String;
PTop, PPos, PEnd, PNext: PChar;
CR: TCharRange;
begin
try
RichEdit.Lines.BeginUpdate;//高速化、1
mask := SendMessage(RichEdit.Handle, EM_GETEVENTMASK, 0, 0);//高速化、2
SendMessage(RichEdit.Handle, EM_SETEVENTMASK, 0, 0);
with RichEdit do
begin
SelStart := length(Text);//高速化、3
Perform(EM_SCROLLCARET, 0, 0);
case 1 of
0:
begin
CFmt.cbSize := sizeof(CFmt);
CFmt.dwMask := CFM_BOLD;
CFmt.dwEffects := CFE_BOLD;
end;
1:
begin
CFmt.cbSize := sizeof(CFmt);
CFmt.dwMask := CFM_COLOR;
CFmt.crTextColor := ColorToRGB(TagColor);
end;
end;
Str := RichEdit.Text;
PTop := PChar(Str);
PPos := PTop;
while (AnsiStrScan(PPos, '<') <> nil) do//高速化、4
begin
PPos := AnsiStrScan(PPos, '<');
PEnd := PPos;
while (AnsiStrScan(PEnd +1 , '>') <> nil) do
begin
PNext := AnsiStrScan(PEnd +1 , '>');
PEnd := PNext +1;
while (PEnd = PChar(#13)) or (PEnd = PChar(#10)) do
Inc(PEnd);
if PEnd <> PChar('<') then Break;
end;
CR.cpMin := PPos - PTop;
CR.cpMax := PEnd - PTop;
RichEdit.Perform(EM_EXSETSEL, 0, lParam(@CR));
RichEdit.Perform(EM_SETCHARFORMAT, 1, lParam(@CFmt));//書式決定
PPos := PEnd;
end;
end;
finally
SendMessage(RichEdit.Handle, EM_SETEVENTMASK, 0, mask);//高速化、2終
RichEdit.Lines.EndUpdate;//高速化、1終
end;
end;
//実装部
procedure TForm1.Button2Click(Sender: TObject);
begin
RichEditHTMLTagPickUp(RichEdit1, Form1.Color);
end;
注意:タグが綺麗に閉じられていないと無限ループに
はまってしまう事が考えられます。気をつけてください
内部でcase文に1を設定している所があります。
ここを0にしますとHTMLタグがBold属性になります。
1の場合ですとForm1.Colorになります。
適当に書きなおしてください。
RichEditのUndoバッファは影響を受けますので注意です。
元々はC++BuilderMLでのC++Builder用のTipsでしたが、
Delphiに書き換えました。
MLのC++BuilderでのサンプルソースはMLを参照してください。
Delphi版とほぼ同じ内容です。
参照: [builder:8463] <WWW> <通信> <Win95>
0231 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/31 おばQ rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/31 おばQ 編集
LinesプロパティエディタでTabを入力する
TMemoやTRichEditでは設計時にLinesプロパティを
設定することが出来ます。
文字列リストの設定ダイアログ(という名前か?)
というLinesのプロパティエディタが表示されて編集します。
常識ですね。
さて、ここでTabキーを押してもTabが入力出来ません!
さあ、困った困ったどうしよう。
そういう時にはCtrl+Tabキーを押します。
参照: <Win95> <Standard>
0215 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/25 花井@自宅 rev 1.5 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/31 花井@自宅 編集
RichEdit の全パラグラフのタブ幅を設定する
RichEdit では、各パラグラフごとにタブ位置を設定できます。
[DelphiML:41963]にて、全パラグラフに一定間隔のタブ位置を
設定する関数を紹介しています。等幅フォント専用です。
(補足)
Tab の幅は書式なので書式付きテキストの OLE D&D と
ペースト処理時にはTabの幅が変化すると予想されます。
ペースト時の書式消しは可能ですが OLE D&D を防ぐ事は
まず無理ですので、そのあたりはご容赦ください。
参照: [Delphi-ML:41963] <Win95>
0211 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/21 おばQ rev 1.7 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/31 花井@自宅 編集
TRichEdit.Paragraph.Tab[] の設定(Tab位置の設定)
<書式>
RichEdit1.Paragraph.Tab[iIndex] := ptsAbsolute
<値>
iIndex: Byte
タブインデックス。段落の何番目のタブ位置を設定するかを示す。
範囲は 0 から MAX_TAB_STOPS - 1 まで。
(定数 MAX_TAB_STOPS は RichEdit.pasで定義)
ptsAbsolute: LongInt
タブ位置。コントロール水平端からの絶対位置で単位は論理ポイント。
(1論理ポイントは 1/72論理インチ)
<解説>
・現在選択範囲のパラグラフのタブ位置設定を行います。範囲選択がされて
いないときは、キャレット(文字カーソル)のあるパラグラフになります。
・ptsAbsoluteにスクリーンのピクセル値を元にした値を設定
したい場合には、次のように変換します。
(論理ポイント) =
72 * ( ピクセル値 / 水平方向1論理インチ当りのピクセル数 )
<備考>
・Delphi のバージョンによっては、バグのために Tab[] プロパティの設定
が行われないパターンが発生します。詳細は[Delphi-ML:41966] をご覧下さい。
(Delphi 4.0 Update 3 には存在しています。)
<サンプル>
テキスト選択範囲内にあるパラグラフの最初のタブ位置をコントロール
中央に設定します
// 四捨五入します
function RoundOff(X: Extended): LongInt;
begin
if X >= 0 then
Result := Trunc(0.5 + X)
else
Result := Trunc(-0.5 + X);
end;
// テキスト選択範囲内にあるパラグラフの最初のタブ位置を
// コントロール中央に設定します
procedure TForm1.RichEdit1SetFirstTabAtCenter;
var
DC: HDC;
PixelsPerInch: Integer;
CenterByPixels: Currency;
CenterByPoints: Currency;
begin
// 水平方向1論理インチ当りのピクセル数を取得します。
DC := GetDC(0);
PixelsPerInch := GetDeviceCaps(DC, LOGPIXELSX);
ReleaseDC(0, DC);
// RichEdit1 の中央を得ます(ピクセル値)
CenterByPixels := RichEdit1.ClientWidth / 2;
// スクリーンのピクセル値から論理ポイント値に変換します
CenterByPoints := 72 * (CenterByPixels / PixelsPerInch);
// タブ位置を設定します。
RichEdit1.Paragraph.Tab[1] := 0; // バグ回避のため
RichEdit1.Paragraph.Tab[0] := RoundOff(CenterByPoints);
end;
// RichEdit1SetFirstTabAtCenter を呼び出します。
procedure TForm1.Button1Click(Sender: TObject);
begin
RichEdit1SetFirstTabAtCenter;
end;
参照: [Delphi-ML:41923] [Delphi-ML:41966] <Win95>
0222 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/28 西坂良幸 rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/28 西坂良幸 編集
データコントロールのDataLinkオブジェクトを取得する方法(ReadOnlyプロパティのバグの解決法)
VCLでは、DataControlのFDataLinkは、プロパティになっていないので、アクセスできませんが、
DataControlにCM_GETDATALINKメッセージを投げる事で取得(参照)できます。[Delphi-ML:41387]田原
多重継承ができないので、こうなっているそうです。
これによって、MLでも多い質問ですが、いわゆる「極東仕様」問題、すなわち、TDBEdit の ReadOnly と継承元の TCustomEdit のReadOnly の同期がとれないバグを解決することができます。[Delphi-ML:41051]藤中
以下は、藤中さんの例示コード(省略部あり)です。
// このデータコントロール(フィールド対応)は編集可能か?
function IsCanModifyFieldDataControl(Control: TControl) : Boolean;
var
DataLink : TFieldDataLink;
begin
// データリンクを取得
DataLink := TFieldDataLink(Control.Perform(CM_GETDATALINK,0,0));
if (DataLink = nil) then
DatabaseError('CM_GETDATALINK は無効', Control);
// データリンクが編集可能かどうか調べる
Result := DataLink.CanModify and
( DataLink.DataSource.AutoEdit or
(DataLink.DataSource.State in [dsInsert, dsEdit]) );
end;
// たとえば、Table1のAfterScrollイベントなどで動的にReadOnlyを変更する
type TCustomEditCracker = class(TCustomEdit);
procedure TForm1.Table1AfterScroll(DataSet: TDataSet);
var
Flg :Boolean;
begin
// 条件
if DataSet.FieldByName('制御フラグ').AsString = '1' then
Flg := True
else
Flg := False;
// 以下でReadOnlyを変更
DBEdit1.ReadOnly := Flg;
if (Flg = False) then
begin
TCustomEditCracker(DBEdit1).ReadOnly :=
not IsCanModifyFieldDataControl(DBEdit1);
end
else begin
TCustomEditCracker(DBEdit1).ReadOnly := True;
end;
end;
参照: [Delphi-ML:41387] [Delphi-ML:17611] [Delphi-ML:41051] <データベース> <バグ> <DataControls>
0219 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/26 西坂良幸 rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/26 西坂良幸 編集
セルのテキストをドラッグイメ−ジにしてグリッド(TStringGrid)でドラッグ&ドロップを行う
やり方は2つあって、Drag Object をすりかえる方法と
コントロール側で DoStartDrag, DoEndDrag, GetDragImages 等を
override する方法があります。[d40216]中村
以下は、[d40948]で須賀さんが書いたコードを若干省略したものです。
Drag&Dropのやり方として大変参考になりますね。
type
TForm1 = class(TForm)
//<省略>
・・・・・
public
CursorSave:TCursor;
end;
type
TCustomDragObject = class(TDragControlObject)
public
function GetDragImages: TDragImageList; override;
procedure HideDragImage; override;
procedure ShowDragImage; override;
function GetDragCursor(Accepted: Boolean; X, Y: Integer)
: TCursor; override;
procedure Finished(Target: TObject; X, Y: Integer;
Accepted: Boolean); override;
end;
//<省略>
・・・・・
var // ローカルに宣言
w_x,w_y:integer; // もとのcellの位置を取っておく
Images: TImageList; // Image作成用
drag_sizex,drag_sizey:integer; // 作成したimageのサイズ
drag_enter_sw:smallint; // drag start時かどうかのsw
function TCustomDragObject.GetDragImages: TDragImageList;
begin
Result := Images;
end;
procedure TCustomDragObject.HideDragImage;
begin
GetDragImages.HideDragImage;
end;
procedure TCustomDragObject.ShowDragImage;
begin
GetDragImages.ShowDragImage;
end;
procedure TCustomDragObject.Finished;
begin
inherited;
Free;
end;
function TCustomDragObject.GetDragCursor;
begin
Result := crDeFault;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
drag_enter_sw := 0; // drag start時かどうかのSwをクリア
Grid1.ControlStyle := ControlStyle + [csDisplayDragImage];
// 適当にセルに文字列を・・・
Grid1.cells[1,1]:='AAA';
end;
procedure TForm1.Grid1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
var
acol,arow:integer;
begin
if source is TCustomDragObject then
begin
Accept := True;
Grid1.mousetocell(x, y, acol, arow);
if (acol<0) or (arow<0) then //
begin
Grid1.DragCursor := crNoDrop;
exit;
end
else begin
Grid1.DragCursor := crDrag;
end;
if State=dsDragMove then
begin
if drag_enter_sw = 0 then // start drag時以外のとき
begin
if (y-drag_sizey) <= Grid1.rowheights[1] then // 上にscroll
begin
if Grid1.toprow > 1 then
begin
Grid1.toprow := Grid1.toprow - 1 ;
Grid1.repaint;
end;
end;
if (y >= (Grid1.height - 5)) then // 下にscroll
begin
if (Grid1.rowcount - Grid1.visiblerowcount)>Grid1.toprow then
begin
Grid1.toprow := Grid1.toprow + 1;
Grid1.repaint;
end;
end;
end
else
drag_enter_sw := 0;
end;
end;
end;
procedure TForm1.Grid1StartDrag(Sender: TObject;
var DragObject: TDragObject);
var Size: TSize;
bm: TBitmap;
acol,arow:integer;
begin
drag_enter_sw := 1;
// drag start時にswをたてる
Grid1.mousetocell(Grid1.screentoclient(mouse.CursorPos).x,Grid1.screentoclient(mouse.CursorPos).y, acol, arow);
w_x := acol; // drag開始時のcellの位置を取っておく
w_y := arow; // drag開始時のcellの位置を取っておく
// textのimageを作成
bm := TBitmap.Create;
bm.Canvas.Font := Font;
Size := bm.Canvas.TextExtent(Grid1.cells[acol, arow]);
bm.Width := Size.cx;
bm.Height := Size.cy;
drag_sizex := Size.cx;
drag_sizey := Size.cy;
bm.Canvas.TextOut(0, 0, Grid1.cells[acol, arow]);
Images := TImageList.Create(Self);
Images.Width := Size.cx;
Images.Height := Size.cy;
Images.Add(bm, Nil);
bm.Free;
Images.SetDragImage(0, Size.cx, Size.cy);
Images.EndDrag;
// カーソル処理
CursorSave := Screen.Cursor;
Screen.Cursor := crDefault;
// DragImageを作成
DragObject := TCustomDragObject.Create(Grid1); // dragobjectの差し替え
end;
procedure TForm1.Grid1EndDrag(Sender, Target: TObject; X, Y: Integer);
begin
Screen.Cursor := CursorSave;
Grid1.repaint;
images.free; // drag終了時にTImageListの開放 これを忘れていました !!
end;
procedure TForm1.Grid1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
acol,arow:integer;
begin
Grid1.mousetocell(x, y, acol, arow);
if (acol < 0) or (arow < 0) then // scroll barや最後の行より下のところにdropしたときは何もしない
begin
Grid1.DragCursor := crNoDrop;
exit;
end;
if (Sender = Grid1) then // drag開始時のcellの内容をdropしたcellにcopy
begin
Grid1.mousetocell(x, y, acol, arow);
Grid1.cells[acol, arow] := Grid1.cells[w_x, w_y];
Grid1.repaint;
end;
end;
なお、落とし穴があります。
IDEの中では、ドラッグカーソルとドラッグイメージの合成処理の過程で(TCustomImageList.CombineDragCursor)
Win95/98 が落ちることが前は良く起きました。Video Driver の問題らしいのですが、いまだに原因が良く判らないそうです。
EXEレベルではこの障害はでません。
参照: [Delphi-ML:22670] [Delphi-ML:40662] [Delphi-ML:40977] <Windows> <その他コンポーネント関連>
0220 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/26 西坂良幸 rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/26 西坂良幸 編集
ツリービューのノードをドラッグ&ドロップで移動させる
TTreeViewでは、DragImageにこだわらなければ,DragModeプロパティを dmAutomatic に設定すれば、OnDragOver、OnDragDropイベントを使うだけで簡単にできます。
OnDragOverイベントのAcceptは,ユーザーがドロップするオブジェクトをそのコントロールが受け入れることを示すだけです。
ツリー構造ですので、移動が無限ルーチンに陥らないように工夫します。
下記では、
IsFamilyメソッドで、ファミリー内にその上位ノードを移動するのか
を判定し、MoveNodeメソッドですべての子ノードが移動できるように
しています。
type
TForm1 = class(TForm)
// <省略>
・・・・・
・・・・・
private
procedure MoveNode(Parent, Source: TTreeNode);
function IsFamily(Parent, Source: TTreeNode):boolean;
public
end;
// <省略>
・・・・・
・・・・・
procedure TForm1.FormCreate(Sender: TObject);
var
i:integer;
Node:TTreeNode;
begin
// 適当にtreeを作成
for i := 1 to 20 do
begin
node := TreeView1.Items.Add(nil,'A00'+IntToStr(i));
node.ImageIndex := 0;
node.SelectedIndex := 1;
end;
end;
// 移動と削除−−すべての子ノードを移動する
procedure TForm1.MoveNode(Parent, Source: TTreeNode);
var
node: TTreeNode;
begin
node := TreeView1.Items.AddChild(Parent,Source.Text);
node.ImageIndex := Source.ImageIndex;
node.SelectedIndex := Source.SelectedIndex;
while Source.Count > 0 do
// 再帰させる
MoveNode(Node, TTreeNode(Source.Item[0]));
Source.Delete;
end;
// Sourceの一族にParentがあれば true の判定
function TForm1.IsFamily(Parent, Source: TTreeNode):boolean;
var
i:integer;
node: TTreeNode;
begin
result := false;
for i := 0 to Source.Count - 1 do
begin
node := Source.Item[i];
if Node.AbsoluteIndex = Parent.AbsoluteIndex then
result := true
else
// 再帰させる
result := IsFamily(Parent, Node);
if result then break;
end;
end;
// ドロップ後の処理
procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
Var
Node:TTreeNode;
begin
if (Sender is TTreeView) and (Source is TTreeView) then
begin //ツリーからツリー
with Sender, Source as TTreeView do
begin
//新親の親
Node := GetNodeAt(X,Y);
if Node = Selected then
begin
MessageDlg('同じ所に移動はできません。', mtInformation, [mbOk], 0);
Exit;
end;
//ツリーのアイテムを移動し削除
Items.BeginUpdate;
if IsFamily(Node, Selected) then
//無限循環を避ける
MessageDlg('この移動はできません。', mtInformation, [mbOk], 0)
else
MoveNode(Node, Selected);
Items.EndUpdate;
end;
end;
end;
// ソースを確認する
procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
Accept := (Source as TTreeView) = TreeView1;
end;
参照: [Delphi-ML:3727] [Delphi-ML:22691] [Delphi-ML:30411] <Win95>
0217 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/26 西坂良幸 rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/26 西坂良幸 編集
書式付きメモ型項目へアクセスしたい
ヘルプなどでは、Streamをつかった方法が紹介されています。
DataSet.FieldByName('Field1').AsString := RichEdit1.Text;
や
DataSet.FieldByName('Field1').AsVariant := RichEdit1.Text;
では、PlainText となり、書式が保存されません。
Field1 書式付きメモ型とし、
DataSetは開かれているとします。
// リッチエディットからフィールドへの書き込み
procedure TForm1.Button8Click(Sender: TObject);
var
Stream1 : TMemoryStream;
begin
Stream1 := TMemoryStream.Create;
try
RichEdit1.Lines.SavetoStream(Stream1);
Query1.Edit;
TBlobField(Query1.FieldByName('Field1')).LoadFromStream(Stream1);
Query1.Post;
finally
Stream1.Free;
end;
end;
// フィールドからリッチエヂットへの読み出し
procedure TForm1.Button4Click(Sender: TObject);
var
Stream1 : TBlobStream;
begin
Stream1 := TBlobStream.Create(TBlobField(Query1.FieldByName('Field1')), bmRead);
try
RichEdit1.Lines.LoadFromStream(Stream1);
finally
Stream1.Free;
end;
end;
TRichEdit系は、いくつかのバグが報告されています。
データが大きくなるとヤバイという噂です。
D4では、大部改善されたようですが、気をつけて下さい。
バグ情報は
http://www.dataweb.nl/~r.p.sterkenburg/indexpag.htm
参照: [Delphi-ML:5484] [Delphi-ML:41858] <データベース> <DataAccess>
0004 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/14 osamu 編集
ショートカットキーのキー名を独自に設定する
例えば、テンキーの + キーをショートカットにした場合に、'Num +'とか表示させたい。
TMenuItem.AppendTo()を参照すると、
TestMenu1.Caption:=TestMenu1.Caption+#9+'Num +'+#0;
とすればよいことが分かる。
キャプションとショートカット名との間にはさんだタブ文字と、自動で追加されるショートカットを隠すために最後にくっつけられた #0 とが味噌。
参照: [Delphi-ML:17989] <Standard> <メニュー>
0192 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/05/28 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/05/28 osamu 編集
TFileListBox にファイル名が重複して表示されてしまう
TFileListBox.Mask に、'*.txt;*.?x?' などと入力した場合、もし複数の Mask に合致するファイル(例えば、example.txt)があると、そのファイルはダブって表示されてしまいます。これを回避する単純な方法は、TDirectoryListBox.OnChange にて、重複しているファイル名を削除してやることです。
procedure Form1.DirectoryListBox1Change(Sender: TObject);
var i: Integer;
begin
for i:=FileListBox1.Items.Count-2 downto 0 do
if FileListBox1.Items[i]=FileListBox1.Items[i+1] then
FileListBox1.Items.Delete(i+1);
end;
この位のことは、TFileListBox 自身で面倒を見てもらいたいものですが。。。
参照: <System>
0191 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/05/19 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/05/19 osamu 編集
コンボボックスのリスト部分の幅を指定する
>コンボボックスをクリックして表示されるリストの幅を
>コンボボックスコントロール自体の幅よりも広くして
>表示することはできないでしょうか?
ComboBox1->Perform(CB_SETDROPPEDWIDTH, 幅, 0);
です。詳細は Win32.hlp(English) を参照してください。
参照: [builder:12689] <Standard>
0188 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/05/19 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/05/19 osamu 編集
TRichEdit の内容を高速に変更する
> RichEdit の Lines の内容を、次の様に変更しているんですが、
> 3000行で10秒もかかってしまいます。
>
> var sl: TStringList;
>
> for i:=1 to 3000 do
> sl.Add(IntToStr(i));
> RichEdit1.Lines.Assign(sl);
ストリームを経由すると速いですよ。
var ms: TMemoryStream;
ms := TMemoryStream.Create;
try
sl.SaveToStream(ms);
ms.Position := 0;
RichEdit1.Lines.LoadFromStream(ms);
finally
ms.Free;
end;
3000行で一秒以下でした。(Pentium 150MHz)
参照: [builder:16379] <Win95>
0179 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/05/01 おばQ rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/05/01 おばQ 編集
右の項目の幅が固定されるTStatusBar
Delphiで普通にTStatusBarを使用してスターテスバーを作り
複数のTStatusPanelを設定すると
Formをリサイズする時、右の項目の幅が変化します。
IEエクスプローラやワードパッドなどでは
WindowをResizeした時に
StatusBarの項目の右の方のものが幅が固定されていて
左の項目の幅が変化するものがあります。
推定ですが、VC++アプリケーションウィザードを普通に
用いるとこのようなものが自動で生成されるようです。
これをDelphiで実現します。
StatusBarのResizeイベントに以下のようなコードを書きます。
procedure TForm1.StatusBarResize(Sender: TObject);
const
ResizePanelNumber=0; //リサイズするStatusbarのパネル番号を指定
MinSize=0; //リサイズされても維持したい最小のWidthを指定
var
BarWidth,i: Integer;
begin
with StatusBar do
begin
BarWidth := 0;
for i:=0 to Panels.Count-1 do
begin
if not(i=ResizePanelNumber) then
BarWidth := BarWidth + Panels[i].Width;
end;
if (Width-BarWidth)<=MinSize then
Panels[ResizePanelNumber].Width := MinSize
else
Panels[ResizePanelNumber].Width := Width - BarWidth;
end;
end;
これで項目のうちResizePanelNumberで指定したパネルのWidthが
StatusBarのResize、つまりFormのリサイズに合わせてリサイズします。
また、
すべての項目において右の項目の幅が優先されて維持される
IEブラウザのような動作をさせるには工夫が必要です。
設計時のPanelのWidthを保持して
Panels.Widthを変化させます。
TForm1 = class(TForm)
…
private
FBarWidths: array of Integer;
end;
procedure TForm1.FormCreate(Sender: TObject);
procedure SetStatusBarWidth;
var //右項目固定StatusBarの為に設計時のpanelのWidthを保存
i: Integer;
begin
with StatusBar do
begin
SetLength(FBarWidths,Panels.Count);
for i:=0 to Panels.Count-1 do
FBarWidths[i]:= Panels[i].Width;
end;
end;
begin
SetStatusBarWidth;
end;
procedure TForm1.StatusBarResize(Sender: TObject);
var
BarWidth,i,PointPanel,k: Integer;
begin
BarWidth:=0;
with StatusBar do
begin
PointPanel := Panels.Count;
repeat
dec(PointPanel); //何番のパネルをリサイズするべきか決定する
BarWidth := BarWidth + FBarWidths[PointPanel];
until (Width < BarWidth)or(PointPanel=0) ;
for i:=0 to PointPanel-1 do //消えるべきパネルをWidth:=0にする
Panels[i].Width := 0;
BarWidth := 0;
for i:=PointPanel+1 to Panels.Count-1 do
begin
Panels[i].Width := FBarWidths[i];
BarWidth := BarWidth + FBarWidths[i];
end;
Panels[PointPanel].Width := StatusBar.Width - BarWidth;
end;
end;
分かりにくい説明ですが、プログラムを実行すると分かります。
VCL化してみるのも面白いでしょう。
参照: <Win95>
0178 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/04/29 おばQ rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/04/29 おばQ 編集
TCoolBarを用いたツールバーの位置保存
クールバーでツールバーを作った場合、
その位置を起動時に前回の状態に戻す為の処理です。
この場合Delphi4で採用されているドッカブルツールバーには
対応していません。もう少し、工夫が必要になるでしょう。
今回はIniファイルにに保存すると言う前提で解説します。
クールバー上のツールバーの数をBandCountとして
const
BandCount=5;
後はFormのクリエイトイベントとクローズイベントに
以下の様なコードを書きます。
procedure TForm1.FormCreate(Sender: TObject);
procedure BandPositionRead;
var
ini: TIniFile;
BandIDNumber,BandWidth: array[0..BandCount-1] of Integer;
BandBreak: array[0..BandCount-1] of Boolean;
i: Integer;
begin
ini := TIniFile.Create(
ExtractFilePath(Application.ExeName)+'BandPos.ini');
with ini do
try
for i:=0 to BandCount-1 do
begin //Bandの位置とBreakを読込む
BandIDNumber[i]:=
ReadInteger('Bar','Band'+IntToStr(i)+'ID',i);
BandWidth[i] :=
ReadInteger('Bar','Band'+IntToStr(i)+'Width',200);
BandBreak[i] :=
ReadBool('Bar','Band'+IntToStr(i)+'Break',true);
end;
finally
Free;
end;
CoolBar1.Bands.BeginUpdate;
for i:=0 to BandCount-1 do
begin //Bandの位置とBreakを復元します。
CoolBar1.Bands.FindItemID(BandIDNumber[i]).Index := i;
CoolBar1.Bands[i].Break := BandBreak[i];
CoolBar1.Bands[i].Width := BandWidth[i];
end;
CoolBar1.Bands.EndUpdate;
end;
begin
BandPositionRead;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
procedure BandPositionWrite;
var
ini: TIniFile;
i: Integer;
begin
ini := TIniFile.Create(
ExtractFilePath(Application.ExeName)+'BandPos.ini');
with ini do
try
for i:=0 to BandCount-1 do
begin //Bandの位置とBreakを書きこむ
WriteInteger('Bar','Band'+IntToStr(i)
+'ID',CoolBar1.Bands[i].ID);
WriteInteger('Bar','Band'+IntToStr(i)
+'Width',CoolBar1.Bands[i].Width);
WriteBool('Bar','Band'+IntToStr(i)
+'Break',CoolBar1.Bands[i].Break);
end;
finally
Free;
end;
end;
begin
BandPositionWrite;
end;
参照: [Delphi-ML:32427] <Win95>
0168 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
IME 入力で読み仮名を取得する
以下のコードを含んだ関数を作成し、追跡したいところで
Application->OnMessage = AppMessage;
と書けばよいのではないでしょうか? はずしていたらすみません。
void __fastcall TForm1::AppMessage(TMessage &Msg, bool &Handled)
{
if (Msg.message == WM_IME_ENDCOMPOSITION)
{
int nRetVal; //APIの戻り値を格納
HIMC hImcIMEHandle; //IMEのコンテキストを格納
char cBuff[256];
hImcIMEHandle = ImmGetContext(Handle);
//変換結果の「読み」を取得
nRetVal = ImmGetCompositionString(hImcIMEHandle,
GCS_RESULTREADSTR,
cBuff, sizeof(cBuff));
cBuff[nRetVal] = '\0';
AnsiString strTemp = cBuff;
//IMEのコンテキストを開放する
nRetVal = ImmReleaseContext(Handle, hImcIMEHandle);
//cBuffに対して処理をする(入力された文字列)
}
}
でcBuffに入力された文字が入ってきていると思います。
このコードを実行するにはimm.hが必要です。
参照: [builder:6815] <その他Windows関連> <Windows> <Standard>
0165 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
DBGrid のスクロールバーを非表示に
この DBGrid のスクロールバーを力で消す方法,大変参考になりました。じっと見ているとちらりと映りますが,言われなければたぶん誰も気づかないでしょう(^^)
> private
> { Private 宣言 }
> FCellBottom : Integer;
>
> procedure TForm1.DBGrid1DrawColumnCell
> (Sender: TObject; const Rect: TRect;
> DataCol: Integer; Column: TColumn; State: TGridDrawState);
> begin
> if (FCellBottom < Rect.bottom) then FCellBottom := Rect.bottom;
> end;
>
> procedure TForm1.Query1BeforeInsert(DataSet: TDataSet);
> begin
> Abort;
> end;
>
> procedure TForm1.Query1AfterScroll(DataSet: TDataSet);
> begin
> ShowScrollBar(DBGrid1.Handle, SB_VERT, False);
> end;
>
> procedure TForm1.Button1Click(Sender: TObject);
> begin
> DBGrid1.Height := FCellBottom - DBGrid1.Top + 6;
> end;
参照: [Delphi-ML:31864] <データベース> <DataControls>
0160 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
半角カナを確定無しで直接入力させる
> IMEの制御についてお聞きしたいのですが、例えばTEditのIMEMode
> プロパティを半角カナに設定した時、「アイウエオ(半角)」と打
> ちこんだ後リターンキーで確定をしなければなりませんが、ここで
> 入力される文字(半角カナ)を随時確定していきたいのですが、どの
> ようにすればよろしいでしょうか?
[スマートな解法]
Edit1の ImeMode プロパティを imSKata にして
uses Imm;
procedure TForm1.Edit1Enter(Sender: TObject);
var
Imc: HIMC;
Conversion, Sentence: DWORD;
begin
Imc := ImmGetContext(Handle);
ImmGetConversionStatus(Imc, Conversion, Sentence);
ImmSetConversionStatus(Imc, Conversion, IME_SMODE_NONE);
ImmReleaseContext(Handle, Imc);
end;
[スマートではないがいろいろ応用が利きそう]
procedure TForm1.Edit1KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
type
TLetters = set of 'A'..'z';
const
Vowels: TLetters = ['A', 'E', 'I', 'O', 'U', 'a', 'e', 'i', 'o', 'u'];
begin
if (Key<>VK_RETURN) and (Chr(Key) in Vowels) then
begin
Keybd_event(VK_RETURN,0,0,0);
Keybd_event(VK_RETURN,0,KEYEVENTF_KEYUP,0);
end;
end;
参照: [Delphi-ML:31097] <その他Windows関連> <Windows> <Standard>
0142 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
長すぎる TreeView アイテムを補完するチップヘルプを出さなくする
TreeView からはみだすアイテムをマウスでポイントしたときに出るチップヘルプを出さないようにしたい。
const
TVS_NOTOOLTIPS = $0080; // comctl32.dll ver4.70(IE3) 以上で使用可
var
Style: DWORD;
begin
Style := GetWindowLong(TreeView1.Handle, GWL_STYLE);
Style := Style or TVS_NOTOOLTIPS;
SetWindowLong(TreeView1.Handle, GWL_STYLE, Style);
end;
参照: [Delphi-ML:24847] [Delphi-ML:24861] <Win95>
0139 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
下の図柄がすける透明パネル
実行時に透明になるパネルコンポーネントです。
unit NkTransparent;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs;
type
// 透明で 背景消去を行わない コンテナウィンドウコントロール
TNkTransparent = class(TCustomControl)
private
procedure WMEraseBkGnd(var Msg: TWMEraseBkGnd);
message WM_ERASEBKGND;
protected
procedure CreateParams(var Params: TCreateParams); override;
public
constructor Create(AOwner: TComponent); override;
end;
procedure Register;
implementation
constructor TNkTransparent.Create;
begin
inherited;
Width := 100; Height := 100;
// 上にコントロールが貼りつけられるようにする
ControlStyle := ControlStyle + [csAcceptsControls];
end;
procedure TNkTransparent.WMEraseBkGnd;
begin
// 実行時は背景消去をしない
if csDesigning in ComponentState then inherited;
end;
procedure TNkTransParent.CreateParams(var Params: TCreateParams);
begin
inherited;
// 実行時は「透明」なウィンドウ
if not (csDesigning in ComponentState) then
Params.ExStyle := Params.Exstyle + WS_EX_TRANSPARENT;
end;
procedure Register;
begin
RegisterComponents('NakCtrl', [TNkTransparent]);
end;
end.
参照: [Delphi-ML:24485] <Standard>
0137 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
API を使って縦書きなどのフォントを指定する
var
lbl :TLogFont;
begin
// ここをいろいろ変えれば縦横比を変えることができる
// (日本語の場合、幅:高さ=1:2で横倍率が100%)
lbl.lfWidth := 10;
lbl.lfHeight := 20;
// 文字の太さ(0〜1000)
// 「標準」の太さは400、「太字」の太さは700
lbl.lfWeight := 400;
// 反時計回りの角度(単位は1/10度)
lbl.lfEscapement := 2700;
// 下線なし(デフォルトでは「あり」)
lbl.lfUnderline := 0;
// 打ち消し線なし(デフォルトでは「あり」)
lbl.lfStrikeOut := 0;
// 斜体無効(デフォルトでは有効)
lbl.lfItalic := 0;
// 縦書き用の「@」がつくフォントを使用する
lbl.lfFaceName := '@MS ゴシック';
// フォントを作成
Canvas.Font.Handle := CreateFontIndirect(lbl);
// キャンバスのブラシスタイルを変えることで
// 背景を透明にして描画します
Canvas.Brush.Style := bsClear;
// 文字列を描画
Canvas.TextOut(300, 300, '縦書き文字');
end;
参照: [Delphi-ML:24027] <描画> <その他コンポーネント関連>
0135 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
表示中のポップアップメニューを消す
>プログラム上でポップアップメニューを表示するにはPopup メソッドを
>使えば出来るのですが、逆に非表示させるにはどうしたらよいのでしょう。
>よろしくお願いします。
キーボードイベントを送って非表示にする、というのはどうでしょう。
procedure TForm1.FormCreate(Sender: TObject);
begin
Timer1.Interval := 1000;
Timer1.Enabled := False;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Timer1.Enabled := True;
PopupMenu1.Popup(Button1.ClientOrigin.x,
Button1.ClientOrigin.y + Button1.Height);
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
keybd_event(VK_ESCAPE, 0, 0, 0);
keybd_event(VK_ESCAPE, 0, KEYEVENTF_KEYUP, 0);
Timer1.Enabled := False;
end;
以上のコードで実行すると、ボタンを押すとポップアップしますが、約1秒後にポップアップが非表示になります。
参照: [Delphi-ML:24269] <Standard> <メニュー>
0134 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
TSplitter をドラッグ中にヒント文字列が表示された時の不具合
TSplitter をドラッグ中に他のコントロール上にてヒントボックスを出現させると、TSpliter の境界線がその場に残ってしまいます。
TSplitter のドラッグ中は Application.ShowHint を False にしてしまうという回避方が [Delphi-ML:24019] にて紹介されています。
参照: [Delphi-ML:24019] <Additional> <バグ> <その他コンポーネント関連>
0131 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
超高速 ListView
> 推測ではありますが、Explorer等は ListView コモンコントロールが備えている
> コールバック機構を利用して、初期化処理の遅延を図っているのだと思います。
> ここでいうコールバックの機能は、画面に表示するハメになった ListItem について
> 「Caption はなんですか?」「ImageIndex は何番ですか?」「SubItem(n)は?」
> と聞いてくる、というものです。
この処理を実現しているコンポーネントが[Delphi-ML:23838]で紹介されています。
参照: [Delphi-ML:23838] <Win95>
0132 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
ステータスバーにプログレスバーを置きたい
>ステータスバーを二つのパネルに分割して一方に文字、もう一方にprogress barを
>表示したい。
動的に作って置けばできます。Panels[0]に文字列、Panels[1]にProgressBarを置くとします。
ProgressBar1 := TProgressBar.Create(StatusBar1);
try
with ProgressBar1 do begin
Top := 2;
Height := StatusBar1.Height -2 ;
Width := StatusBar1.Panels[1].Width -2;
Left := StatusBar1.Panels[0].Width +2;
Max := ***.Count;
Step := 1;
end;
for i := 0 to ***.Count -1 do begin
//何らかの処理
ProgressBar1.StepIt;
Progressbar1.Update;
end;
finally
ProgressBar.Free;
end;
参照: [Delphi-ML:23558] <Win95>
0066 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
PopupMenu に MainMenu のサブ項目をそのまま表示する
一つのメニューアイテムを、同時に二つのメニューに結び付ける事は出来ないようなので、実行時に、MainMenu と PopupMenu 、それぞれが開く時点で、共有するアイテムをそのメニューに結びつけ直す、という方法をとりました。ある位置から下を全部共有アイテムとする仕様です。
結び付け直すタイミングですが、PopupMenu の方は、OnPopup イベントで良いのですが、MainMenu の方は、(エディットメニュー以下をすべて使う場合に) Edit1 のOnClick だと、位置がずれたりしてうまくないので、WndProc をオーバーライドして、WM_INITMENU メッセージに応答させました。
以下は PASCAL 翻訳版
const COMMON_ITEM_START = 3; // 共有アイテムの始まる位置
procedure TForm1.PopupMenu1Popup(Sender: TObject);
var Item: TMenuItem;
begin
while Edit1.Count > COMMON_ITEM_START do begin
Item:= Edit1.Items[COMMON_ITEM_START];
Edit1.Remove(Item);
PopupMenu1.Items.Add(Item);
end;
end;
procedure TForm1.WndProc(var Msg: TMessage);
var Item: TMenuItem;
begin
if (Msg.Msg=WM_INITMENU) and (Msg.WParam=Menu.Handle) then begin
while PopupMenu1.Items.Count > COMMON_ITEM_START do begin
Item:= PopupMenu1.Items[COMMON_ITEM_START];
PopupMenu1.Items.Remove(Item);
Edit1.Add(Item);
end;
end;
inherited WndProc(Msg);
end;
参照: [builder:5012] <Standard> <メニュー>
0082 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
StringGrid で マウスのある Cell 内容に応じた Hint を出したい
{Application.OnShowHint イベントハンドラの設定}
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnShowHint := DoShowHint;
end;
{ヒントの表示ルーチン}
{TForm1 の Private で宣言してあります。}
procedure TForm1.DoShowHint( var HintStr: string; var CanShow: Boolean;
var HintInfo: THintInfo);
var
ACol,ARow: Integer;
ARect: TRect;
begin
{ストリンググリッドならば}
if HintInfo.HintControl = StringGrid1 then begin
with HintInfo do begin
{ヒントの色の指定}
HintColor := clAqua;
{セルの位置を取得}
StringGrid1.MouseToCell( CursorPos.x, CursorPos.Y, ACol, ARow );
{セルの範囲の取得}
ARect := StringGrid1.CellRect( ACol, ARow );
{ヒントの表示位置}
HintPos := StringGrid1.ClientToScreen( Point(ARect.Left,ARect.Bottom));
{ヒントの内容}
HintStr := 'このセルは('+IntToStr(ACol)+','+IntToStr(ARow)+')';
{ヒントの有効範囲の設定}
CursorRect := ARect;
end;
end;
end;
参照: [Delphi-ML:19686] <Additional> <その他コンポーネント関連>
0083 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
自作コントロールで IME 入力時の変換候補をキャレット位置に表示したい
IMEが編集を開始する直前にWM_IMESTARTCOMPOSITION というメッセージを送って来るので、そのメッセージを捕らえて設定してやります。
class TCustom : public TCustomControl
{
・・・・・・・・・・・・
void __fastcall IMEStart(TMessage& Message);
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER( WM_IME_STARTCOMPOSITION ,TMessage,IMEStart)
END_MESSAGE_MAP(TCustomControl)
};
void __fastcall TCustom::IMEStart(TMessage& Message)
{
// IMEの位置をキャレットのポジションに設定
COMPOSITIONFORM CompForm;
POINT pt;
LOGFONT lf;
HIMC hImc=ImmGetContext(Handle);
//キャンバスのフォントと同じに設定する
GetObject(Canvas->Font->Handle,sizeof(LOGFONT),&lf);
ImmSetCompositionFont(hImc,&lf);
//キャレットのポジションに設定する
ImmGetCompositionWindow(hImc,&CompForm);
CompForm.dwStyle=CFS_POINT;
GetCaretPos(&pt);
CompForm.ptCurrentPos=pt;
ImmSetCompositionWindow(hImc,&CompForm);
ImmReleaseContext(Handle, hImc);
// その他の処理
・・・・・・・・・・・・・
}
参照: [builder:5269] <その他Windows関連> <コンポーネント開発> <Windows> <Standard>
0084 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TDDEClientConvで最初の行しか実行されない?
TDDEClientConv を使って、ステップ実行を行うとちゃんとマクロは実行されるのですが、そのまま実行すると、最初のコマンドのみ実行してその後が実行されません。
これは2.0からのバグです。3.0/3.1用のパッチを当てると直ると思います。
http://www.dataweb.nl/~r.p.sterkenburg/bugsall.htmが詳しいです。
日本語版でも通用する個所が多いです。参考にしてみて下さい。
1)必要なったらTDDEClientConvをCreate
2)マクロを実行
3)用が済んだらFree
で逃げられます。
参照: [Delphi-ML:19657] <その他Windows関連> <System> <ShellApi> <Windows> <バグ>
0085 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TUpDown の不具合
TUpDown コントロールは OnChange/OnClick イベントがらみで、様々な問題を抱えているようです。
詳しくは、まとめとして投げられた、[Delphi-ML:20127]からたどれるスレッドを参照してください。
参照: [Delphi-ML:20127] <バグ> <Win95>
0091 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TPageControl を TabPosition=tpBottom にすると不具合
Windowsのバージョンによって、tpBottom での利用に制限がある場合があるようです。[Delphi-ML:20099]からのスレッドに、いくつか報告例があるので、参考にしてください。
参照: [Delphi-ML:20099] <バグ> <Win95>
0111 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
超高速タイマーコンポーネント(サブミリ秒)
DSPで見つけたのは「TZTimer」コンポーネントでした。
以下 Readme からの抜粋。
----------
Similar to the vcl Ttimer component, but with a resolution of 0.1ms and accurate to 0.015ms.
(略)
Resolution could be improved considerably when the RDTSC (pentium) instruction is utilized, however I had to get this timer working on a 486 also.
It was tested on D2 and D3 for W95, it should also work on D1 and W3.1.
However it will not work in Windows NT (?).
Some observed timing measurements of Tztimer, compared with the (vcl) Ttimer
:
{ evaluation of 60 runs on W95, P133, D3}
interval (vcl) Ttimer Tztimer
100 ms 108 +/- 4 ms 99.3 +/- 0.14 ms
10 ms 59 +/- 5 ms 10.1 +/- 0.07 ms
1 ms 59 +/- 6 ms (!) 1.00 +/- 0.02 ms
0.1ms Not available 0.098+/- 0.015 ms
----------
参照: [Delphi-ML:21828] <System>
0029 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
StringGridで選択セルのハイライト表示を無くしたい
>> TStringGridについてなんですが、それにFocusが当たってない場合に一つのセルの色
>> が青で塗りつぶされています。これを塗りつぶされていなくしたいのですが、やり方
>> をご存知の方、ご教授願います。
以下のような方法でできます。
// TForm1のメンバ
StringGrid1Selection:TGridRect;
// implementation
procedure TForm1.FormCreate(Sender: TObject);
begin
StringGrid1Selection:=TGridRect(Rect(-1,-1,-1,-1));
end;
procedure TForm1.StringGrid1Exit(Sender: TObject);
begin
StringGrid1Selection:=StringGrid1.Selection;
StringGrid1.Selection:=TGridRect(Rect(-1,-1,-1,-1));
end;
procedure TForm1.StringGrid1Enter(Sender: TObject);
begin
StringGrid1.Selection:=StringGrid1Selection;
end;
ただし、
TGridRect(Rect(-1,-1,-1,-1))は、ちょっと無理をしているので、
Delphi1.0や、将来のバージョンでは動かないことも考えられます。
> function GridRect(l,t,r,b:LongInt):TGridRect;
> begin
> with Result do begin
> Left:=l;
> Top:=t;
> Right:=r;
> Bottom:=b;
> end;
> end;
を作っておいて
StringGrid1.Selection:=GridRect(-1,-1,-1,-1);
とするのが正攻法です。
参照: [Delphi-ML:17189] <Additional>
0040 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
フォームの印刷時にComboBoxの内容が印刷されない
Delphi3 になって、フォームの印刷時に TComboBox の内容(テキスト)が印刷表示されなくなりました。
Delphi2 ではちゃんと印刷されます。
kohiroさんが[Delphi-ML:19070]で解法を示してくださっています。
参照: [Delphi-ML:19070] <印刷> <バグ> <フォーム> <Standard>
0048 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TrueTypeフォントからベクタ情報を得る
中村@NECさんのサンプルコードが[Delphi-ML:6963]にあります。
参照: [Delphi-ML:06963] <Windows> <その他コンポーネント関連>
0017 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TDriveComboBoxの内容の更新
ネットワークドライブの割当てをした後、TDriveComboBoxにて割当てたドライブを反映させたいのですが、方法が解りません。
DriveComboBox1.TextCase:=DriveComboBox1.TextCase;
たぶんこれが一番楽な方法だと思います。
非常に裏わざ臭いですが。。。
参照: [Delphi-ML:7162] <System> <バグ> <ファイル>
0018 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TMemoで入力*行数*を制限したい
アンドゥを犠牲にすれば、
procedure TForm1.Memo1Change(Sender: TObject);
const MaxLineCount=5;
begin
if Memo1.Lines.Count>MaxLineCount then
Memo1.Perform(EM_UNDO,0,0);
Memo1.Perform(EM_EMPTYUNDOBUFFER,0,0);
end;
というのがうまく動きそうです。
参照: [Delphi-ML:5738] <Win95> <Standard>
0021 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TListViewで列を指定してソート
以下のようにすると簡単にできます。
// ソートに使うColumnを表す。
var ListViewSort:Integer;
// Formのメンバ変数にするのがいいとおもいます。
// 0でソートしない。
// ゼロ以外ならば、Abs(ListViewSort)コラムをキーにする
// 正負で正・逆順ソート
// 汎用的に使える比較ルーチン
procedure TMainForm.ListViewCompare(Sender: TObject; Item1, Item2:
TListItem; Data: Integer; var Compare: Integer);
begin
case ListViewSort of
0: Compare:=0;
1,-1: Compare:=AnsiCompareStr(Item1.Caption,Item2.Caption);
else Compare:=AnsiCompareStr(Item1.SubItems[Abs(ListViewSort)-2],
Item2.SubItems[Abs(ListViewSort)-2]);
end;
if ListViewSort<0 then Compare:=-Compare;
end;
// コラムヘッダがクリックされたらソートしなおす。
procedure TMainForm.ListViewColumnClick(Sender: TObject; Column:
TListColumn);
begin
if Abs(ListViewSort)=Column.Index+1 then begin
ListViewSort:=-ListViewSort;
end else begin
ListViewSort:=Column.Index+1;
end;
ListView.SortType:=stNone;
ListView.SortType:=stData;
end;
参照: [Delphi-ML:11526] <Win95>
0023 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TStringGridのソート
> StringGrid上に表示したデータを、ある列(Col)をKEYとしてソートし、
> 再表示させたいのですが、どのようにしたらよいのでしょうか?
エレガントさを求めず、力わざで良ければ、
procedure TForm1.FormCreate(Sender: TObject);
var i,j:Integer;
begin
for i:=0 to StringGrid1.ColCount-1 do
for j:=0 to StringGrid1.RowCount-1 do
StringGrid1.Cells[i,j]:=IntToStr(Random(100));
end;
procedure TForm1.StringGrid1DblClick(Sender: TObject);
var c,min,i,j:Integer;
s:string;
begin
c:=StringGrid1.Col;
for i:=0 to StringGrid1.RowCount-2 do begin
min:=i;
for j:=i+1 to StringGrid1.RowCount-1 do
if StrToInt(StringGrid1.Cells[c, j ]) <
StrToInt(StringGrid1.Cells[c,min]) then
min:=j;
if min<>i then
for j:=0 to StringGrid1.ColCount-1 do begin
s:=StringGrid1.Cells[j,i];
StringGrid1.Cells[j,i]:=StringGrid1.Cells[j,min];
StringGrid1.Cells[j,min]:=s;
end;
end;
end;
としてできますが、セル数が1万を超えると速度的にチョットきつくなってきます。大きなグリッドに対してこのようなことをやりたければTListViewを使うことをお勧めします。
参照: [Delphi-ML:12259] <Additional>
0009 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
StringGrid/DBGrid でのセル編集の動作を細かく指定する
下のような関数を使って、TCustomGridで定義されているインプレースエディタを取得すれば、
GetGridEditor(StringGrid1).SelStart:=0;
などとできます。
function GetGridEditor(Grid: TCustomGrid): TCustomEdit;
var i: Integer;
begin
Result:=nil;
for i:=0 to Grid.ControlCount-1 do
if Grid.Controls[i] is TCustomEdit then begin
Result:=TCustomEdit(Grid.Controls[i]);
Exit;
end;
end;
参照: [Delphi-ML:18726] <データベース> <Additional> <DataControls>
[新規作成] [最新の情報に更新]
How To
Lounge
KeyWords
Osamu Takeuchi osamu@big.or.jp
Tips
Delphi
Home