Microsoft MVP성태의 닷넷 이야기
Mouse이벤트 관련해서 질문있습니다. [링크 복사], [링크+제목 복사]
조회: 20742
글쓴 사람
이성환 (vactorman at naver.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)

다른 플랫폼에서 해보진 않아서 잘 모르겠습니다만

WPF에서 Mouse 관련 이벤트들이 예상과 다르게 동작하는 것이 있어서 질문드립니다.

예를 들어 Rectangle에 MouseEnter / Leave / Up / Down 이벤트 핸들러를

Preview와 함께 모두 연결했습니다.

그랬을 때 Rectangle을 한 번 클릭하면

Preview Mouse Left Button Down
Preview Mouse Down
Mouse Left Button Down
Mouse Down
Preview Mouse Left Button Up
Preview Mouse Up
Mouse Left Button Up
Mouse Up

뭐 이런 순서로 이벤트가 발생할 거라고 예상 하는데요.




네. 예상대로 잘 발생합니다.



그런데

Rectangle을 Button의 자식. 그러니까 Button의 Content로 넣은 후

다시 클릭하면



Preview Mouse Left Button Down
Preview Mouse Down
Mouse Left Button Down
Mouse Down
Mouse Leave

(이 시점에서 Mouse Up 했습니다.)
Mouse Enter



이렇게 이벤트가 발생합니다.

문제는 Rectangle에 걸어놨던 MouseUp이벤트 들이 하나도 발생하지 않는다는 것이죠.

또 저 MouseLeave가 발생하는 시점에서 Rectangle의 IsMouseOver 값을 확인하면 false가 나옵니다.

Mouse Down 상태에서 MouseLeave 이벤트가 발생한 것도 이상한데

마우스 포인터가 Rectangle을 벗어나지 않았음에도 IsMouseOver가 false로 나오는 것 역시 이해가 잘 안 됩니다.


그런데 더 이상한 건

Rectangle의 PreviewMouse 이벤트 핸들러에서 Break Point를 걸고 다시 테스트 하면

위와 다른, 정상적인 결과 (처음 예상했던 결과)가 나옵니다.

결과가 이상해서 추가한 Button에도 동일하게

PreviewMouseLeftButtonDown, PreviewMouseLeftButtonUp, PreviewMouseDown, PreviewMouseUp, MouseDown, MouseUp, MouseEnter, MouseLeave

그리고 Click 이벤트 핸들러를 연결하고 다시 테스트 해보니

Button의 PreviewMouseLeftButtonDown, PreviewMouseDown, MouseDown 이벤트들이 발생하지 않았습니다.
(물론 Button의 자식으로 들어가 있는 Rectangle은 앞서 언급한 비정상적인 결과와 동일했습니다.)

가장 마지막에 발생한 이벤트는 Click 이벤트였습니다.


제가 WPF의 Mouse 이벤트에 대해 이해를 잘못 하고 있는 건지

원래 이런 건지 잘 모르겠습니다.

당장 필요한 건 MouseUp 이벤트를 받는 건데 어떻게 해야할 지 감이 안 잡힙니다.


혹시나 해서 간단한 테스트 솔루션하나 첨부해볼게요.

확인 부탁드립니다.



[연관 글]






[최초 등록일: ]
[최종 수정일: 7/13/2011]


비밀번호

댓글 작성자
 



2011-07-13 05시40분
[이성환] 아.. 수정하다 실수로 파일이 두개가 돼 버렸네요. 둘 다 같은 파일 입니다. 하나만 보시면 될듯...
[guest]
2011-07-15 08시23분
[이성환] 혼자 이것저것 테스트 해보면서 어설프게나마 결론에 도달했습니다.

Button 내부의 Element들에게서 MouseLeaver가 발생하는 이유는

Button의 MouseDown 이벤트가 도착할 때 Button 스스로 CaptureMouse를 하기 때문이더군요.

비단 Button 뿐만 아니라 자식을 가진 Element의 경우에도 마찬가지로

