Delphi 11.2 Alexandria has arrived!

「アレキサンドリアがやってきた!」

2022年9月17日(土)早朝、てか、普通のヒト的には深夜、MyPCにDelphi 11.2 Alexandriaがやってきた。わぁーい*(^_^)*♪ インストールに時間がかかりそうだから、土曜日を待ってたんだ。きゃっほー♪ isoファイルをDLして、マウントして、インストーラを起動。しばらく待って無事インストール完了。それから、My Secret Weapon、大好きなPython4Delphiも入れて、今、作ってるプログラムを読み込んで実行したら・・・。

あれー? バルーンヒントが指定したVCLじゃなくて、マウスのポインタ位置に表示されるんだけど・・・。ふーん、今度からそうなったんだ。Delphi すごーい。でも、なんでー? みたいな・・・ T_T

1.11.2でバルーンヒントが大変なコトに
2.VCLの位置をTPointでGet!
3.まとめ
4.お願いとお断り

1.11.2でバルーンヒントが大変なコトに

MyPCだけで起きていることカモしれないけど、Delphi 11.2 Alexandriaをインストールして、以前のバージョンで作ったプログラムを読み込んで実行したら、バルーンヒントの表示される位置が・・・、んー。設定と・・・かなり「違う」。みたいな・・・

早速、検証用プログラムを作って、動作確認。

Button3をクリックしたら・・・ の手続きの中で、
(※注意:バルーンヒントにアイコンを表示する方法は、この下で解説)

procedure TForm1.Button3Click(Sender: TObject);
begin
  //バルーンヒントを表示
  BalloonHint1.Title := 'ヒント';
  BalloonHint1.Description := 'ここをクリックしてください';
  BalloonHint1.HideAfter := 12000; //表示時間(単位:ms)
  BalloonHint1.ShowHint(button2.ClientToScreen(CenterPoint(button2.ClientRect)));
  //案内アイコンも追加
  BalloonHint1.ImageIndex := 0;
end;

バルーンヒントを表示するのは、「button2」の真ん中だよって、ちゃんと指定してるのに・・・

なぜか Button2ではなく、マウスポインタ位置にバルーンヒントが表示・・・される

これでは役に立たないけれど、案内アイコンを付けてバルーンヒントを表示する方法をいちおうメモ(11.2より前のバージョンのDelphiなら、期待通りに動くはず)。

(1)FormにImageList1を置いて、HeightプロパティとWidthプロパティ両方に「32」を設定。

ImageList1のHeightプロパティとWidthプロパティ両方に「32」を設定。

(2)BalloonHint1のImagesプロパティにImageList1を指定。

BalloonHint1のImagesプロパティにImageList1を指定。

(3)IconExplorerをDLして、インストール。

Icon Explorer

https://www.mitec.cz/iconex.html

(4)IconExplorerを起動し、c:\Windows\System32\Shell32.dllをクリックするとアイコン一覧が表示されるので、その中から目的のIconを探して、以下のように操作。

c:\Windows\のSystem32フォルダをクリック
Shell32.dllをクリック
目的のアイコンをさがしてクリック
32×32を右クリック

で、表示されるサブメニューから、「Save to Bitmap」を選択し、任意のフォルダに保存する(PNGだと背景が透明になる・・・。Jpegは試していない)。

(5)TImageListをダブルクリックして表示されるWindowの「追加」をクリックして、上で任意のフォルダに保存したInfoアイコンを選択して「OK」をクリックする。

「追加」をクリックして、上で任意のフォルダに保存したInfoアイコンを選択してOKをクリック

(6)上で紹介したコードを記述して実行すれば、11.2より前のバージョンのDelphiなら期待した通りに動作するはず。バルーンヒントが表示される位置が、目的のVCLコントロールの上だったり、下だったり、その表示位置を自由に制御できないのがもどかしかったり、ヒントの色が背景と同じで、実際に使ってみると思ったほどヒントが目立たなかったり・・・ みたいな不満は、正直ずっとあったけど。少なくても「そこに出せ!」とコードで指示したVCLを無視するようなことだけはなかった・・・。11.2より前のバージョンのDelphiなら・・・

でも、もう前のバージョンには戻せない。

何回コンパイルしても、頑なまでに、指示を無視する11.2。
生まれたてなのに、イイ根性してます・・・。

でもね。

Delphiを心から信じ、愛している人間は、きっとこう思うはずなんですよ。

これは11.2で「バルーンヒントの表示位置は、マウスポインタがアクティブな場合、プログラム内容よりポインタの現在位置を優先する」仕様へとDelphiが進化したため・・・。

一瞬、そう思いたくもなったのですが。次の瞬間、

