WPF Popup 버그

WPF에서 Popup 을 사용할 때

  1. StaysOpen속성을 True로 줍니다.
  2. IsOpen속성을 True로 줘서 Popup을 띄웁니다.
  3. 다른 프로세스의 Window에 포커스를 줍니다. 예를 들어 Visual Studio나…Chrome 브라우저나, Slack 같은 아무 프로그램입니다.
  4. 다시 WPF의 Popup 안의 TextBox를 클릭합니다.

이 경우 4번에서 TextBox에 Cursor가 안 생기고, 3번에서 클릭했던 프로세스에 포커스가 가있습니다. 키보드로 입력해도 그쪽에 타이핑이 됩니다.

왜 그런걸까요?

WPF 프로젝트를 새로 만들어서 아래 gist 소스와 똑같이 복붙해서 따라하시면 재현하실 수 있습니다.

단순하게 버그일까요?

2개의 좋아요

단순 질문입니다.


이 것과 비슷한 현상인가요?

3개의 좋아요
// MainWindow.cs

[DllImport("USER32.DLL")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);

public static void ActivatePopup(Popup popup)
{
    //try to get a handle on the popup itself (via its child)
    HwndSource source = (HwndSource)PresentationSource.FromVisual(popup.Child);
    IntPtr handle = source.Handle;

    //activate the popup
    SetForegroundWindow(handle);
}

// Popup
private void popup_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    ActivatePopup(popup);
}

자세한 설명은 하단 링크를 참조하심이…

4개의 좋아요

그래보입니다!

2개의 좋아요

음…이 주장이 그나마 신빙성은 있는데, Popup이 입력 컨트롤이 아니라 단순 출력컨트롤로만 디자인 되었다는 말은 이 답변자의 개인적인 주장인 것인지 레퍼런스가 있는지 궁금하네요.

일단 MSDN을 찾아봤지만 Popup이 원래 그런 컨셉으로 디자인되었다는 것은 따로 명시는 안되어있는 것 같습니다. 물론 제가 못찾은 것일 수도 있지만요…

WS_EX_NOACTIVATE 윈도우 메세지에 관한 설명도 잘 이해가 안됩니다. 지식이 얕아서 그런거 같네요.

또한 언급해주시고, 코드 프로젝트 본문에도 나와있는 저 예제 코드는 어디서 가져온 것인지 모르겠습니다. .NET Source Browser에도 해당 코드를 찾아볼 수 없고. .NET Framework Reference Source에도 없는 코드입니다…

답변해주신 분의 출처가 궁금하네요…;;

감사드립니다…!!

3개의 좋아요

이곳에서 WS_EX_NOACTIVATE의 설명을 보셔야 합니다. WPF의 Popup는 내부적으로 윈도우를 만들 때 WS_EX_NOACTIVATE 스타일이 적용되므로 버그라기 보다는 올바른 동작으로 평가해야 한다고 생각합니다.

그렇더라도 입력 포커스가 되도록 하려면 @Nobody 님의 코드 (이 코드는 출처가 특별한 코드라기 보다는 단지 윈도우의 스타일을 변경하는 코드입니다) 처럼 윈도 스타일을 변경해서 쓸 수 있을 것 같습니다.

5개의 좋아요

Nobody님 코드는 윈도우 스타일 변경이 아니라 마우스 클릭시 팝업 윈도우 최상위로 지정하는 거고
스타일 변경은 SetWindowLong(handle, GWL_EXSTYLE, GetWindowLong(handle, GWL_EXSTYLE) & ~WS_EX_NOACTIVATE);
개인적으로 팝업 창의 Activate 속성을 지정할 수 있어야 한다고 생각합니다.

6개의 좋아요

링크 감사드립니다.

하지만 이 설명을 읽어보아도 이해가…쉽지 않네요…ㅠㅠ

하지만 뭔가 근거가 더 뒷받침되었다는 뉘앙스는 있는 것 같아서 오류라고 생각하지 않고 나중에는 TextBox가 필요한 Popup에 대해서는 Window를 고려하겠습니다!!

3개의 좋아요

WPF (Windows Presentation Foundation)에서 Popup 컨트롤은 특별한 윈도우 동작을 가지고 있습니다. StaysOpen 속성이 True로 설정되면, 팝업은 외부 요소를 클릭해도 자동으로 닫히지 않게 됩니다. 그런데, 이것은 WPF 애플리케이션 내에서의 동작만을 의미합니다.

문제는 다른 프로세스의 윈도우 (예: Chrome 브라우저)를 활성화하게 되면, 운영체제가 해당 프로세스에 키보드 포커스를 줍니다. 이 경우, WPF 애플리케이션으로 다시 돌아와 팝업의 TextBox를 클릭하더라도, Popup은 이미 IsOpen 상태이기 때문에 실제로 새로운 '포커스 요청’이 발생하지 않습니다. 그 결과, 키보드 포커스는 여전히 이전에 활성화된 프로세스 (Chrome 등)에 있게 됩니다.

정확한 답변인지는 모르겠지만 StaysOpen이 WPF 애플리케이션 안에서만 처리되는 것은 맞는 것 같네요.

강제로 Window를 활성화햐할 것 같아 문뜩 터널링을 통해 아래처럼 시도해봤습니다.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        PreviewMouseDown += MainWindow_PreviewMouseDown;
    }

    private void MainWindow_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        if (!IsKeyboardFocused)
        {
            Activate();
        }
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        popup.IsOpen = true;
    }
}
5개의 좋아요