C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[8]: マウスカーソルがタイトルバー上かを検知


(過去ログ 96 を表示中)

[トピック内 12 記事 (1 - 12 表示)]  << 0 >>

■57305 / inTopicNo.1)  マウスカーソルがタイトルバー上かを検知
  
□投稿者/ ken (2回)-(2011/02/22(Tue) 12:13:55)

分類:[C++/CLI] 

 Visual C++ 2010 expressでマウス位置の検知を用いたフォームアプリケーションを作成しています。しかし、クライアント領域はともかくとしても、タイトルバー上などは、WndProcをオーバーライドする手法しか無理と言うことが分かりました。

WM_NCHITTEST = &H84

を用いれば良いようですが、具体的な記述方法が分かりません。オーバーライドはクラスビュー上でオーバーライドしたいクラス(例えばForm1)を選択しておき、プロパティーのオーバーライドボタンを押せば記述できるのような説明も見つけましたが、Form1を選択しても、表示されているボタンは項目順、アルファベット順、プロパティページのみでオーバーライドボタンは見つかりませんし…。
 書いている質問から初心者であることは分かると思いますが、よろしくお願い致します。
引用返信 編集キー/
■57329 / inTopicNo.2)  Re[1]: マウスカーソルがタイトルバー上かを検知
□投稿者/ shu (469回)-(2011/02/22(Tue) 23:17:28)
2011/02/22(Tue) 23:17:42 編集(投稿者)
No57305 (ken さん) に返信

VB.NETのコードですが参考までタイマーコントロールにて

pt...マウスの座標
bd...フォーム全体の領域
clbd...クライアント部分の領域(スクリーン座標に変換)
bd内でclbdより上にマウス座標があればタイトル部にマウスカーソルがあります。
  bd.left <= pt.x <= bd.left + bd.width  かつ bd.y <= pt.y < clbd.y 
こんな感じの条件でみればよいと思います。
CLIなので多分同じ処理が出来ると思います。

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        Dim pt = Control.MousePosition
        Dim bd = Me.DesktopBounds
        Dim clBd = New Rectangle(Me.PointToScreen(Me.ClientRectangle.Location), Me.ClientSize)

        Label1.Text = String.Format("({0}, {1})", pt.X, pt.Y) & Environment.NewLine &
                        String.Format("({0}, {1}, {2}, {3})", bd.X, bd.Y, bd.Width, bd.Height) & Environment.NewLine &
                        String.Format("({0}, {1}, {2}, {3})", clBd.X, clBd.Y, clBd.Width, clBd.Height) & Environment.NewLine
    End Sub

引用返信 編集キー/
■57331 / inTopicNo.3)  Re[1]: マウスカーソルがタイトルバー上かを検知
□投稿者/ 魔界の仮面弁士 (2083回)-(2011/02/22(Tue) 23:39:24)
No57305 (ken さん) に返信
>  Visual C++ 2010 expressでマウス位置の検知を用いたフォームアプリケーションを作成しています。
何のためにマウス位置を検知したいのかにもよりますが、
Timer で定期的に Cursor->Position を調べるというのは駄目でしょうか。


> WM_NCHITTEST = &H84
&H84 は VB の文法ですね。(^^;) 


> プロパティページのみでオーバーライドボタンは見つかりませんし…。
WndProc メソッドをオーバーライドするのであれば、こんな感じかな。


private:
  static const int WM_NCHITTEST = 0x84;

protected:
  virtual void WndProc( Message% m ) override
  {
    Form::WndProc(m);
    if( m.Msg != WM_NCHITTEST) return;

    // HTCLIENT = 1
    // HTCAPTION = 2
    // HTSYSMENU = 3
    // など(winuser.h で定義されています)
    this->label1->Text = m.Result.ToString();
  }