こんなプログラム。フツーのヒトは、
壊れてるとしか思わねーだろ!

・・・という声が聞こえ(た気がする)、僕は自分を取り戻した次第です。

そう言えば、ある冬の寒い朝、これと似た出来事がありました。

ハナが冷たくて目が覚めた僕は、
となりでまどろんでる彼女に、小さな声でききました。

『ねぇ 今日もさむいー?』

想像を絶する大音量で、返事が。

冬だから寒いに決まってんだろ!

おまけに、

冬をなめとんのか? オマエは

はい。すみません。

ですが、そこまで言わなくても・・・。
クー。クー。眠ってたはずなのに。もしかして、寝言?

こんな、違うだろ・・・みたいな出来事は、たくさんあって、僕は彼女が大好き。

パスタが大好きな僕ですが、ある晩、無茶苦茶美味しいパスタを彼女が作ってくれて・・・。ほんとに美味しかったから、翌朝、夢で味わったようなパスタを思い出して

『ねぇ まだおかわり、あるー?』って、やっぱり夢の中にいる彼女にきいたら、

ヨーシ、髪の毛で増量!

この人と結婚してよかったぁ☆

彼女とのことは、これでよくても、プログラムは、良くないです。
もし、本当に仕様変更であったにしても、この設定は受け入れられません。

で、Google先生に、どうしたらイイかを、いっぱい訊ねて得た僕なりの結論は・・・

現段階で、どうしてもバルーンヒントを表示したい。・・・なら
自前で作ったバルーンヒントを表示するしかない(したい)。

VCLコントロールのHintプロパティに「言い訳」的に何かを入力して、ShowHintプロパティをTrueに設定。で、実行時、マウスポインタがそのVCLコントロールをポイントしたら、操作方法のヒントを表示するみたいな「控えめ」なユーザーへの案内でなく、何かVCLをクリックしたら、プログラムを初めて使うユーザーにも「こっちだよー!」と手招きするような案内を、僕は表示したくて・・・。

普通のヒントでなく、バルーンヒントを表示させたいだけなら、こちらのWebサイトで紹介されていた方法もあるけど。

Delphi2010 バルーンヒント(BalloonHint)

http://afsoft.jp/program/del2010/p11_047.html

Mr.XRAYさんのWebサイトに完璧な答えが掲載されていました。
以下、その記事を引用して書いたプログラムです。

06_バルーンヒントウィンドウを自作

http://mrxray.on.coocan.jp/Delphi/Others/BalloonHintWindow.htm

上記サイトからDLできるplBalloonHint.pasをdprojファイルがあるのと同じフォルダに入れて、usesに次のように記述。

implementation

uses
  plBalloonHint;

{$R *.dfm}

Button1Click手続きに、以下のコードを記述。

procedure TForm1.Button1Click(Sender: TObject);
var
  LTitle : string;
  LText  : string;
  LhIcon : HICON;
  LPos   : TPoint;
  LArrow : TBalloonArrow;
begin

  //バルーンヒントを表示

  //タイトルとヒントの内容
  LTitle := 'ヒント';
  LText  := 'バルーンヒントを表示' + sLineBreak + '2行目'+ sLineBreak + '3行目';

  //表示のスタイル
  //LArrow:= baTopLeft;       //VCLの上・左へ向けて表示
  //LArrow:= baTopCenter;     //VCLの上・中央
  LArrow:= baTopRight;        //VCLの上・右へ向けて表示
  //LArrow := baBottomRight;  //VCLの下・右へ向けて表示
  //LArrow := baBottomCenter; //VCLの下・中央
  //LArrow := baBottomLeft;   //VCLの下・左へ向けて表示

  //吹き出しの始点
  GetCursorPos(LPos);   //マウスでクリックした位置に表示

  //システムのInfoアイコンを使用
  LhIcon := LoadIcon(0, IDI_INFORMATION);

  try
    //引数はタイトル、ヒント、アイコン、表示位置、吹き出しの始点、時間はミリ秒
    BalloonHint(LTitle, LText, LhIcon, LArrow, LPos, 12000);
  finally
    DestroyIcon(LhIcon);
  end;

end;

で、実行すると・・・

これくらい目立って欲しかった! Mr.XRAYさん、ほんとうにありがとうございます。

2.VCLの位置をTPointでGet!

んじゃ、Button1をクリックしたら、Button2の上に「こっちだよー」みたいにバルーンヒントを表示できたらいいなーっと思って、コードを書こうとしたら、なんと! その書き方を知らないことに気がつきました。

とりあえず、Button2の位置が取得できればいいわけですから、イロイロ調べた結果、次のstack overflow の記事を発見。

How can I get the X,Y position of a TWinControl (relative to the screen)

