- PR -

【C#】マウスの乗っているTabコントロールを取得したい

1
投稿者投稿内容
Makoto
大ベテラン
会議室デビュー日: 2004/03/31
投稿数: 133
投稿日時: 2005-10-14 14:24
いつもお世話になっております、

C#で下記の実装をしたいのですが、どうしたらよいでしょうか?
ご存知の方いらっしゃいましたら、よろしくお願いします。

●仕様
 下記のような、タブA、B、Cがある場合に、
 いずれかのタブ上にマウスが入ったら、入ったタブを識別したい。
 また、タブ上からマウスが出たらでたことを取得したい。

 __________
|タブA|タブB|タブC|
|   |____|_____|___
|               |


思うにイベントは、
MouseEnter(object sender, System.EventArgs e)、
MouseLeave(object sender, System.EventArgs e)メソッド内で処理かなぁ
と思うのですが、いくつか問題が出ています。

※MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)は、
 eからマウス座標を取れそうな気もするのですが...

ちなみに、VC++ではMFCのタブクラスにHitTestなる関数があり計算すると
取得できるらしい?のですが、VC#のタブクラスにはないようです。
(日付クラスにはあるみたいです...)

問題1:どのタブ(A,B,C)にマウスが乗ったかわからない

タブコントロールのMouseEnterでは、どのタブ(A,B,C)に
マウスが入ったかわからないのです...
(選択されているページを取得するのではないので、TABのSelectedIndex
では判断できないですし...)

問題2:タブAからタブBへの移動には対応できない

1とも関連しますが、MouseEnterとMouseLeaveでは、
タブAからタブBへの移動には対応できないです。
(MouseMoveで処理できないかなとも考えていますが、どうなんでしょうか?)

※下記は、『選択されているタブに赤枠をつける』例です。
 これを『マウスが乗っているタブに赤枠をつける』ようにしたいです。

MouseEnter(object sender, System.EventArgs e)
{
//下記では、選択されているタブに枠をつけます。
TabControl tab = (TabControl)sender;
Graphics g = tab.CreateGraphics();

int selectID = tab.SelectedIndex;

Rectangle rect = tab.GetTabRect(selectID);
g.DrawRectangle(Pens.Red,rect.Left,rect.Top,rect.Right-rect.Left,rect.Height);
}

以上、良いアイデアなどあれば、よろしくお願いいたします。
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2005-10-14 17:48
ピュアな.NETオンリーでいくのなら、確かにMouseMoveイベントでタブに対してforeachを行いそれぞれにGetTabRectを実施して、という手順が必要かと思います。

.NETのほとんどのコントロールはWindowsのコモンコントロールをラップしただけのものですので、それらに対して可能なほとんどの操作は.NETでも利用可能です。
ですから、APIでコモンコントロールを操作する方法も.NETにおいても基本的には使えます。
で、タブコントロールで、特定の位置にあるタブのインデックスを取得するのに使用する方法として、SendMessage関数を使用してTCM_HITTESTメッセージを送信し、その結果を見るという手段があります。
//MFCのHitTest関数も、内部でTCM_HITTESTを送信し、その返値を取得しているはずです。

ですから、.NETでもSendMessage関数を使用してTCM_HITTESTを送信してやればインデックスを取得できるというわけです。

手前みそですがToolBarで似たようなものを実装した記事です。
これとは使用する構造体の構造が違ったり、メッセージの数値が違ったり、とそのままと言うわけにもいきませんが、参考にはなるかと思います。
//ちなみに、TabControlをオーバーライドすれば自身のWndProcを呼び出すことでSendMessageの代わりにすることができ、DllImportがいらなくなります。
//構造体とIntPtrの変換のため結局Marshalクラスが必要になりますが。
Makoto
大ベテラン
会議室デビュー日: 2004/03/31
投稿数: 133
投稿日時: 2005-10-17 10:41
いつもお世話になっております。

本件ですが、下記のようにコーディングしたところできました。
(ピュアな.NETオンリーでいくという方針なので、下記のように実装してみました。)

結局はAPIといえども、コントロールの座標内に、マウスがあるかどうかを
中で計算しているのかな?と思いますので、自分で計算してみました。

To:ご回答頂いたHongliangさん

 SendMessageやTCM_HITTESTメッセージの件は勉強になりました。
 今回は使用していませんが、WndProc()を使用する方法は今後役に立てられそうな情報です。
 ありがとうございました。

ところで、私のソースコードはアップするとタブ間隔が消えてしまうよう
なんですが、どうしたら消えないようにできるんでしょうか...
(↓見づらいですよね...)

〜〜〜参考までにサンプルコードを下記に示します。〜〜〜

//下記は、Formクラスのメンバとして保持しています。
//(※最終的にはTabクラスのサブクラス化まですれば完了です。)下記では、そこまでしていません。

//描画変更前のデフォルト描画表示イメージの保存データ
System.Drawing.Drawing2D.GraphicsState m_TabDefaultSts = null;

//前回マウスが乗っていたタブのIndex番号(-1)を無効値として使用する。
int previsiousTabIdx = INVALID_IDX;
const int INVALID_IDX = -1;

private void tabControl1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
try
{
TabControl tab = (TabControl)sender;
Graphics g = tab.CreateGraphics();

//赤枠表示前のイメージを保存する!
m_TabDefaultSts = g.Save();

//タブのIDX保存用
int NowIdx = INVALID_IDX;
Rectangle NowRect = System.Drawing.Rectangle.Empty;

for(int i=0;i<tab.TabCount;i++)
{
Rectangle rect = tab.GetTabRect(i);
if( (rect.Left <= e.X) && (e.X <= rect.Right) &&
(rect.Top <= e.Y) && (e.Y <= rect.Bottom) )
{
g.DrawRectangle(Pens.Red,rect.Left,rect.Top,rect.Right-rect.Left,rect.Height);
NowIdx=i;
NowRect=rect;
break;
}
}

if(previsiousTabIdx == INVALID_IDX) //INVALID_IDX(=-1)は、無効タブ番号として定義
{
g.DrawRectangle(Pens.Red,NowRect.Left,NowRect.Top,NowRect.Right-NowRect.Left,NowRect.Height);
}
else //有効なIDX取得できた。
{
if(previsiousTabIdx!=NowIdx) //『前回マウスが乗っていたIDX』と『現在マウスが乗っているIDX』が異なる場合。
{
//////////////////////////
// ●古いのを消す //
// ↓ //
// ●新しいのを書く //
//////////////////////////

g.ResetTransform();
g.Restore(m_TabDefaultSts);
tab.Invalidate();

g.DrawRectangle(Pens.Red,NowRect.Left,NowRect.Top,NowRect.Right-NowRect.Left,NowRect.Height);
}
}
//前回のタブIDXを保存する
previsiousTabIdx = NowIdx;
}
catch(Exception ex)
{
ex.ToString();
}
}

private void tabControl1_MouseLeave(object sender, System.EventArgs e)
{
try
{
TabControl tab = (TabControl)sender;
Graphics g = tab.CreateGraphics();
g.ResetTransform();
g.Restore(m_TabDefaultSts);

tab.Invalidate();
}
catch(Exception ex)
{
ex.ToString();
}
}

以上、ありがとうございました。
1

スキルアップ/キャリアアップ(JOB@IT)