teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

5

WM_NCMOUSEMOVEの処理が正しくなかったのを修正

2018/06/14 15:35

投稿

atata0319
atata0319

スコア881

answer CHANGED
@@ -114,11 +114,18 @@
114
114
  if (m.Msg == WM_MOUSEMOVE || m.Msg == WM_NCMOUSEMOVE)
115
115
  {
116
116
  Point current;
117
- var control = Control.FromHandle(m.HWnd);
118
- if (control != null)
117
+ if (m.Msg == WM_NCMOUSEMOVE)
118
+ {
119
- current = panel1.PointToClient(control.PointToScreen(new Point(m.LParam.ToInt32())));
119
+ current = panel1.PointToClient(new Point(m.LParam.ToInt32()));
120
+ }
120
121
  else
122
+ {
123
+ var control = Control.FromHandle(m.HWnd);
124
+ if (control != null)
125
+ current = panel1.PointToClient(control.PointToScreen(new Point(m.LParam.ToInt32())));
126
+ else
121
- current = panel1.PointToClient(Cursor.Position);
127
+ current = panel1.PointToClient(Cursor.Position);
128
+ }
122
129
  //Debug.Print("WM_MOUSEMOVE: {0}", current);
123
130
  if (panel1.ClientRectangle.Contains(current))
124
131
  {

4

マウスを素早く操作してパネルを通り抜けた際に反応しない問題に対処してみました。(文面修正)

2018/06/14 15:35

投稿

atata0319
atata0319

スコア881

answer CHANGED
@@ -111,7 +111,7 @@
111
111
  {
112
112
  const int WM_MOUSEMOVE = 0x0200;
113
113
  const int WM_NCMOUSEMOVE = 0x00A0;
114
- if ((m.Msg == WM_MOUSEMOVE || m.Msg == WM_NCMOUSEMOVE) && panel1.Visible)
114
+ if (m.Msg == WM_MOUSEMOVE || m.Msg == WM_NCMOUSEMOVE)
115
115
  {
116
116
  Point current;
117
117
  var control = Control.FromHandle(m.HWnd);

3

マウスを素早く操作してパネルを通り抜けた際に反応しない問題に対処してみました。(文面修正)

2018/06/13 17:40

投稿

atata0319
atata0319

スコア881

answer CHANGED
@@ -70,7 +70,9 @@
70
70
  [https://github.com/atata0319/teratail130742](https://github.com/atata0319/teratail130742)
71
71
 
72
72
  ---
73
- takabosoftさんが指摘されている案2に対応してみました。コーディングのコストはかなり上がった印象になりますね。Panelの派生クラスとして共通の部品として実装されているのであれば、ありかなと思います。線分の交差判定には別のサイトのサンプルを使用しています。あと、可能な限り Win32 API を直接使用するのを避けるためにキャプチャーやフックに頼らない方法で実装してあります。
73
+ takabosoftさんが指摘されている案2に対応してみました。コーディングのコストはかなり上がった印象になりますね。Panelの派生クラスとして共通の部品として実装されているのであれば、ありかなと思います。線分の交差判定には別のサイトのサンプルを使用しています。
74
+
75
+ 可能な限り Win32 API を直接使用するのを避けるためにキャプチャーやフックに頼らない方法で実装してあります。タイマー処理を削除したかったのですが、前述の制限によりフォーム外に移動した際の処理を記述するのが難しいので諦めました。
74
76
  ```C#
75
77
  public partial class Form1 : Form, IMessageFilter
76
78
  {
@@ -198,4 +200,5 @@
198
200
  return tc * td < 0 && ta * tb < 0;
199
201
  }
200
202
  }
201
- ```
203
+ ```
204
+ Github には検証用プロジェクトを追加してあります。

2

マウスを素早く操作してパネルを通り抜けた際に反応しない問題に対処してみました。(文面修正)

2018/06/13 17:29

投稿

atata0319
atata0319

スコア881

answer CHANGED
@@ -70,7 +70,7 @@
70
70
  [https://github.com/atata0319/teratail130742](https://github.com/atata0319/teratail130742)
71
71
 
72
72
  ---
73
- takabosoftさんが指摘されている案2に対応してみました。コーディングのコストはかなり上がった印象になりますね。Panelの派生クラスとして共通の部品として実装されているのであれば、ありかなと思います。線分の交差判定には別のサイトのサンプルを使用しています。
73
+ takabosoftさんが指摘されている案2に対応してみました。コーディングのコストはかなり上がった印象になりますね。Panelの派生クラスとして共通の部品として実装されているのであれば、ありかなと思います。線分の交差判定には別のサイトのサンプルを使用しています。あと、可能な限り Win32 API を直接使用するのを避けるためにキャプチャーやフックに頼らない方法で実装してあります。
74
74
  ```C#
75
75
  public partial class Form1 : Form, IMessageFilter
76
76
  {

1

マウスを素早く操作してパネルを通り抜けた際に反応しない問題に対処してみました。

2018/06/13 17:26

投稿

atata0319
atata0319

スコア881

answer CHANGED
@@ -67,4 +67,135 @@
67
67
  なお、TrackMouseEvent 自体はかなり優秀な API でフォーム外のマウスの動作を補足することができます。通常の MouseMove イベントでこれと同程度の処理を実施する場合、他の回答者のコメント通りフックを使用したりマウスをキャプチャーすることになります。いずれの処理も実装するのがちょっと難しいので、タイマーで処理するのがベターであると私は考えています。
68
68
 
69
69
  検証用に書いたコードを Github に上げました。
70
- [https://github.com/atata0319/teratail130742](https://github.com/atata0319/teratail130742)
70
+ [https://github.com/atata0319/teratail130742](https://github.com/atata0319/teratail130742)
71
+
72
+ ---
73
+ takabosoftさんが指摘されている案2に対応してみました。コーディングのコストはかなり上がった印象になりますね。Panelの派生クラスとして共通の部品として実装されているのであれば、ありかなと思います。線分の交差判定には別のサイトのサンプルを使用しています。
74
+ ```C#
75
+ public partial class Form1 : Form, IMessageFilter
76
+ {
77
+ private bool flag = false;
78
+ private Point previous;
79
+
80
+ public Form1()
81
+ {
82
+ InitializeComponent();
83
+ }
84
+
85
+ private void Form1_Load(object sender, EventArgs e)
86
+ {
87
+ panel1.Visible = false;
88
+ }
89
+
90
+ private void button1_Click(object sender, EventArgs e)
91
+ {
92
+ ShowPanel();
93
+ }
94
+
95
+ private void panel1_MouseEnter(object sender, EventArgs e)
96
+ {
97
+ flag = true;
98
+ }
99
+
100
+ private void panel1_MouseLeave(object sender, EventArgs e)
101
+ {
102
+ if (!panel1.ClientRectangle.Contains(panel1.PointToClient(Cursor.Position)))
103
+ {
104
+ HidePanel();
105
+ }
106
+ }
107
+
108
+ bool IMessageFilter.PreFilterMessage(ref Message m)
109
+ {
110
+ const int WM_MOUSEMOVE = 0x0200;
111
+ const int WM_NCMOUSEMOVE = 0x00A0;
112
+ if ((m.Msg == WM_MOUSEMOVE || m.Msg == WM_NCMOUSEMOVE) && panel1.Visible)
113
+ {
114
+ Point current;
115
+ var control = Control.FromHandle(m.HWnd);
116
+ if (control != null)
117
+ current = panel1.PointToClient(control.PointToScreen(new Point(m.LParam.ToInt32())));
118
+ else
119
+ current = panel1.PointToClient(Cursor.Position);
120
+ //Debug.Print("WM_MOUSEMOVE: {0}", current);
121
+ if (panel1.ClientRectangle.Contains(current))
122
+ {
123
+ flag = true;
124
+ }
125
+ else
126
+ {
127
+ if (flag)
128
+ {
129
+ HidePanel();
130
+ }
131
+ else
132
+ {
133
+ // 直前の座標と現在の座標を結ぶ線分がパネルの矩形と交差する場合、パネルを閉じる。
134
+ if (IsIntersected(panel1.ClientRectangle, current, previous))
135
+ {
136
+ HidePanel();
137
+ }
138
+ }
139
+ }
140
+ previous = current;
141
+ }
142
+ return false;
143
+ }
144
+
145
+ private void timer1_Tick(object sender, EventArgs e)
146
+ {
147
+ var current = panel1.PointToClient(Cursor.Position);
148
+ // フォーム内から一気にフォーム外に出ていったカーソルはここで処理する。
149
+ if (IsIntersected(panel1.ClientRectangle, current, previous))
150
+ {
151
+ HidePanel();
152
+ }
153
+ previous = current;
154
+ }
155
+
156
+ private void ShowPanel()
157
+ {
158
+ panel1.Visible = true;
159
+ timer1.Start();
160
+ Application.AddMessageFilter(this);
161
+ previous = panel1.PointToClient(Cursor.Position);
162
+ }
163
+
164
+ private void HidePanel()
165
+ {
166
+ flag = false;
167
+ timer1.Stop();
168
+ Application.RemoveMessageFilter(this);
169
+ panel1.Visible = false;
170
+ }
171
+
172
+ private static bool IsIntersected(Rectangle rectangle, Point a, Point b)
173
+ {
174
+ // 矩形と線分の交差は各辺と交差しているかどうかで判定する。
175
+ var lefttop = new Point(rectangle.Left, rectangle.Top); // 左上座標
176
+ var righttop = new Point(rectangle.Right, rectangle.Top); // 右上座標
177
+ var leftbottom = new Point(rectangle.Left, rectangle.Bottom); // 左下座標
178
+ var rightbottom = new Point(rectangle.Right, rectangle.Bottom); // 右下座標
179
+ if (IsIntersected(a, b, lefttop, leftbottom)) // 左辺
180
+ return true;
181
+ if (IsIntersected(a, b, righttop, rightbottom)) // 右辺
182
+ return true;
183
+ if (IsIntersected(a, b, leftbottom, rightbottom)) // 下辺
184
+ return true;
185
+ if (IsIntersected(a, b, lefttop, righttop)) // 上辺
186
+ return true;
187
+ return false;
188
+ }
189
+
190
+ private static bool IsIntersected(Point a, Point b, Point c, Point d)
191
+ {
192
+ // https://qiita.com/ykob/items/ab7f30c43a0ed52d16f2
193
+ // 線分(ab)と線分(cd)が交差しているか判定する。
194
+ var ta = (c.X - d.X) * (a.Y - c.Y) + (c.Y - d.Y) * (c.X - a.X);
195
+ var tb = (c.X - d.X) * (b.Y - c.Y) + (c.Y - d.Y) * (c.X - b.X);
196
+ var tc = (a.X - b.X) * (c.Y - a.Y) + (a.Y - b.Y) * (a.X - c.X);
197
+ var td = (a.X - b.X) * (d.Y - a.Y) + (a.Y - b.Y) * (a.X - d.X);
198
+ return tc * td < 0 && ta * tb < 0;
199
+ }
200
+ }
201
+ ```