https://stackoverflow.com/questions/290000/how-can-i-get-the-x-y-position-of-a-twincontrol-relative-to-the-screen

で、以下のコードで、Button2の位置をLabel1に表示できることを確認。
(Pointを使うためにusesにSystem.Typesを追加)

implementation

uses
  plBalloonHint,
  System.Types;

  //System.TypesはButtonの位置を取得するPointを使用するために追加

{$R *.dfm}

procedure TForm1.Button3Click(Sender: TObject);
var
  LPos: TPoint;
begin
  //Button2の左上座標を取得して表示
  LPos := Button2.ClientToScreen(Point(0,0));
  Label1.Caption := Format('Screen: %d, %d', [LPos.X, LPos.Y]);
end;

で、Button1Click手続きのコードを次のように変更。

procedure TForm1.Button1Click(Sender: TObject);
var
  LTitle : string;
  LText  : string;
  LhIcon : HICON;
  LPos   : TPoint;
  LArrow : TBalloonArrow;
begin

  //バルーンヒントを表示

  //タイトルとヒントの内容
  LTitle := 'ヒント';
  LText  := 'バルーンヒントを表示' + sLineBreak + '2行目'+ sLineBreak + '3行目';

  //表示のスタイル
  //LArrow:= baTopLeft;       //VCLの上・左へ向けて表示
  //LArrow:= baTopCenter;     //VCLの上・中央
  LArrow:= baTopRight;        //VCLの上・右へ向けて表示
  //LArrow := baBottomRight;  //VCLの下・右へ向けて表示
  //LArrow := baBottomCenter; //VCLの下・中央
  //LArrow := baBottomLeft;   //VCLの下・左へ向けて表示

  //吹き出しの始点
  //GetCursorPos(LPos);   //マウスでクリックした位置に表示
  //Button2の上・幅の1/2の位置に吹き出しの始点がくるように表示
  LPos := Button2.ClientToScreen(Point(Trunc(Button2.Width div 2), 0));

  //システムのInfoアイコンを使用
  LhIcon := LoadIcon(0, IDI_INFORMATION);

  try
    //引数はタイトル、ヒント、アイコン、表示位置、吹き出しの始点、時間はミリ秒
    BalloonHint(LTitle, LText, LhIcon, LArrow, LPos, 12000);
  finally
    DestroyIcon(LhIcon);
  end;

end;
実現したかったのは、まさにコレ!

バルーンヒントを表示する位置によっては、ヒントが画面からはみ出して見えなくなってしまうことがあるので、表示位置の上下・表示する向きは実際の場面に合わせて調整する必要があるけれど、表示位置はDelphiまかせで制御できない(・・・と思ってるのは私だけ?)TBalloonHintより、見た目もくっきり・はっきりしていて目立つし、plBalloonHint.pasを公開してくださったMr.XRAYさんに心から感謝です。

うまく動かなかったTBalloonHintのコードの一部を使って、次のコードにすれば、

  //LPos := Button2.ClientToScreen(Point(Trunc(Button2.Width div 2), 0));
  LPos := Button2.ClientToScreen(CenterPoint(button2.ClientRect));

ボタンの中心に吹き出しの始点を持ってくることもできます。

ほんとに微妙な違いですが・・・僕はButtonのCaptionが全部見える方が好きです。

バルーンヒント表示対象のVCLコントロールの大きさや位置によって、VCLの周囲に表示するか、内部に表示するか、その判断が異なってくると思うので、ClientRectで座標を取得する方法も覚えておいた方が賢明かと。

3.まとめ

MyPCだけで発生する現象なのかもしれないが、Delphi 11.2 をインストールしたらバルーンヒントの表示位置がオカしくなった。

Mr.XRAYさんが配布してくださっている「自作のバルーンヒント」が表示可能なplBalloonHint.pasを使用すれば、この問題は解決でき、さらに「より良く目立つ」バルーンを表示できる。

バルーンヒントを表示するターゲット(VCL)の左上座標は、

  LPos := ターゲットとするVCLの名称.ClientToScreen(Point(0,0));

上のコードで取得できるので、結果をTPoint型の変数に代入して、バルーンヒントの引数に指定(必要に応じてX、Y座標の値が増加するような式を付加する)。また、VCLコントロールの大きさによっては、ClientRectで座標を取得した方がよい場合もありそう。

LPos := ターゲットとするVCLの名称.ClientToScreen(Point(VCLの名称.ClientRect));

で、表示するコードは、

  //引数はタイトル、ヒント、アイコン、表示位置、吹き出しの始点、時間はミリ秒
  BalloonHint(LTitle, LText, LhIcon, LArrow, LPos, 12000);

4.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容を利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。