none
ToolTipについて

    質問

  • ToolTipの表示なのですが、

     

    ButtonやRadioButtonなどは、どの様にマウスカーソルをコントロール上に

    載せてもToolTipは表示されるんですが、TextBoxやMaskedTextBoxなどは

    ButtonやRadioButtonに比べると非常に表示させ辛い様な気がするのですが。

     

    皆さん、如何なものでしょうか。

     

    環境:
    Windows Vista Ultimate
    Visual Studio 2005 Professional Edition SP1
    .NET Framework 2.0

     

     

    2008年4月24日 13:37

回答

  • 試してみたところ、こちらでも表示させづらいと思いました。
    表示するためには、マウスを素早く動かして TextBox 内に移動するか、TextBox 上でマウスカーソルをいったん停止してから少し動かし、再び停止する必要があるようです。

    # こちらの環境は、Windows XP(SP2)、.NET Framework 2.0(SP1) です。

     

    そこで原因を調べてみたところ、以下のことがわかりました。

    ただし、それに対処するための良さそうな回避策は思いつきませんでした。

    一応、TextBox の BorderStyle を BorderStyle.FixedSingle にすると、ツールチップの表示は正常になります。

     

    まずツールチップの仕組みですが、ツールチップは Windows クラス "tooltips_class32" の仕組みを利用して表示されます。この仕組みでは、ウィンドウメッセージ TTM_WINDOWFROMPOINT を処理し、このメッセージで渡されるマウス座標を使ってツールチップを表示するコントロールを決定します。

    このメッセージは、本来はツールチップが表示される直前に発生するため、渡される座標は停止しているマウスカーソルの座標になりますが、ビジュアルスタイルが有効な場合にはマウスを動かしている最中にも発生するようになり、その上、渡される座標は移動中の座標になってしまいます。確認したところ、この座標はマウスを素早く動かさない限り、TextBox の端になっていました。

    そして、そこへもう一つの仕様が影響します。

    BorderStyle が BorderStyle.Fixed3D の場合には、TextBox のクライアント領域は少し小さくなります(BorderStyle.FixedSingle では小さくなりません)。これが影響することで、座標が TextBox の端の場合には、現在の .NET Framework でのコントロールを決定するロジック(最終的には以下のコードのようなもの)ではコントロールの取得に失敗します。

    この失敗のために1度目の移動ではツールチップが表示されませんが、2度目の移動ではすでに完全にクライアント領域内であるため、処理が期待通りに実行されます。

     

    この2つの動作が影響することで、TextBox では、ツールチップを表示させづらくなってしまっていました。

    Windows 側の問題のような気もしますし、.NET Framework 側の配慮不足のような気もしています。

     

    ツールチップ表示対象コントロールの取得ロジックのイメージ
    Windows API の定義(以下では WinAPI クラスとして定義)は省略しています。

    // TTM_WINDOWFROMPOINT で渡される座標が TextBox の端

    Point cursorPosition = this.PointToScreen(textBox1.Location);

    IntPtr hParent = textBox1.Handle; // 親として TextBox のハンドル

    WinAPI.POINT p = cursorPosition;

    WinAPI.MapWindowPoints(IntPtr.Zero, hParent, ref p, 1);

    // http://msdn.microsoft.com/ja-jp/library/cc410658.aspx を見ると、

    // 親のハンドル(この場合は TextBox)が返されて良さそうにも思いますが…
    IntPtr hChild = WinAPI.ChildWindowFromPointEx(
        hParent, new Point(p.X, p.Y),
        (uint)WinAPI.ChildWindowFromPointFlags.CWP_SKIPINVISIBLE);
    MessageBox.Show(hChild != IntPtr.Zero ? "○取得成功" : "×取得失敗");

     

    遅レスでしかも説明ばかり長々となり、肝心の回避策が提示できませんでしたが、何かの参考になればと思います。

    # tooltips_class32 の仕組みは、にわか勉強です。

    2008年4月30日 12:46

