Post

WPF ContextMenu의 SubMenu 가 왼쪽에 뜨는 현상

WPF는 XAML 로 간단히 ContextMenu를 만들 수가 있는데 대충 아래와 같은 형식이다.

1
2
3
4
5
6
7
8
9
<ContextMenu>
    <MenuItem Header="Item1">
        <MenuItem Header="ItemSub1"></MenuItem>
        <MenuItem Header="ItemSub2"></MenuItem>
        <MenuItem Header="ItemSub3"></MenuItem>
    </MenuItem>
    <MenuItem Header="Item2"></MenuItem>
    <MenuItem Header="Item3"></MenuItem>
</ContextMenu>

위와 같은 메뉴를 만들고 실행을 해 보면 아래와 같이 메뉴가 오른쪽이 아닌 왼쪽으로 뜨는 경우가 있다.

Image

이것은 PC를 터치패널로 사용 할 경우 ContextMenu 를 터치가 편하도록 더 크게 보여주고, 왼손잡이냐 오른손잡이냐에 따라 메뉴 위치를 편한 위치로 조정해주는 Windows 의 편의 기능인데… 여기에 일종의 버그가 발생 하여 그렇다. 간혹 기본 설정이 왼손잡이로 되어 있는 경우가 있는 것이다.

문제는 터치패널이 없을 경우 아래와 같이 손 설정 메뉴가 모두 비활성화 되어 수정 할 수 없다는 것이다…
Image

이 경우 레지스트리를 강제로 수정 후 재부팅 하는 것으로 해결할 수 있다.

경로는 다음과 같다

1
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows\MenuDropAlignment

0일 경우 일반적인 오른쪽 메뉴, 1일 경우 왼쪽 메뉴다.

그런데 이런 시스템 설정을 건드리는건 이래저래 위험하기도 하고, 권한도 필요하다. 그래서 내 Application 에서만 영향을 미치게 하는 방법이 있는데, 바로 해당 값을 로딩 해서 캐싱하는 SystemParameters._menuDropAlignment 의 값을 변경 하는 것이다. 해당 값은 Private 이므로 Reflaction 으로 바꿔야 한다.

그런데 막상 바꿔보면 동작 여부가 랜덤한데, 이것은 Windows가 주기적으로 상태 정보를 Application 으로 전송하고 그것을 WPF 가 받아서 다시 업데이트 하기 때문이다.

그래서 업데이트가 되었을 때 마다 매번 다시 바꿔주는 작업을 필요로 하며, 그 작업까지 완료된 예제 코드는 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void SetSubMenuPositionRight()
{
    var menuDropAlignmentField = typeof(SystemParameters)
        .GetField("_menuDropAlignment", BindingFlags.NonPublic | BindingFlags.Static);
    var setPosition = () =>
        {
            if ((menuDropAlignmentField != null) && (SystemParameters.MenuDropAlignment == true))
            {
                menuDropAlignmentField.SetValue(null, false);
            }
        };

    SystemParameters.StaticPropertyChanged += (sender, e) => { setPosition(); };
    setPosition();
}

해당함수를 적은 후 실행하여 테스트 해 보면 OS의 설정과 관계 없이 메뉴가 오른쪽으로 나타나는것을 볼 수 있다

Image

만약 C# Version 이 10 이하라면 var 를 lambda 선언에 쓸 수 없으므로 아래와 같이 해야 한다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void SetSubMenuPositionRight()
{
    var menuDropAlignmentField = typeof(SystemParameters)
        .GetField("_menuDropAlignment", BindingFlags.NonPublic | BindingFlags.Static);

    Action setPosition = () =>
        {
            if ((menuDropAlignmentField != null) && (SystemParameters.MenuDropAlignment == true))
            {
                menuDropAlignmentField.SetValue(null, false);
            }
        };

    SystemParameters.StaticPropertyChanged += (sender, e) => { setPosition(); };
    setPosition();
}
This post is licensed under CC BY 4.0 by the author.