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๊ฐœ์˜ ์ข‹์•„์š”