すべての返信

  • 試してみたところ、こちらでも表示させづらいと思いました。
    表示するためには、マウスを素早く動かして TextBox 内に移動するか、TextBox 上でマウスカーソルをいったん停止してから少し動かし、再び停止する必要があるようです。

    # こちらの環境は、Windows XP(SP2)、.NET Framework 2.0(SP1) です。

     

    そこで原因を調べてみたところ、以下のことがわかりました。

    ただし、それに対処するための良さそうな回避策は思いつきませんでした。

    一応、TextBox の BorderStyle を BorderStyle.FixedSingle にすると、ツールチップの表示は正常になります。

     

    まずツールチップの仕組みですが、ツールチップは Windows クラス "tooltips_class32" の仕組みを利用して表示されます。この仕組みでは、ウィンドウメッセージ TTM_WINDOWFROMPOINT を処理し、このメッセージで渡されるマウス座標を使ってツールチップを表示するコントロールを決定します。

    このメッセージは、本来はツールチップが表示される直前に発生するため、渡される座標は停止しているマウスカーソルの座標になりますが、ビジュアルスタイルが有効な場合にはマウスを動かしている最中にも発生するようになり、その上、渡される座標は移動中の座標になってしまいます。確認したところ、この座標はマウスを素早く動かさない限り、TextBox の端になっていました。

    そして、そこへもう一つの仕様が影響します。

    BorderStyle が BorderStyle.Fixed3D の場合には、TextBox のクライアント領域は少し小さくなります(BorderStyle.FixedSingle では小さくなりません)。これが影響することで、座標が TextBox の端の場合には、現在の .NET Framework でのコントロールを決定するロジック(最終的には以下のコードのようなもの)ではコントロールの取得に失敗します。

    この失敗のために1度目の移動ではツールチップが表示されませんが、2度目の移動ではすでに完全にクライアント領域内であるため、処理が期待通りに実行されます。

     

    この2つの動作が影響することで、TextBox では、ツールチップを表示させづらくなってしまっていました。

    Windows 側の問題のような気もしますし、.NET Framework 側の配慮不足のような気もしています。

     

    ツールチップ表示対象コントロールの取得ロジックのイメージ
    Windows API の定義(以下では WinAPI クラスとして定義)は省略しています。

    // TTM_WINDOWFROMPOINT で渡される座標が TextBox の端

    Point cursorPosition = this.PointToScreen(textBox1.Location);

    IntPtr hParent = textBox1.Handle; // 親として TextBox のハンドル

    WinAPI.POINT p = cursorPosition;

    WinAPI.MapWindowPoints(IntPtr.Zero, hParent, ref p, 1);

    // http://msdn.microsoft.com/ja-jp/library/cc410658.aspx を見ると、

    // 親のハンドル(この場合は TextBox)が返されて良さそうにも思いますが…
    IntPtr hChild = WinAPI.ChildWindowFromPointEx(
        hParent, new Point(p.X, p.Y),
        (uint)WinAPI.ChildWindowFromPointFlags.CWP_SKIPINVISIBLE);
    MessageBox.Show(hChild != IntPtr.Zero ? "○取得成功" : "×取得失敗");

     

    遅レスでしかも説明ばかり長々となり、肝心の回避策が提示できませんでしたが、何かの参考になればと思います。

    # tooltips_class32 の仕組みは、にわか勉強です。

    2008年4月30日 12:46
  • TH01さん、回答ありがとうございます。

     

    >遅レスでしかも説明ばかり長々となり

    いえいいえ、とんでもないです。

    私では「何かわからんけどおかしい」って騒ぐだけで、じゃ、何がいけないのか

    どこに問題があるのか調べる事なんでできませんから。

     

    BorderStyle(プロパティ)の値を変更しただけで、ToolTipが表示させやすくなったり

    表示させ辛くなったりするのだから、.NET Framework(Windows)側に問題があると

    みるのが正しい見方かもしれませんね。

     

    「フィードバック」の方に載せてみます。

     

    追伸:

    細かく調べて頂いて本当にありがとうございます。

    大変、参考になりました。

     

    2008年5月1日 5:18
  • こんにちは、森田です。

     

    TH01さん、有用な情報をありがとうございました。

     

    @ぶるーのさん、フォーラムのご利用ありがとうございます。

    また、Microsoft Connectへのフィードバックありがとうございました!

     

    他のフォーラム利用者の方にも参考にしていただけるよう、URLを展開させていただきますね。

     

    ToolTipの表示について

    https://connect.microsoft.com/VisualStudioJapan/feedback/ViewFeedback.aspx?FeedbackID=341420&wa=wsignin1.0

     

    なお、回答を投稿してくださったTH01さんの情報は有用なものだと思われましたので、

    勝手ながら回答チェックをつけさせて頂きました。

    @ぶるーのさんはチェックを解除することもできますのでご確認ください。

     

    それでは、これからもフォーラムのご利用よろしくお願いいたします。

    2008年5月12日 6:43