부모가 CaptureMouse를 하면 그 순간 자식들에게 모두 MouseLeave 이벤트가 떨어졌습니다.

당연히 MouseLeave 이후 MouseEnter나 MouseDown이 일어나지 않았으니 MouseUp 역시 발생하지 않은 거구요.

Button 이 CaptureMouse를 하는 지는 이번에 처음 알았습니다.

어쩔 수 없이 MouseUp을 대체하는 방법으로 돌고 돌아서 MouseUp과 유사한 형태의 이벤트 처리를 하긴 했지만

생각보다 사소하다고 여겼던 것에 한 번 당하고 나니

이벤트 처리, 특히 마우스 관련 처리는 한 번 더 고민하고 사용하게 됐네요.

[guest]
2011-07-15 08시25분
[이성환] 근데 궁금한 건.

왜 Button에는 버블링 MouseDown / Up 이벤트가 발생하지 않을까요?

암만 찾아봐도 PreviewMouseDown / Up 까지는 들어오는데 버블링 MouseDown / Up 이벤트는 발생하지 않네요.

왜 그런건가요??
[guest]
2011-07-18 07시28분
제가 WPF 손을 놓은지 꽤나 오래 되어서 ^^ 관련 지식도 희미해지는 바람에 답변이 너무 늦었습니다.

일단, 이벤트 버블링과 터널링이 있는데요.

WPF Input Events
; https://docs.microsoft.com/en-us/dotnet/desktop/wpf/advanced/routed-events-overview#how_event_processing_works

버블링은 부모 요소로 이벤트를 전달하는 것이기 때문에 "Button에서 버블링이 왜 안되느냐"는 질문은 올바르지 않습니다. ^^ Button의 하위 요소인 Rectangle에 이벤트가 내려가는 것이므로 Tunneling이 왜 안되냐가 맞습니다.

이성환 님이 직접 살펴보신 대로, 아마도 마우스의 캡처가 원인이지 않을까 싶습니다. ^^ Win32 시절부터 윈도우 영역을 벗어나는 경우에도 마우스 이벤트를 유지해야 하는 경우 SetCapture/ReleaseCapture 함수를 호출하는 동작이 있었습니다.

버튼의 경우, 마우스를 누른 체로 윈도우 영역을 벗어나도 여전히 마우스가 캡처된 상태이기 때문에 다시 누른 체로 마우스를 버튼 위로 이동해서 버튼을 떼면 Click 이벤트가 발생하게 됩니다. 즉, 다른 요소보다는 특별한 동작이 요구되는데, 이러한 처리 때문에 이벤트 터널링에 영향이 있는 것 같습니다.

일단 현상이 그러하니, 다른 방법을 찾아야 할 텐데 터널링이 안 되므로 자식 요소에서 직접 부모 요소의 Up 이벤트를 감시하는 방법이 어떨까 싶습니다. 이를 위해 버튼이 버블링해 주는 Up 이벤트를 가로채면 되겠지요. ^^

xButton.AddHandler(MouseUpEvent, new MouseButtonEventHandler(mouseUpEvent), true);

어쨌든, 이성환 님 덕분에 저도 버튼의 이벤트 처리에 대한 생각을 다시 해 볼 수 있게 되었군요. ^^

(참고로, 마지막 질문에서 헷갈리신 것 같은데 PreviewMouseDown/MouseDown은 잘 들어오는데, PreviewMouseUp/MouseUp이 안되는 것입니다. ^^)
정성태
2011-07-18 09시20분
[이성환] 답변 감사드립니다. (__)

그런데.. 제가
왜 Button에는 버블링 MouseDown / Up 이벤트가 발생하지 않을까요? 라고 질문했던 건

xButton의 MouseDown/Up 이벤트가 발생하지 않는다는 의미였습니다.

실제로 Output을 찍어보면

xButton_MouseEnter Button Captured : False
xRootGrid_MouseEnter Button Captured : False
xMainRectangle_MouseEnter Button Captured : False
== 요기까지는 클릭을 위해 MouseEnter 되는 시점.