引用返信 編集キー/
■57362 / inTopicNo.4)  Re[2]: マウスカーソルがタイトルバー上かを検知
□投稿者/ kenken (3回)-(2011/02/23(Wed) 23:28:03)
> private:
> static const int WM_NCHITTEST = 0x84;
>
> protected:
> virtual void WndProc( Message% m ) override
> {
> Form::WndProc(m);
> if( m.Msg != WM_NCHITTEST) return;
>
> // HTCLIENT = 1
> // HTCAPTION = 2
> // HTSYSMENU = 3
> // など(winuser.h で定義されています)
> this->label1->Text = m.Result.ToString();
> }

 ありがとうございます。これで少なくとも境界上はm.Resultが18で検知できることがわかりましたし動作しました。
 目的を書いていませんでした。作成しているのはFormアプリケーションで、Form上にマウスがあるとき(クライアント領域だけでなく、フォーム境界やタイトルバー上を含む)と、フォームからそれているときでフォームの不透明度(Opacity)を変えることを目的としています。.Netでもクライアント領域に関してはMouseEnterとMouseLeaveイベントを用いれば可能ですが、ボーダーとタイトルバー上は検知できません。
 よって正確には、例えばタイトルバー上(境界上も以下同じ)にマウスがあることの検知ではなく、フォーム外からタイトルバーにマウスが動いた、もしくは、タイトルバー上からフォーム外へマウスが動いた事の検知がしたいことになります。一方、同じタイトルバー上にマウスがある場合でも、フォームから移動してきたのか、フォームの外から移動してきたのでは動作を変える必要があります。マウスの位置は判定できますが、マウスの変化を検知することは可能ですか?
引用返信 編集キー/
■57363 / inTopicNo.5)  Re[3]: マウスカーソルがタイトルバー上かを検知
□投稿者/ ken (3回)-(2011/02/23(Wed) 23:31:49)
すみません、名前でkenken=kenです。それと、m.Result=18でボーダー上は検知できますが、m.Result=2のタイトルバー上は、なぜか検出できないときがあるようです。
引用返信 編集キー/
■57364 / inTopicNo.6)  Re[3]: マウスカーソルがタイトルバー上かを検知
□投稿者/ shu (471回)-(2011/02/24(Thu) 08:51:00)
No57362 (kenken さん) に返信

> 。一方、同じタイトルバー上にマウスがある場合でも、フォームから移動してきたのか、フォームの外から移動してきたのでは動作を変える必要があります。マウスの位置は判定できますが、マウスの変化を検知することは可能ですか?

そうするとやっぱりTimerで見ないと難しいんじゃないかな。他のウィンドウのメッセージまで監視することになるし。
引用返信 編集キー/
■57367 / inTopicNo.7)  Re[3]: マウスカーソルがタイトルバー上かを検知
□投稿者/ 魔界の仮面弁士 (2095回)-(2011/02/24(Thu) 11:04:04)
No57363 (ken さん) に返信
> m.Result=18でボーダー上は検知できますが、
HTBORDER ですね。これはサイズ変更されないウィンドウ境界なので、
FormBorderStyle が可変サイズの場合は、別の値になります。

this->ClientRectangle 内の場合は HTCLIENT (1 : クライアント領域) ですね。


> m.Result=2のタイトルバー上は、なぜか検出できないときがあるようです。
検出できなかった場合、何が渡されましたか?

WM_NCHITTEST による検出がうまくいかないようであれば、
gcnew Point(m.LParam.ToInt32()) の座標をもとにして
自分で m.Result をセットするという選択肢もあります。

タイトルバー部に関係ありそうなところだと、Result としては
 HTCAPTION   (2 : タイトルバー領域) 
 HTSYSMENU   (3 : コントロールメニュー) 
 HTMINBUTTON (8 : 最小化ボタン)
 HTMAXBUTTON (8 : 最大化ボタン)
 HTCLOSE    (20 : 閉じるボタン)
あたりですかね。あとはボーダー系。
# あえて別の値を返しても構いませんが。


■No57364 (shu さん) に返信
> そうするとやっぱりTimerで見ないと難しいんじゃないかな。
> 他のウィンドウのメッセージまで監視することになるし。

WM_NCHITTEST や WM_SETCURSOR は基本的に、カーソルが自ウィンドウ内に
あるときにしか飛んできませんしね。


とりあえず、Timer での実装例。
マウスポインタが自フォーム上なら不透明、フォーム外なら透明度 50% です。

private:
  void UpdateOpacity()
  {
    double opacity = Bounds.Contains(Cursor->Position) ? 1.0 : 0.5;
    if (Opacity != opacity) Opacity = opacity;
  }
