// 複数行対応ツールチップクラス // (C)2002,2003 Yutaka Wada, AirparkLab /* 使い方 標準の CToolTipCtrl と差し替えてそのまま使用できる。 まずツールヒントを表示したいダイアログ(プロパティページ)クラスの protected: メンバにコントロール変数を追加する。 protected: CToolTipsCtrl m_tooltip; 次にダイアログの OnInitDialog() で以下のコードを追加する。 m_tooltip.Create( this ); m_tooltip.Activate( TRUE ); // 以下のどちらかの形式でヒントを表示したいダイアログアイテムに表示するテキストを割り当てる // m_tooltip.AddTool( GetDlgItem(IDC_), ); // m_tooltip.AddTool( GetDlgItem(IDC_), "" ); 最後にダイアログの PreTranslateMessage ハンドラをクラスウィザードで 実装し以下のようにコーディングする。 BOOL C????Dlg::PreTranslateMessage(MSG* pMsg) { // ツールヒントの処理 m_tooltip.RelayEvent(pMsg); return CDialog::PreTranslateMessage(pMsg); } パブリックメンバ関数は以下の通り CToolTipsCtrl() コンストラクタ COLORREF SetBkColor( COLORREF color ) 背景色の設定 引数 新しい背景色 戻値 前の背景色 COLORREF SetTextColor( COLORREF color ) 前面色の設定 引数 新しい前面色 戻値 前の前面色 void SetFont( CFont *pFont ) フォントの設定 引数 設定するフォントオブジェクトへのアドレス */ #ifndef __TOOLTIPS_H__ #define __TOOLTIPS_H__ class CToolTipsCtrl: public CToolTipCtrl { CWnd *m_pWnd; // 親ウインドウ(ダイアログアイテム) BOOL m_DrawFlag; // 描画フラグ CPoint m_Point; // マウスポインタ位置 COLORREF m_BackColor; // 背景色 COLORREF m_FrontColor; // 前面色 CFont m_Font, *m_pFont; // フォント #ifdef _DEBUG UINT MessageLog[256]; // デバグ用 UINT MessageLogP; // デバグ用 #endif public: // コンストラクタ CToolTipsCtrl() { m_pWnd = NULL; m_DrawFlag = FALSE; m_Point.x = m_Point.y = -1; m_BackColor = RGB( 255, 255, 224 ); m_FrontColor = RGB( 0, 0, 0 ); m_Font.CreatePointFont( 90, "MS Pゴシック" ); m_pFont = &m_Font; #ifdef _DEBUG for( int i=0; i<256; i++ ) MessageLog[i] = 0; MessageLogP = 0; #endif } // 背景色の設定 COLORREF SetBkColor( COLORREF color ) { COLORREF oldcolor = m_BackColor; m_BackColor = color; return( oldcolor ); } // 前面色の設定 COLORREF SetTextColor( COLORREF color ) { COLORREF oldcolor = m_FrontColor; m_FrontColor = color; return( oldcolor ); } // フォントの設定 void SetFont( CFont *pFont ) { m_pFont = pFont; } // メッセージハンドラ(Onナントカってやつ)が実行されないっぽいのでメッセージを直接フックする LRESULT WindowProc( UINT message, WPARAM wParam, LPARAM lParam ) { #ifdef _DEBUG // デバグ用にメッセージログを取る MessageLog[MessageLogP] = message; MessageLogP++; MessageLogP &= 255; #endif // ツールチップの表示開始タイミングから1秒毎に来るメッセージ if( message == TTM_WINDOWFROMPOINT ){ // カーソル位置と親ウインドウ(ダイアログアイテム)を取得しておく m_Point = *(POINT*)lParam; m_pWnd = CWnd::WindowFromPoint( m_Point ); //描画フラグをクリア m_DrawFlag = FALSE; } // 非クライアント領域の描画(デフォルトプロシージャは実行しない) if( message == WM_NCPAINT ) return( 0 ); // アニメーションには対応せず(なぜかメッセージはWM_PRINT?) if( message == WM_PRINT || message == WM_PRINTCLIENT ) return( 0 ); // クライアント領域の描画 if( message == WM_PAINT ){ // ツールチップを未描画のときのみ if( m_DrawFlag == FALSE ){ // WM_PAINTハンドラはデバイスコンテキストと更新リージョンを BeginPaint で取得する PAINTSTRUCT Paint; CDC *pDc = CWnd::BeginPaint( &Paint ); // 色とフォントを変更 COLORREF OldBkColor = pDc->SetBkColor( m_BackColor ); COLORREF OldTextColor = pDc->SetTextColor( m_FrontColor ); CFont *pOldFont = pDc->SelectObject( m_pFont ); // ヒント文字列を取得して描画領域サイズを算出 CString HintStr; this->GetWindowText( HintStr ); CRect TipRect( 2, 2, 0, 0 ); pDc->DrawText( HintStr, &TipRect, DT_CALCRECT|DT_LEFT ); // 描画はまだしない // ツールチップウインドウを開く位置と大きさを算出する // マウスの大きさを考慮したりちらつきを抑えたりちょっと臭いコード :-P int w = TipRect.Width() + 7; int h = TipRect.Height() + 6; CRect rect; this->GetWindowRect( &rect ); int x = m_Point.x; int y; if( m_Point.y > rect.top ) y = m_Point.y - h; else y = rect.top; // 画面外にはみ出さないように位置を調節 SystemParametersInfo( SPI_GETWORKAREA, 0, &rect, 0 ); if( rect.right-rect.left < x+w ) x -= (x+w) - (rect.right-rect.left); if( rect.bottom-rect.top < y+h ) y = m_Point.y - h; if( x < 0 ) x = 0; if( y < 0 ) y = 0; // ツールチップウインドウの位置と大きさを変更する this->MoveWindow( x, y, w, h, TRUE ); // 背景と枠の描画 this->GetClientRect( &rect ); pDc->FillSolidRect( &rect, m_BackColor ); CBrush br( m_FrontColor ); pDc->FrameRect( &rect, &br ); // ヒント文字列の描画 pDc->DrawText( HintStr, &TipRect, DT_LEFT ); // 描画フラグをセット m_DrawFlag = TRUE; // 前回の色とフォントを復帰 pDc->SetBkColor( OldBkColor ); pDc->SetTextColor( OldTextColor ); pDc->SelectObject( pOldFont ); // WM_PAINTハンドラ終了 CWnd::EndPaint( &Paint ); } // デフォルトプロシージャは実行しない return( 0 ); } // ツールチップウインドウを表示してからのメッセージタイミング if( message == TTM_RELAYEVENT && m_DrawFlag == TRUE && m_pWnd != NULL ){ // 親ウインドウ(ダイアログアイテム)の範囲を取得 CRect rect; m_pWnd->GetWindowRect( &rect ); // 現在のカーソル位置を取得 POINT pt; GetCursorPos( &pt ); // 親ウインドウ(ダイアログアイテム)から外れたらツールチップウインドウを消す if( rect.PtInRect( pt ) == FALSE ){ this->ShowWindow( SW_HIDE ); m_DrawFlag = FALSE; } } // デフォルトプロシージャの実行 return( CWnd::WindowProc( message, wParam, lParam ) ); } }; #endif /* 参考資料 WindowProc メッセージ TTM_WINDOWFROMPOINT が 来てからの流れ WM_WINDOWPOSCHANGING WM_NCCALCSIZE WM_WINDOWPOSCHANGED WM_MOVE WM_SIZE WM_WINDOWPOSCHANGING WM_WINDOWPOSCHANGING WM_NCPAINT WM_ERASEBKGND WM_WINDOWPOSCHANGED WM_PAINT WM_ERASEBKGND TTM_RELAYEVENT TTM_RELAYEVENT WM_TIMER TTM_RELAYEVENT 定義値 3 0x0003 WM_MOVE 5 0x0005 WM_SIZE 15 0x000f WM_PAINT 20 0x0014 WM_ERASEBKGND 70 0x0046 WM_WINDOWPOSCHANGING 71 0x0047 WM_WINDOWPOSCHANGED 131 0x0083 WM_NCCALCSIZE 133 0x0085 WM_NCPAINT 275 0x0113 WM_TIMER 1031 0x0407 TTM_RELAYEVENT WM_USER(0x0400)+7 1040 0x0410 TTM_WINDOWFROMPOINT WM_USER(0x0400)+16 */ /* 参考資料 WindowProc メッセージ TTM_WINDOWFROMPOINT が 来てからの流れ(Windows2000) * メニュー描画をアニメーション化する場合 * 1040 TTM_WINDOWFROMPOINT 1040 TTM_WINDOWFROMPOINT 70 WM_WINDOWPOSCHANGING 131 WM_NCCALCSIZE 71 WM_WINDOWPOSCHANGED 3 WM_MOVE 5 WM_SIZE 70 WM_WINDOWPOSCHANGING 70 WM_WINDOWPOSCHANGING 131 WM_NCCALCSIZE 71 WM_WINDOWPOSCHANGED 70 WM_WINDOWPOSCHANGING 71 WM_WINDOWPOSCHANGED 791 WM_PRINT 20 WM_ERASEBKGND 792 WM_PRINTCLIENT 70 WM_WINDOWPOSCHANGING --- 131 WM_NCCALCSIZE 71 WM_WINDOWPOSCHANGED 70 WM_WINDOWPOSCHANGING --- x 14 131 WM_NCCALCSIZE 71 WM_WINDOWPOSCHANGED 15 WM_PAINT 20 WM_ERASEBKGND * Windows2000 メニュー描画をアニメーション化しない場合 * 1040 TTM_WINDOWFROMPOINT 1040 TTM_WINDOWFROMPOINT 70 WM_WINDOWPOSCHANGING 131 WM_NCCALCSIZE 71 WM_WINDOWPOSCHANGED 3 WM_MOVE 5 WM_SIZE 70 WM_WINDOWPOSCHANGING 70 WM_WINDOWPOSCHANGING 133 WM_NCPAINT 20 WM_ERASEBKGND 71 WM_WINDOWPOSCHANGED 15 WM_PAINT 20 WM_ERASEBKGND */