== 요기서부터 MouseLeftButtonDown
xRootGrid_MouseLeave Button Captured : False
xButton_PreviewMouseDown Button Captured : False
xRootGrid_PreviewMouseLeftButtonDown Button Captured : False
xRootGrid_PreviewMouseDown Button Captured : False
xMainRectangle_PreviewMouseLeftButtonDown Button Captured : False
xMainRectangle_PreviewMouseDown Button Captured : False
xMainRectangle_MouseLeftButtonDown Button Captured : False
xMainRectangle_MouseDown Button Captured : False
xRootGrid_MouseLeftButtonDown Button Captured : False
xRootGrid_MouseDown Button Captured : False
xMainRectangle_MouseLeave! Button Captured : True
xRootGrid_MouseLeave Leave Button Captured : True
== 요기까지가 MouseLeftButtonDown 후 그대로 유지 상태.

== 요기서부터 MouseUp
The thread '<No Name>' (0x880) has exited with code 0 (0x0).
xButton_PreviewMouseLeftButtonUp Button Captured : True
xButton_PreviewMouseUp Button Captured : True
xMainRectangle_MouseEnter Button Captured : False
xRootGrid_MouseEnter Button Captured : False
xButton_Click Button Captured : False

요런 형태 인데요.

보시다 시피

xButton_PreviewMouseDown
xButton_PreviewMouseLeftButtonUp
xButton_PreviewMouseUp
xButton_Click 까지는 발생하고

나머지 버블링 관련 이벤트들은 하나도 발생하지 않았는데요. (당연히 핸들러는 다 걸려있구요.)

왜 xButton에 버블링 이벤트가 하나도 발생하지 않았는지는 아직 답을 찾지 못해서 질문드린 것이었습니다.
(그러고 보니 xButton_PreviewLefrButtonDown도 발생하지 않았네요..)

요거까지 확인을 해야 시원하게 원인파악을 했다고 말할 수 있을 것같아서요...;ㅅ;
[guest]
2011-07-18 09시42분
아래의 글에 별도로 답변을 달았으니 참고하세요. ^^

왜? Button 컨트롤에는 MouseDown/MouseUp 이벤트가 발생하지 않을까요?
; http://www.sysnet.pe.kr/2/0/1089

그리고, 마지막 댓글에서 xButton_PreviewLeftButtonDown도 발생하지 않는다고 쓰셨는데... 발생합니다. 왜 발생하지 않은 듯(?) 보인 것인지, 다시 한번 소스 코드를 점검해 보세요. ^^
정성태
2011-07-18 11시01분
[이성환] 매번 친절한 답변 감사드립니다.

근데 제가 우매하여... 아직도 이해가 되질 않습니다.

일단 제가 첨부한 소스에서는 e.Handled = true; 가 없습니다.
(요건 알고 있던 거라..)

그런데 저는 제가 첨부한 소스로 해봐도, 제가 테스트 하던 소스로 해봐도

역시 xButton의 버블링 이벤트는 발생하지 않습니다.
(xButton_PreviewMouseLeftDown 역시 발생하지 않습니다.)

이벤트 전달을 막은 곳도 없고
(e.Handled = true로 막았다고 이벤트가 전달 안 되는 건 아니니까 더 이상합니다.)

해당 이벤트의 핸들러가 등록되어있지 않은 것도 아닌데

포스팅 해주신 글을 몇 번 읽어봐도

왜 이벤트가 발생 안 하는지 모르겠습니다.
(정확히는 왜 핸들러를 호출하지 않는 건지)

혹시 같은 소슨데 저만 안 되는 건가요??

;ㅅ;
[guest]
2011-07-18 11시03분
[이성환] 아.. PreviewMouseLeftDown 은 Output을 잘못 찍고 있었군요...

이건 찾았습니다...;;;

