Delphi Tips
>> Index
● 05/17 TIdentToInt型とTIntToIdent型
● 09/11 エディットコントロールにポップアップウィンドウをつけたい
● 09/06 ButtonのCaptionで改行を使って文字を複数段で表示したい
● 09/06 エディットコントロールにコンボボックスのようなボタンをつけたい
● 08/14 カスタムコントロールの子コントロールをオブジェクトインスペクタに表示させない
● 07/14 基本的な階層プロパティ定義の例
● 02/08 自作コントロールで IME 入力時の変換候補をキャレット位置に表示したい
● 02/08 Delphi1/2で状況依存型のコンポーネントヘルプを作るときの注意
● 02/08 親の published プロパティを子クラスで隠蔽する
● 02/08 oDelphi1.0とDelphi2.0/3.0でコンポーネントのソースを共有したい
● 02/08 complib.dllが壊れた!また全部のコンポーネントをインストールするの?!
最終更新: 8224 日前
0323 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 編集
TIdentToInt型とTIntToIdent型
IdentToColor は TIdentToInt 型の変換関数で、TIntToIdent型 とともに、整数型のプロパティを見やすくするために登録される関数です。
TColorのような特定の整数型に対し、TIdentToInt 型の関数とTIntToIdent型の関数を RegisterIntegerConsts で登録しておくとコンポーネントの整数型プロパティの特定の値を見やすい文字表現でオブジェクトインスペクタに表示することができます。
また、dfm ファイルも読みやすくなります。
また、実行時に任意の整数型に対して
function FindIntToIdent(AIntegerType: Pointer): TIntToIdent;
function FindIdentToInt(AIntegerType: Pointer): TIdentToInt;
で変換関数が存在するか調べることもでき、整数を見やすい文字表現に変換したり、その逆も可能です。
参照: [Delphi-ML:66991]
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>
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>
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>
0012 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 編集
カスタムコントロールの子コントロールをオブジェクトインスペクタに表示させない
オブジェクトインスペクタには、OwnerがFormであるコントロールだけが表示されるので、カスタムコントロールの内部で作成するコンポーネントならば、OwnerにSelfを与えればよいです。
サンプルコンポーネントのTSpinEditなどを参照しましょう。
参照: [Delphi-ML:5112]
0200 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/07/14 久保田 宏光 rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/07/14 久保田 宏光 編集
基本的な階層プロパティ定義の例
// 子にするクラスの宣言
// ・TPersistentから継承する
// ・プロパティ内に、さらに他のクラスを定義する場合はAssignでコピーする。
// 直接代入を許さないように留意する(SetListメソッドを参照)
// メソッドをprotectedにする場合はvirtualにする。
// また、隠蔽したい場合はprivateに置く。
// ※protected/privateが良く分からない場合はprotected/virtualが無難
// ・クラスにAssignを実装する必要がある。
type
TChild = class(TPersistent)
private
FList:TStrings;
public
constructor Create;
destructor Destroy; override;
procedure Assign(Source:TPersistent); override ;
protected
procedure SetList(Value:TStrings); virtual;
published
property List :TStrings read FList write SetList;
end;
//親側のクラス宣言
//・こちらでも、Childプロパティの書き込みにSetChildを実装する。
type
TParent = class(TComponent)
Private
FChild:TChild;
public
constructor Create(AOwner:TComponent); override;
destructor Destroy; override;
protected
procedure SetChild(Value:TChild); override;
published
property Child :TChild read FChild write SetChild;
end;
//実現部(子)
//・抽象クラスで宣言したプロパティは、こちらで実装クラスとして
// create/freeする。
//・プロパティのwriteはメソッドになっている。間違っても
// List := TStringList.Create
// ~~~~ などとしないように。
//・コンポーネントがTStrings内でObjectsを使用している場合のみ、
// free時に解放する。
//
// 例 )
// for i:=0 to FList.count - 1 to
// FList.Objects[i].Free;
// FList.Free;
constructor TChild.Create;
begin
inherited Create;
FList := TStringList.Create;
end;
destructor TChild.Destroy;
begin
FList.Free;
inherited Destroy;
end;
procedure TChildSetList(Value:TStrings);
begin
if Value <> FList then
FList.Assign(Value);
end;
Procedure TChild.Assign(Source:TPersistent);
begin
if Source is TChild then
FList.Assign(TChild(Source).List);
inherited Assign(Source);
end;
//実現部(親)
//・FChildをcreate/freeするのを忘れないように。
constructor THyperText.Create(AOwner:TComponent);
begin
inherited Create(AOWner);
FChild := TChild.Create;
end;
destructor THyperText.Destroy;
begin
FChild.Free;
inherited Destroy;
end;
参照: <PASCAL>
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>
0087 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 編集
Delphi1/2で状況依存型のコンポーネントヘルプを作るときの注意
[delphi-cw 127]より。
マニュアルによれば、ヘルプトピックに、決められた形式の B 脚注を入れれば良いことになっているのですが、この時、脚注の書式に気を付けないといけません。
#{\footnote hlp_TNkDIB}
${\footnote TNkDIB}
K{\footnote TNkDIB;NkDIB;DIB}
B{\footnote class_TNkDIB}
などとする変わりに、
#{\footnote # hlp_TNkDIB}
${\footnote $ TNkDIB}
K{\footnote K TNkDIB;NkDIB;DIB}
B{\footnote B class_TNkDIB}
のようにしないと、kwgen.exe がキーワードの読み取りに失敗してしまいます。
参照: <開発環境> <バグ> <ヘルプ>
0102 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 編集
親の published プロパティを子クラスで隠蔽する
Object Pascal では、クラスの継承時に親クラスで指定されたアクセス指定子よりも厳しいアクセス指定子をメンバに指定しても無視されてしまいます。従って、親クラスで public に指定されたメンバを子クラスで protected にするようなことは出来ません。
これは、published も同じで、一度公開されたものを元に戻すことはできないのですが、単に「オブジェクト・インスペクタに表示させない」だけならば、「読み込み専用のプロパティ」として再宣言することで、オブジェクトインスペクタに表示しないようにすることができます。
例:AutoScrollプロパティを読み込み専用にする。
THogeScrollBox = class(TScrollBox)
private
function GetAutoScroll: Boolean;
published
property AutoScroll: Boolean read GetAutoScroll;
end;
function THogeScrollBox.GetAutoScroll: Boolean;
begin
Result := inherited AutoScroll;
end;
※当然、イベントも同じ方法で隠すことが出来ます。
参照: [Delphi-ML:21207]
0015 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 編集
oDelphi1.0とDelphi2.0/3.0でコンポーネントのソースを共有したい
普通にやると、*.dcr のフォーマットが違うのと、*.dcu の形式が違うという2つの問題が生じます。
この前者を解決する方法が[Delphi-ML:6212]に説明されています。
味噌となるのは、コンポーネントを含むユニットと、コンポーネントを登録するユニット( RegisterComponentsを行うRegister手続きを含むユニット )を分離して記述することです。
実はこれは、VCL自身が採用しているやり方です。
後者については、手ですべての *.dcu ファイルを削除してやるしか方法を思い付きません。
参照: [Delphi-ML:6212] <開発環境>
0014 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 編集
complib.dllが壊れた!また全部のコンポーネントをインストールするの?!
以下のようなファイルを一つ作っておくと、コンポーネントの再インストールの手間がかなり省けます。
この方法はDelphi1.0/2.0のどちらでも使えます。
----------------------------------------------------
unit MyReg;
interface
{インストールしたいコンポーネント}
{ユニットを列挙する }
uses
Stdreg,
Sysreg,
Sampreg,
Ddereg;
{上記のコンポーネントユニットに }
{対応するリソースファイルがあれば}
{インクルード?する }
{$R D:\DELPHI\LIB\STDREG.DCR}
{$R D:\DELPHI\LIB\SYSREG.DCR}
{$R D:\DELPHI\LIB\SAMPREG.DCR}
{$R D:\DELPHI\LIB\DDEREG.DCR}
procedure Register;
implementation
{個々のユニットのRegister手続きを}
{呼び出す }
procedure Register;
begin
Stdreg.Register;
Sysreg.Register;
Sampreg.Register;
Ddereg.Register;
end;
end.
----------------------------------------------------
他のコンポーネントファイルをすべてアンインストールして、あるいは、まったく新しくライブラリを作って、このMyRegのみをインストールすると、中で呼び出した全てのコンポーネントユニットをすべてインストールしたのと同じ効果が得られます。
御自分で作った/ダウンロードしたユニットもここに加えれば、最インストールの手間はほとんどなくなります。
ただし、個々のコンポーネントがライブラリパス以外に含まれている場合には手動でライブラリパスを入力しなければならなくなるので、MyReg.Pas のようなユニットは、中で参照するユニットと同じディレクトリに置いておくのが良いでしょう。
御参考までに。
参照: [Delphi-ML:7402] <開発環境>
[新規作成] [最新の情報に更新]
How To
Lounge
KeyWords
Osamu Takeuchi osamu@big.or.jp
Tips
Delphi
Home