https://www.petitmonte.com/bbs/answers?question_id=7657
に続きたびたびすいません。
またつまづいてしまいました。
Panelの上にImageがあり、Panel自体に画像が貼り付けられたりするとImageで描画したものがPanelの画像の下になってしまいます。
Imageで描画したものは常にTopにあってほしいのですが、BringToFrontでは意味がなく…
何か方法ありますか?
>Panelの上にImageがあり
ImageはPanelの子になってますか?
ImageはPanelの子です。
Panelの子にしないと、Image自体がPanelの上にないのでイベントがひろえないのです。
ImageがPanelの子でなくてもMouseDown等のイベントひろえますか?
いえ、Panelの子にすれば解決、と思っただけですが、だめですか...
もしかして、
>Panel自体に画像が貼り付けられたりすると
っていうのは、PanelのCanvasに描画していると思い込んでましたが、
実際はどうしているのでしょう。
失礼しました。PanelにCanvasなんてないですね。
安易な手段としては、SetWindowPosでしょうか。
例えば、
SetWindowPos(Image1.Handle, HWND_TOP, 0, 0, 0, 0,
SWP_NOMOVE or SWP_NOSIZE or SWP_SHOWWINDOW);
もしかしたら、オプションフラグは変更が必要かもしれません。
Novさんどうもです。
SetWindowPos(Image1.Handle, HWND_TOP, 0, 0, 0, 0,
SWP_NOMOVE or SWP_NOSIZE or SWP_SHOWWINDOW);
Image1はHandleないですよね、
Image1.Canvas.Handleでやってみましたが、思い通りにはいきませんでした。
Panelに何も画像がない状態なら問題ありませんが。
再投稿すいません。
うーん。
ImageがPanelの子だからだめそうです。
最初にも書きましたが、ImageだとPanelの子にならないとPanelの後ろにかくれてしまい、イベントをひろえません。
Imageでなくてもいいので、
Panelの子にならずにPanel上で描画できる方法あれば
と思います。
>Image1はHandleないですよね、
ですね、済みませんでした。
>Image1.Canvas.Handleでやってみましたが
このハンドルはデバイスコンテキストですので、ウィンドウ操作には使えません。
これ以上おバカなコメントをしないように、自分で試してから出直しますので、ご容赦を。
試そうと思ってんですが、いきなりつまづきました。
>Panel自体に画像が貼り付けられたりすると
ってどういうことですか。この画像はTImageではないんですよね?
もし、PanelにCanvasを割り当てているだけなら、
Panelの画像の描画の後に、Image.ReFreshをいれれば
Imageが再描画されます。
Novさん、説明不足ですいません。
Panelのハンドルを取得して、別アプリでビットマップ画像を常時貼り付けています。
そのため、一度Panel上にImageのイベントで描画してもすぐ画像にかくれてしまうわけです。
すいません、大事なとこでした。
色々試していますが、これ厳しそうですね
>Panelのハンドルを取得して、別アプリでビットマップ画像を常時貼り付けています。
が、デバイスコンテキストのハンドルを指しているなら、
(ウィンドウハンドルを指している場合はだめですが)
メインフォームの定義の前に
TPanel = class(Vcl.ExtCtrls.TPanel) // XE2の場合
// TPanel = class(TPanel) // XE2以外の場合
protected
procedure WndProc(var AMsg: TMessage); override;
end;
を入れて、
procedure TPanel.WndProc(var AMsg: TMessage);
begin
inherited;
if AMsg.Msg=WM_PAINT then Form1.Image1.Refresh;
end;
でどうでしょうか。
Novさん
ありがとうございます。
試しましたがだめでした。
この場合
> TPanel = class(TPanel)
ではなく
TMyPanel = class(TPanel)
としてTPanelを動的に生成する方法ですかね?
と認識してためしましたが…
違うアプローチで考えた方がいいのでしょうかね。
少し説明が足りなかったかもしれません。
まず、
>Panelのハンドル
これは、デバイスコンテキストのハンドルと考えてよいですか?
>としてTPanelを動的に生成する方法ですかね?
単に、現在のフォームの既存のTPanelのメッセージ処理を追加する
ためのものなので、クラス名は変更しないでお試しください。
具体的には、TPanel描画の最後に、Imageを再描画させようとしています。
>違うアプローチで考えた方がいいのでしょうかね。
この方法にこだわる必要はないです。
(状況がつかめていないので、うまくいく保証もできないので)
念のため、テストコードを載せておきます。
Panel1をクリックすると、画像をファイルから読み込んで、Panel1に表示します。Image1には、あらかじめ画像をプリロードしておきます。
bmpにロードする画像ファイル名は任意のものに差し替えてください。
type
TPanel = class(Vcl.ExtCtrls.TPanel)
protected
procedure WndProc(var AMsg: TMessage); override;
end;
TForm1 = class(TForm)
Panel1: TPanel;
Image1: TImage;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Panel1Click(Sender: TObject);
private
{ Private 宣言 }
dc: HDC;
cv: TCanvas;
bmp: TBitmap;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TPanel.WndProc(var AMsg: TMessage);
begin
inherited;
if AMsg.Msg=WM_PAINT then begin
Form1.cv.StretchDraw(Rect(0, 0, Width, Height), Form1.bmp);
Form1.Image1.Refresh;
end;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
cv.Free;
ReleaseDC(Panel1.Handle, dc);
bmp.Free;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
bmp := TBitmap.Create;
dc := GetDC(Panel1.Handle);
cv := TCanvas.Create;
cv.Handle := dc;
end;
procedure TForm1.Panel1Click(Sender: TObject);
begin
if bmp.Empty then bmp.LoadFromFile('..\..\hoge.bmp');
Panel1.Refresh;
end;
サンプルありがとうございます。
ですが、、
TPanel = class(TPanel) // XE2以外の場合
protected
procedure WndProc(var AMsg: TMessage); override;
end;
まずこれがコンパイル通らないため、私の方でTPanelを変更した形で試したという経緯です。
'TPanel'型の宣言が完了していません
とでます。
うまくいったら、の話ですが、WM_PAINTしかいじってないので、
>procedure WndProc(var AMsg: TMessage); override;
よりも
procedure EndOfPaint(var AMsg: TMessage); message WM_PAINT;
とかにした方が良いかも(条件文が削れるし)です。
すいません、やはり
→ TPanel = class(TPanel)
の「TPanel」そのままでいける方法を私は知りません。
TPanelA = class(TPanel)
とかにせず、
いけるものですか?
TPanelの宣言はXE2じゃないなら下記ですね
TPanel = class(ExtCtrls.TPanel)
auさんフォローありがとうございます。
スコープは大事なんですね。
ありがとうございます。
> これは、デバイスコンテキストのハンドルと考えてよいですか?
そうです。
サンプル試しました。
ごめんなさい。結果がどうなればよいサンプルでしょうか?
私が試したのは、Image1にあらかじめ画像をプリロードしてあり、PanelクリックでBMPを読み込みます。
すると、Imageの下にBMPがきます。
ということが確認できるようですか?
ただ結果Imageに描画するとBMPが消えてしまうし、
> Panelのハンドルを取得して、別アプリでビットマップ画像を常時貼り付けをためすとPanelの画像が上に来ます。
私の方で納得できていなかったらすいません。
すいません、読みづらかったので。。。
誤
> Panelのハンドルを取得して、別アプリでビットマップ画像を常時貼り付けをためすとPanelの画像が上に来ます。
正
> Panelのハンドルを取得して、別アプリでビットマップ画像を常時貼り付け
これをためすとPanelの画像が上に来ます。
こんにちは,Mr.XRAYです.
途中からで,よくスレッドを読んではいなのですが,最初の質問文から判断して,
(1) フォームにTPanelを配置
(2) その上にTImageを配置,このTImageはPanelよりも小さいサイズ
(3) TImageにビットマップを描画する
(4) TPanelに何らかの方法で,ビットマップを描画する
(5) TPanelに描画した画像は全て見えるが,TImageの画像が見えなくなってしまう
(6) TImageの画像を前面に表示したい
ということですよね?
TPanelは,TGraphicControlの派生コンポではありません.
したがって,他のウィンドウが重なると,重なった部分は消えてしまいます.
>Panelのハンドルを取得して、別アプリでビットマップ画像を常時貼り付けています。
(プロセス越えの動作ですね.かなりの高級技です)
(一体,どういう仕様のアプリなんだろう?)
ということですので,それでいいんですよね?
一番簡単なのは,Panelに直接描画するのではなく,Panelの上に,
AlignをalClientにしたTImageを載せ,そこに描画することではないかと思います.
そうすれば,BringFront等も使用可能です.
どうしてもPanelに描画していのであれば,そのPanelへの描画の終了を検出して,
TImage.Refresh; を実行することではないかと思います.
他のアプリからの実際の描画方法が分かりませんので,このぐらいしか言えません.
以下のコードは,自アプリの場合の処理の1つの方法です.
動作確認環境は,Windows XP(SP3), Windows 7 U64(SP1) + Delphi XE です.
implementation
{$R *.dfm}
type
TMyPanel = class(TPanel); // Protected部へのアクセス用
//=================================================================
// Image1にビットマップ画像を描画する
// Panel1は,Image1の親で,サイズはPanel1より小さい
//=================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
Image1.Picture.LoadFromFile('TestbmpImage.bmp');
end;
//=================================================================
// パネルにビットマップ画像を描画する
// TPanelは,TPaintBox同様,ウィンドウが上に重なると描画は消える
//=================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
Bmp : TBitmap;
begin
Bmp := TBitmap.Create;
try
Bmp.LoadFromFile('Neko.bmp');
TMyPanel(Panel1).Canvas.StretchDraw(Panel1.ClientRect, Bmp);
//これがないと,描画が更新されない場合がある
//Image1.Invalidate;でも可
Image1.Refresh;
finally
Bmp.Free;
end;
end;
//=================================================================
// 描画をクリア
//=================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
Image1.Picture := nil;
Panel1.Refresh;
end;
Panelに描画した画像が,「見掛け上」上に表示されてしまうのは,
TWinControlとTGraphicControlを混在して,画像表示に使用しているためです.
どちにか一方だけであれば,この現象は発生しません.
ちょっとだけですが、
他のアプリってのが曲者っぽいですね。
多分DirectX使ったフルスクリーンアプリだったりしませんか?
いずれにせよ、Windowsの描画方式は一種類だけではありません。
そのへんから調べないとキツイっすよ。
>ただ結果Imageに描画するとBMPが消えてしまうし、
もしかして、Imageの背景としてBMP画像を残したままにしたいということなのでしょうか。
だとすると、Imageは不要で、こんな感じ?
procedure TPanel.WndProc(var AMsg: TMessage);
begin
inherited;
if AMsg.Msg=WM_PAINT then begin
// 外部アプリのbmp書き込みの代わり
Form1.cv.StretchDraw(Rect(0, 0, Width, Height), Form1.bmp);
// ここに描画処理
cv.Brush.Style := bsClear;
cv.Pen.Color := clRed;
cv.Rectangle(50, 50, 100, 100);
end;
end;
テストでは描画できますが、実際にうまくいくかは、別アプリの'常時貼り付け'の方法とタイミングによります。
>他のアプリってのが曲者っぽいですね。
ですね.実際,他のアプリへは,デバイスコンテキストを取得すれば可能ですが,
恒久的というわけには行きません.
Delphiで言えば,TForm, TPanel や TPaintBox 等,TWinControl への描画と同じで,
常に再描画が必要となります.
したがつて,
>もしかして、Imageの背景としてBMP画像を残したままにしたいということなのでしょうか。
等と予想するしかないですね.
どのようなアプリで,どのような動作仕様にするかを考える必要がありますから.
難しいところです.
今見直したら,失礼しました.
>AlignをalClientにしたTImageを載せ,そこに描画することではないかと思います.
これは出来ないですね.
他のアプリから,TImageには描画できません.ハンドルがないですから.
procedure TPanel.WndProc(var AMsg: TMessage);
begin
inherited;
if AMsg.Msg=WM_PAINT then Form1.Image1.Refresh;
end;
これループするだろ
>これループするだろ
あ、ごめんなさい。気づきませんでした。
(キューが違うから意識してませんでした)
>テストでは描画できますが、実際にうまくいくかは、別アプリの'常時貼り付け'の方法とタイミングによります。
これ、たぶん駄目です。
(画像描画が、背景消去と描画の間にうまく割り込むタイミングでない限り)
>Panelのハンドルを取得して、別アプリでビットマップ画像を常時貼り付けています。
このアプリはなに?
みなさん、ありがとうございます。
結局ほかアプリはデバイスコンテキストを取得して、Panelに貼り付けを行っているものです。
貼り付け画像は常時変化しますので、タイミングとしてはその変化のあとPanelに画像をのこしたまま描画し、今度は描画をのこしたまま、画像を変化させる。
といった荒業? になってしまいます。
→ここのアドバイスと合わせ、自分が試した結果そういったことは厳しいと判断しました。
すいません、当初できないだろうという思いはあったのですが、
みなさんならどういった切り抜け方をするのか、また自分が知らないアドバイス等もほしかったので質問しました。
結局自分としては、画像の上に描画する際は画像の更新は必要なかったので描画の際は更新を止めるようにしました。
色々勉強になりました。
ツイート |