그럼 xButton의 버블링 이벤트만 확인하면 되겠군요....;;;
[guest]
2011-07-19 12시34분
음... ^^ 제가 쓴 글은, WPF의 Button 컨트롤도 같은 방식으로 되어 있다는 것을 설명해 드린 것입니다. .NET Reflector로 버튼 쪽 소스를 보면 MouseLeftButtonDown 이벤트에서 e.Handled = true를 걸고 있기 때문에 Bubbling이 발생하지 않는 것입니다.
정성태
2011-07-19 08시54분
[이성환] 아.. 그렇군요. Button 내부에서 이미 e.Handled = true 처리를 하고 있어서

버블링 이벤트가 발생 안 하는 것이었군요. ;;;;

포스팅에서 링크 걸어 놓으신 social 게시판에서도 질문자가 저와 동일한 질문을 한 번 더 했고

그 다음 답변에 Snoop을 써서 이벤트 구조를 확인해보라는 걸로 마무리가 나 있길래

이거 그냥은 모르는 거구나 했습니다.

그리고 제가 아직 그 정도까지 Reflector를 잘 쓰질 못해서 내부 소스 보는 게 꺼려지더군요.

이 기회에 공부 좀 해야겠습니다.

덕분에 많이 배웠습니다. ~ㅂ~

매번 좋은 글과 답변 주셔서 감사합니다 (__)
[guest]

... 31  32  33  34  35  36  37  38  39  [40]  41  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
4854heyh...7/6/20179497
4853heyh...7/5/20179611성태님이 작성한대로 해봤습니다. [1]파일 다운로드1
4852김레오7/4/201711791서드파티 dll 디버깅에 대해 질문드립니다. [2]
4851김현준7/3/201710895Datagridview VirtualMode 시 GC가 계속 호출되는 현상이 이해가 안갑니다. [2]
4850heyh...7/3/201710636성태님 밑에 질문드렸던 오류입니다. [1]파일 다운로드1
4849포플러7/2/201729320C#으로 만든 프로그램이 어느 순간 속도가 느려지거나 멈춤현상이 있습니다. [4]
4848윤진영7/2/201712274Microsoft Visual C++ 6.0 무설치 관련 [1]
4847heyh...6/30/201710278아무리 고민해도 답이 안나와서.. 질문 드립니다 [1]
4846heyh...6/28/201710317Clickonce에서 Clickonce로 변수 전달 [1]
4845heyh...6/26/20179989vb.net 에서 manifest 제거 방법 [1]파일 다운로드1
4844san6/21/20179422part3 pdf파일로 보는데 눈아퍼요.... 활자로 보고싶어요 [2]
4843윤현수6/20/20179648socket통신에 관한 질문입니다. [1]
4842오세운6/7/201710332로그인폼 다시 질문드려요. [1]
4841popo6/7/201710103궁금한사항이 있어 질문 드립니다. [1]
4840윤현수6/5/201710112datagridview에 바인딩시킨 dataset.table에 관련된 질문입니다. [2]파일 다운로드1
4839오세운6/2/201711015로그인폼 만들기 [1]
4838후배5/29/201711744PDF Reader 라이브러리.. [3]
4837KDP5/29/201711248c++ to C# 컨버팅 문의 [2]
4836popo5/26/201714381리플렉션 성능 관련 질문 드립니다. [2]
4835guest5/25/201710100wpf Canvas 질문드립니다. [1]
4834부탁드립...5/19/201711012익스체인지 서버 구동중 인증서를 받는 과정에서오류가 납니다 [1]
4833안종윤5/18/201711415winform을 Web Browser에 붙일 수 있는지요? [1]
4832guest5/17/201710897c# UDP socket예제 실행방법 [3]
4831guest5/15/201713384WPF .net 3.5에서 TextBox 한글 문제 [3]
4830초보자5/7/201718286프로그램 비정상 종료 메시지 창 없애는 방법 [4]
4829윤현수4/26/201714665System.Text.Encoding 질문 [1]
... 31  32  33  34  35  36  37  38  39  [40]  41  42  43  44  45  ...