(WPF) 마우스 이벤트 통과 투명 윈도우 만들기

 

WPF 투명 윈도우 만들기 관련 질문에 대한 답변 포스트 입니다.

이 글에서 다루는 코드는 다음 Repository에서 확인할 수 있습니다.
code_check - Transparent_Window

WPF Transparent Window 관련 질문이 있었는데 다음과 같은 내용 입니다.
요약하면 투명하고 컨텐츠의 모양 그대로 표시 되는 창을 만들려면 어떡게 해야 하는지 그리고
그 위에 마우스 오른쪽 버튼 이벤트나 드래그 등 사용자 액션 이벤트 메세지가 그대로 허용 되도록 하고 싶다는 조건이 있었습니다.

질문 분석 및 분해

우선 질문 내용을 분해해서 분석해 보면

  • a. 창 테두리 표시 없이 컨텐츠의 모양 그대로 표시되도록 하는 방법
  • b. 마우스 관련 이벤트 메세지 그대로 허용 (예 : 배경화면에서 해당 창 위에 마우스 오른쪽 버튼 클릭시 OS Shell Context Menus 표시)

크게 이렇게 질문을 분해해 볼 수 있습니다.

질문 분해의 답

사실 a 분석의 경우 단순히 윈도우의 배경 처리를 투명으로 하면 쉽게 해결 되는 부분 입니다.
문제는 b 분석 내용의 프로세스가 마우스 이벤트 관련 메세지를 받지 않고 그대로 통과 시켜야 하는 부분 입니다.
이 부분의 답은 약간의 구글 검색을 통해 WinAPI의 SetWindowLong(IntPtr hwnd, int index, int newStyle) 함수로 쉽게 해결 할 수 있을 것 같은 답을 찾았습니다.

질문 해결

우선 투명 윈도우로 설정하기 위해 System.Windows.Window 클래스에 있는 AllowsTransparency 속성 값을 True로 설정 합니다.
그리고 WindowStyle속성 값은 None로 설정 합니다. (WPF에서 AllowsTransparency속성이 true인 경우 반드시 WindowStyle속성을 None로 해야 합니다.)
이렇게 하면 투명 배경 설정이 가능 합니다.

<Window AllowsTransparency="True"
        WindowStyle="None"
        Background="#01000000">
</Window>



마우스 이벤트 메세지 통과 처리는 WinAPI함수중 SetWindowLong(IntPtr hwnd, int index, int newStyle) 함수로 윈도우 스타일 설정으로 해결 가능 합니다.
윈도우 스타일을 투명으로 설정하면 해당 윈도우에서 메세지를 받지 않고 그대로 통과 합니다.
투명 창 스타일은 SetWindowLong(IntPtr hwnd, int index, int newStyle) 함수의 세번째 파라메터에 투명 스타일 상수 값 전달로 설정 할 수 있습니다.

창 스타일에 대한 추가 정보는Extended Window Styles (Winuser.h) - Win32 MS Doc에서 확인 가능합니다.

참고로 GetWindowLong(IntPtr hwnd, int index) 함수로 기존 윈도우 스타일 정보를 가져 올 수 있습니다.

따라서 다음과 같이 구현하여 처리 할 수 있습니다.

namespace Transparent_Window
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Threading.Tasks;

    public static class WindowsServices
    {
        /// <summary>
        /// 투명 윈도우 스타일
        /// </summary>
        const int WS_EX_TRANSPARENT = 0x00000020;
        /// <summary>
        /// 계층화 윈도우 스타일 및 클래스 스타일 적용 <para/>
        /// Window8 버전 이하에서는 최상위 윈도우에서만 지원됨
        /// </summary>
        const int WS_EX_LAYERED = 0x00080000;
        /// <summary>
        /// 새로운 확장 윈도우 스타일 설정
        /// </summary>
        const int GWL_EXSTYLE = (-20);

        [DllImport("user32.dll")]
        static extern int GetWindowLong(IntPtr hwnd, int index);

        [DllImport("user32.dll")]
        static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);

        public static void SetWindowExTransparent(IntPtr hwnd)
        {
            // 기존 윈도우 스타일 정보를 가져오기
            //var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);

            if (AppSetting.Instance.IsMouseEventMessagePass)
            {
                SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_TRANSPARENT);
            }
            else
            {
                SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_LAYERED);
            }
        }
    }
}

이렇게 윈도우의 핸들을 받아 창 스타일 WS_EX_LAYERED / WS_EX_TRANSPARENT 설정으로 투명 처리에 대해 토글로 처리할 수 있도록 SetWindowExTransparent(IntPtr hwnd) 메서드를 구현했습니다.
지금까지 코드를 응용하여 다음과 같은 포켓몬 스티커 앱을 간단하게 구현해 보았습니다.

[Pokemon sticker App]
pokemon_sticker


위 코드는 다음 Repository에서 확인할 수 있습니다.

code_check - Transparent_Window