티스토리 뷰

IT/C#

[CS][WPF] 기본 타이틀바에 적용된 ContextMenu 해제 및 변경하기

주인장 진빼이

기본 타이틀 바에 적용되는 ContextMenu가 있다.

이 ContextMenu는 xaml에서 비활성화 하거나 변경할 수 없다.

 

기본 타이틀 바에 적용되는 ContextMenu를 비활성화 후 직접 만든 메뉴로 교체해보자.

 

 

작업 순서는 다음과 같다.

1. 새로운 ContextMenu 만들기

2. WPF 창에 대한 핸들 구하기

3. 구한 창 핸들을 이용하여 HwndSource 객체 얻기

4. WndProc 연결하기

5. WM_NCRBUTTONDOWN 메시지 처리하기

새로운 ContextMenu 만들기

ContextMenu를 contextMenu라는 Key로 정의했다.

Item1, Item2.. 메뉴 클릭 시 ContextMenu_Click 함수가 처리한다.

<Window.Resources>
	<ContextMenu x:Key="contextMenu" MenuItem.Click="ContextMenu_Click">
		<MenuItem Header="Item 1"/>
		<MenuItem Header="Item 2"/>
		<MenuItem Header="Item 3"/>
		<MenuItem Header="Item 4"/>
	</ContextMenu>
</Window.Resources>

1. WPF 창에 대한 핸들 구하기

WPF 창 핸들은 WindowInteropHelper 객채가 가지고 있다.

WindowInteropHelper 객체를 생성하려면 생성자로 Window 객체를 요구한다. (Application.Current.MainWindow 사용)

Window 객체는 xaml에 정의된 창(Window)을 의미한다.

IntPtr windowHandle = new WindowInteropHelper(Application.Current.MainWindow).Handle;

2. HwndSource 객체 얻기

HwndSource 객체는 windowHandle을 통해서 구할 수 있다.

이 객체는 WndProc를 연결시켜줄 AddHook, RemoveHook 함수를 가지고 있다.

HwndSource hwndSource = HwndSource.FromHwnd(windowHandle);

3. WndProc 연결하기

HwndSource 객체에 AddHook 함수를 이용하여 WndProc 함수를 연결한다.

HwndSourceHook는 델리게이트이다.

 

이때 WndProc를 하나씩 정의할 필요 없이 IDE 빠른완성을 통해 빠르게 델리게이트 구현부 WndProc를 빠르게 작성할 수 있다.

hwndSource.AddHook(new HwndSourceHook(WndProc));
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{

	return IntPtr.Zero;
}

4. WM_NCRBUTTONDOWN 메시지 처리하기

WPF 창에 WndProc을 연결했으니 NonClientArea를 우클릭했을 때 나오는 기본으로 적용된 ContextMenu를 없애야 한다.

WndProc함수 매개변수인 handled 값을 true로 하여 윈도우 메시지를 처리되었다 라고 설정할 수 있다.

조금 더 자세하게 설명하자면 기본으로 처리되는 DefWindowProc 메시지를 처리하지 않는다고 생각하면 된다.

이제 기본으로 적용되었던 ContextMenu는 비활성화 되었다.

 

WndProc에서 조건문에 HTCAPTION을 포함시킨 이유는 새로운 ContextMenu는 최소화, 최대화, 닫기 버튼에서 작동하지 않고

오직 CAPTION(타이틀 바)에서만 작동하기 위해 HITTESTVALUE를 조건을 준 것이다.

 

0x0a4, 0x02는 해당메시지 msdn을 참고하자

private const int WM_NCRBUTTONDOWN = 0x0a4;
private const int HTCAPTION = 0x02;
        
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
	if (msg == WM_NCRBUTTONDOWN && (wParam.ToInt32() == HTCAPTION))
	{
		ShowContextMenu();
		handled = true;
	}
	return IntPtr.Zero;
}

 

기존에 추가한 메뉴를 새롭게 보여지게 만들고 싶다면 함수 하나를 새롭게 정의 후 호출하면 된다.

private void ShowContextMenu()
{
	var contextMenu = Resources["contextMenu"] as ContextMenu; // need resource key
	contextMenu.IsOpen = true;
}

 

 

결과

기본으로 적용된 ContextMenu는 비활성화 되었으며,

새로운 ContextMenu는 NonClient 영역인 타이틀바에서만 작동되는 것을 확인할 수 있다

 

윈도우 창에 대한 Win32 핸들은 구할 수 있지만

창에 부착된 Control은 Visual이기에 Win32 핸들을 구할 수 없다.

WndProc 함수 매개변수 hwnd도 창에 대한 핸들 값만 넘어온다.

 

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함