Vincent
September 14, 2023, 10:12am
1
WPF에서 Popup 을 사용할 때
StaysOpen 속성을 True로 줍니다.
IsOpen 속성을 True로 줘서 Popup을 띄웁니다.
다른 프로세스의 Window에 포커스를 줍니다. 예를 들어 Visual Studio나…Chrome 브라우저나, Slack 같은 아무 프로그램입니다.
다시 WPF의 Popup 안의 TextBox를 클릭합니다.
이 경우 4번에서 TextBox에 Cursor가 안 생기고, 3번에서 클릭했던 프로세스에 포커스가 가있습니다. 키보드로 입력해도 그쪽에 타이핑이 됩니다.
왜 그런걸까요?
WPF 프로젝트를 새로 만들어서 아래 gist 소스와 똑같이 복붙해서 따라하시면 재현하실 수 있습니다.
MainWindow.xaml
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
mc:Ignorable="d">
This file has been truncated. show original
MainWindow.xaml.cs
using System.Windows;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
This file has been truncated. show original
단순하게 버그일까요?
2 Likes
Nobody
September 14, 2023, 11:52am
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 Likes
Vincent
September 15, 2023, 3:07am
5
음…이 주장이 그나마 신빙성은 있는데, Popup이 입력 컨트롤이 아니라 단순 출력컨트롤로만 디자인 되었다는 말은 이 답변자의 개인적인 주장인 것인지 레퍼런스가 있는지 궁금하네요.
일단 MSDN을 찾아봤지만 Popup이 원래 그런 컨셉으로 디자인되었다는 것은 따로 명시는 안되어있는 것 같습니다. 물론 제가 못찾은 것일 수도 있지만요…
WS_EX_NOACTIVATE 윈도우 메세지에 관한 설명도 잘 이해가 안됩니다. 지식이 얕아서 그런거 같네요.
또한 언급해주시고, 코드 프로젝트 본문에도 나와있는 저 예제 코드는 어디서 가져온 것인지 모르겠습니다. .NET Source Browser 에도 해당 코드를 찾아볼 수 없고. .NET Framework Reference Source 에도 없는 코드입니다…
답변해주신 분의 출처가 궁금하네요…;;
감사드립니다…!!
3 Likes
dimohy
September 15, 2023, 5:15am
6
다음은 확장된 창 스타일입니다.
이곳에서 WS_EX_NOACTIVATE 의 설명을 보셔야 합니다. WPF의 Popup는 내부적으로 윈도우를 만들 때 WS_EX_NOACTIVATE 스타일이 적용되므로 버그라기 보다는 올바른 동작으로 평가해야 한다고 생각합니다.
그렇더라도 입력 포커스가 되도록 하려면 @Nobody 님의 코드 (이 코드는 출처가 특별한 코드라기 보다는 단지 윈도우의 스타일을 변경하는 코드입니다) 처럼 윈도 스타일을 변경해서 쓸 수 있을 것 같습니다.
5 Likes
그라목손
September 15, 2023, 5:27am
7
Nobody님 코드는 윈도우 스타일 변경이 아니라 마우스 클릭시 팝업 윈도우 최상위로 지정하는 거고
스타일 변경은 SetWindowLong(handle, GWL_EXSTYLE, GetWindowLong(handle, GWL_EXSTYLE) & ~WS_EX_NOACTIVATE);
개인적으로 팝업 창의 Activate 속성을 지정할 수 있어야 한다고 생각합니다.
5 Likes
Vincent
September 15, 2023, 7:23am
8
링크 감사드립니다.
하지만 이 설명을 읽어보아도 이해가…쉽지 않네요…ㅠㅠ
하지만 뭔가 근거가 더 뒷받침되었다는 뉘앙스는 있는 것 같아서 오류라고 생각하지 않고 나중에는 TextBox가 필요한 Popup에 대해서는 Window를 고려하겠습니다!!
3 Likes
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;
}
}
3 Likes