private:
  void Form1_Load(Object^  sender, EventArgs^  e)
  {
    UpdateOpacity();
    timer1->Interval = 100; //ここの値はお好みで
    timer1->Start();
  }
private:
  void timer1_Tick(Object^  sender, EventArgs^  e)
  {
    UpdateOpacity();
  }

引用返信 編集キー/
■57390 / inTopicNo.8)  Re[4]: マウスカーソルがタイトルバー上かを検知
□投稿者/ ken (4回)-(2011/02/24(Thu) 17:48:48)
ありがとうございます。プログラムの使い勝手から少し仕様を変えようと思います。

フォームを透明化するイベント→フォームからフォーカスが失われたとき
フォームを非透明化するイベント→フォーム(クライアント領域+キャプション領域など)にマウスカーソルが達したとき。

 WM_NCHITTESTで非透明化するイベントを拾おうとすると、例えばキャプション領域にマウスカーソルを留め続けると、次々にイベントが起こってしまい、CPUを全て食ってしまうようです。最初にカーソルがやってきた初回のみ行動を起こすには、前回のWM_NCHITTESTの値を保存しておき、値が同じであったら、非透明化処理をするルーチンを呼び出さないみたいな処置しかないものでしょうか。
引用返信 編集キー/
■57393 / inTopicNo.9)  Re[5]: マウスカーソルがタイトルバー上かを検知
□投稿者/ 魔界の仮面弁士 (2099回)-(2011/02/24(Thu) 19:44:59)
No57390 (ken さん) に返信
> 例えばキャプション領域にマウスカーソルを留め続けると、
> 次々にイベントが起こってしまい、CPUを全て食ってしまうようです。
あれ?

マウスを動かしていないのであれば、ウィンドウの状態が変化したりしない限り、
WM_NCHITTEST は流れてこないと思いますが…。

protected: virtual void WndProc( Message% m ) override
{
  Form::WndProc(m);
  if( m.Msg != WM_NCHITTEST) return;
  listBox1->Items->Insert(0, m.ToString());
}

で。タイマーによる実装だと、何か都合が悪かったのでしょうか?
あれなら、WM_NCHITTEST / MouseLeave / MouseEnter の処理も不要になるように思えますが…。

引用返信 編集キー/
■57430 / inTopicNo.10)  Re[6]: マウスカーソルがタイトルバー上かを検知
□投稿者/ ken (5回)-(2011/02/25(Fri) 23:22:32)
2011/02/28(Mon) 11:00:35 編集(投稿者)
2011/02/26(Sat) 00:08:43 編集(投稿者)

どうもありがとうございます。
うまく行きました。
解決済み
引用返信 編集キー/
■57431 / inTopicNo.11)  Re[7]: マウスカーソルがタイトルバー上かを検知
□投稿者/ 魔界の仮面弁士 (2108回)-(2011/02/25(Fri) 23:31:30)
No57430 (ken さん) に返信
> 以下のように記述してみたのですが、タイマーのイベントが起きていないようです。
そりゃまぁ、timer1 の Tick イベントに timer1_Tick を割り当てていませんし。


> System::Windows::Forms::Timer^ timer1 = gcnew System::Windows::Forms::Timer;//この行追加
コードで直書きせず、デザイナから貼り付けましょう。

そうすれば Visual Studio によって、 void InitializeComponent(void) 内に
 this->timer1 = (gcnew System::Windows::Forms::Timer(this->components));
 this->timer1->Tick += gcnew System::EventHandler(this, &Form1::timer1_Tick);
といったコードが自動生成され、イベント発生時に timer1_Tick が呼ばれるようになるはずですよ。
引用返信 編集キー/
■57458 / inTopicNo.12)  Re[8]: マウスカーソルがタイトルバー上かを検知
□投稿者/ ken (6回)-(2011/02/28(Mon) 11:02:17)
2011/02/28(Mon) 11:06:48 編集(投稿者)

すみません、前後して気がつきました。所望動作できるようになりました。ありがとうございました。
ところで、
Bounds.Contains(Cursor->Position) ? 1.0 : 0.5;
の記述を初めて見たのですが、?より前が条件式、その後のコロンで区切られた2つの数値が、条件式がtrueおよびfalseの戻り値でよいのですよね。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -