教えてください。困ってます。
カスタムコンポを作って、コードにGetDC(Handle);を入れるとフォームに配置する際、「コントロール・・・は親ウィンドウを持っていません」とメッセージが出てフォームにコントロールが配置されません。
GetDCをどこに書いても症状はおなじです。ただし、先にコンポを配置しておいて、あとからコンポのソースにGetDCを書くとちゃんと動作します。
それなりに色々試しましたがどうしようもありません。どうぞ教えてください。お願いします。
よくありますね、それ。困ったものです。
CreateWndメソッドをオーバーライドして、そこでGetDCすると、わりとうまくいくようです。
ただ、Createと違って、必ず"最初にしか発生しない"というわけではないです。
それから、ReleaseDCはDestroyWndに、ウィンドウを作り直す(メモ型コントロールのオプションを切り替えるときなど)必要があるときは、
RecreateWndを使います。
どういうところでGetDCしていますか?
Paintをオーバーライドして、その中で使っていますか?
HandleAllocated関数で、ウィンドウハンドルが存在するか確認してからGetDCしてみるとか。
たいてい、うまく行かないのはロジックの間違いです。
CreateWndをオーバーライドしましたがうまくいかず、functionでGetDCを使って必要最小限度にしましたが、それでもうまくいかず、いっそのことGetDCを外すとこれまたうまくいかず、また一からGetDCのないコードを書き直すとようやくうまくいきました。
それでロジックの間違い、ではたと気づきGetDCでエラーの出たコードをドンドンコメントアウトしていくと、if文で条件が2箇所あるところで出ていることがわかりました。具体的には
procedure TMyMemo.Change;
var
i: Integer;
begin
if (FMaxLines > 0) and (Lines.Count > MaxLines) then
{ここに処理};
end;
{ここに処理}には何を書いてもエラーが出ます。
親ウィンドウを割り当てればそれでうまくいくのかなぁと思っていましたがそういうものでもないのでしょうか?(ちゃんとした割り当て方もわかっていませんが・・・)
そういう系統のエラーは大体、エラーメッセージでおおよその見当はつくんですけど、
・コントロールXXXは親ウィンドウを持っていません。
−原因
親として参照しようとしているウィンドウは、まだ存在しない。
−対処法
parentプロパティに何か入れてみる
できるのなら、ウィンドウハンドルの作成と同時に、inheritedのあとで処理を行う
・アプリケーション名のXXXXで、読み込み違反
−原因
参照しようとしたコンポーネントが、実はnil
−対処法
ちゃんとCreateが成功しているか、間違えてその前に開放してないか確認。
nilのものにfreeを使おうとしてもエラーになるので、怪しいものはAssignedで調べてから。
そのへんを調べてみてはどうでしょうか?
それと、Changeイベントはどうやら、コントロールが実際に使えるようになる前にも、一度通っているようです。
そうだとすると、 もしかしたらLinesが使えないということもありえますね。
ところで、
> CreateWndをオーバーライドしましたがうまくいかず
とのことですけど、ちゃんとinheritedのあとでGetDCをしましたよね?
"あたりまえジャン!"だったら、悪いですけど。
ところで
> procedure TMyMemo.Change;
TMemoの拡張ですか?なら、これが参考になるかも。
http://www.geocities.co.jp/SiliconValley-PaloAlto/8945/tips/index2_14.html
描画時に、デバイスコンテキストの取得・解放をするようにしてみてはどうですか?
> if (FMaxLines > 0) and (Lines.Count > MaxLines) then
ここでエラーが出たと言うことは、おそらくLines.Countが原因です。
Lines.CountのLinesは、TMemoStringsで、そのCreateがされていないのだと思います。
Createは、
constructor TCustomMemo.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Width := 185;
Height := 89;
AutoSize := False;
FWordWrap := True;
FWantReturns := True;
FLines := TMemoStrings.Create;
TMemoStrings(FLines).Memo := Self;
end;
と、TCustomMemoでしていて、Selfを入れているし、TMemoStrings.GetCountでは、
Memoに大してHandleAllocatedを調べ、存在すればメッセージを送っています。
カスタムコンポーネントのCreateは、ちゃんと
procedure TMyMemo.Create(AOwner: TComponent); override;
begin
inherited Create(AOwner);
・・・から始まっていますか?
結構、inherited;と間違えてかく場合があります。引数を指定しないと、TObjectのCreateしか呼べません。
失礼。
procedure TMyMemo.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
・・・でした。
override;は、宣言時に必要なだけです。実体にあるとエラーですね^^;
色々なアドバイス本当にありがとうございます。
「親ウィンドウがない」ということなので親ウィンドウを割り当てようとしたことに無理があったみたいですね。
結局、エラーの出るところに
if Parent <> nil then
を入れるとうまく動きました。
なんとなく解決策としてはまずいような気もしているのですが・・・
ファイルが見つからないとのことなので。
http://www.geocities.co.jp/SiliconValley-PaloAlto/8945/delphi/tip1.html
でも解決したのならいいかぁ。
いえいえ、ファイルは見つかりました。そして参考にしています。
ただ、MyMemo=Class(TMemo)に
Canvas: TControlCanvas;
して、
constructor TMyMemo.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Canvas := TControlCanvas.Create;
end;
procedure TMyMemo.CreateWnd;
var
DC: HDC;
begin
inherited CreateWnd;
DC := GetDC(Handle);
Canvas.Handle := GetDC(DC);
{ 処理 }
end;
やってみましたが、OSの再起動後にエラーが出たのです。今から思えば他のコードロジックに問題があったのかもしれませんが、現在は、
procedure TMyMemo.{ 処理名 };
var
DC: HDC;
begin
if Parent <> nil then
begin
DC := GetDC(Handle);
Canvas.Handle := DC;
{ Canvasに対する描画処理 }
Canvas.FreeHandle;
ReleaseDC(Handle, DC);
end;
end;
としています。
「解決」としましたが、解決したようなしてないような不安な気持ちはどうしてなんでしょう?
とにかく、色々勉強になりましたし、ありがとうございました。
TControlCanvasは、文字通り「コントロールのキャンバス」です。
Canvas.Handleを使うのなら、TCanvasで十分です。
TControlCanvasを使うなら、Controlプロパティに自分自身を入れればOKです。
# もしかしてControlがnilだから親ウィンドウがないと言われていたり…?推測ですが。
もともとはTCanvasだったのを、usesにGraphicsを書かなくてよい、という理由だけでTControlCanvasにしただけです。
結果は同じでしたので・・・
すでに解決済になっていますが、
inherited Create(AOwner);
Parent := AOwner as TWinControl;
FCanvas := TControlCanvas.Create; //←この1行
再現しましたのでその回避コードです。
#ちなみに、上記サイトのコードはDel3で作成したコードです。
#まさかDel6で動かないとは (^^;;
>inherited Create(AOwner);
>Parent := AOwner as TWinControl;
>FCanvas := TControlCanvas.Create; //←この1行
間違えた(^^;
inherited Create(AOwner);
Parent := AOwner as TWinControl;//←この1行を追加
FCanvas := TControlCanvas.Create;
ずいぶん前の件ですが、うまくいった例としてPaintのオーバーライドを紹介します。
procedure TMyCtrl.Paint;
begin
inherited;
case Tag of
0: begin
Child.Color := clCream;
self.Font.Size := 12;
Tag := 1;
end;
end;
end;
つまり、初めてのPaint呼び出し後には、親ウィンドウができていると言うことでしょうか?
ツイート | ![]() |