diff --git a/src/SheetMe.Designer/Diagnostics/ModalCheck.cs b/src/SheetMe.Designer/Diagnostics/ModalCheck.cs
index daf15a1..320eaa1 100644
--- a/src/SheetMe.Designer/Diagnostics/ModalCheck.cs
+++ b/src/SheetMe.Designer/Diagnostics/ModalCheck.cs
@@ -71,6 +71,7 @@ public static class ModalCheck
};
var scrimsWhileOpen = -1;
+ var dialogAbove = false;
var modalAtLoaded = false;
dialog.Loaded += (_, _) =>
modalAtLoaded = System.Windows.Interop.ComponentDispatcher.IsThreadModal;
@@ -85,6 +86,7 @@ public static class ModalCheck
timer.Stop();
modalAtProbe = System.Windows.Interop.ComponentDispatcher.IsThreadModal;
scrimsWhileOpen = CountScrims();
+ dialogAbove = DialogIsAboveScrim(dialog);
dialog.Close();
};
timer.Start();
@@ -97,6 +99,8 @@ public static class ModalCheck
Check("모달일 때 가림막이 깔린다", scrimsWhileOpen == 1,
$"실제 {scrimsWhileOpen}개");
+ // 가림막이 대화상자 위에 오면 대화상자까지 같이 흐려진다 — 실제로 그렇게 만들었다
+ Check("가림막이 대화상자 아래에 있다", dialogAbove);
Check("닫으면 가림막이 걷힌다", CountScrims() == 0, $"실제 {CountScrims()}개");
owner.Close();
@@ -112,6 +116,28 @@ public static class ModalCheck
return failed == 0 ? 0 : 1;
}
+ [System.Runtime.InteropServices.DllImport("user32.dll")]
+ private static extern IntPtr GetWindow(IntPtr hwnd, uint command);
+
+ ///
+ /// 대화상자 바로 다음(= 아래) 창이 가림막인가.
+ /// GW_HWNDNEXT = 2 는 z순서에서 이 창 다음에 오는 창을 준다.
+ ///
+ private static bool DialogIsAboveScrim(Window dialog)
+ {
+ var dialogHandle = new System.Windows.Interop.WindowInteropHelper(dialog).Handle;
+ var next = GetWindow(dialogHandle, 2);
+ foreach (Window window in Application.Current.Windows)
+ {
+ if (window.AllowsTransparency && !window.ShowInTaskbar
+ && new System.Windows.Interop.WindowInteropHelper(window).Handle == next)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
/// 가림막은 투명 + 작업표시줄 미표시 + 소유자 있는 창이다
private static int CountScrims()
{
diff --git a/src/SheetMe.Designer/Services/ModalScrim.cs b/src/SheetMe.Designer/Services/ModalScrim.cs
index e1dca07..4d62849 100644
--- a/src/SheetMe.Designer/Services/ModalScrim.cs
+++ b/src/SheetMe.Designer/Services/ModalScrim.cs
@@ -75,8 +75,12 @@ public static class ModalScrim
behind.StateChanged += Follow;
scrim.Show();
- // 가림막을 나중에 띄웠으니 대화상자를 다시 앞으로 올린다
- window.Activate();
+ // 가림막을 대화상자 바로 아래로 내린다.
+ //
+ // Activate() 로는 안 된다. 둘 다 같은 창을 소유자로 갖는 형제라 나중에 띄운 쪽이 위에 오고,
+ // 활성 창을 바꾸는 것과 z순서를 바꾸는 것은 다른 일이다 —
+ // 실제로 대화상자까지 같이 흐려졌다.
+ PlaceBehind(scrim, window);
Layers.Push(scrim);
window.Closed += (_, _) =>
@@ -92,6 +96,26 @@ public static class ModalScrim
};
}
+ [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Unicode)]
+ private static extern bool SetWindowPos(IntPtr hwnd, IntPtr insertAfter,
+ int x, int y, int cx, int cy, uint flags);
+
+ ///
+ /// 을 바로 아래로 놓는다.
+ /// SetWindowPos 의 insertAfter 는 "이 창 다음"이라는 뜻이라, 대화상자를 넘기면 그 아래가 된다.
+ ///
+ private static void PlaceBehind(Window scrim, Window above)
+ {
+ var scrimHandle = new System.Windows.Interop.WindowInteropHelper(scrim).Handle;
+ var aboveHandle = new System.Windows.Interop.WindowInteropHelper(above).Handle;
+ if (scrimHandle == IntPtr.Zero || aboveHandle == IntPtr.Zero)
+ {
+ return;
+ }
+ // SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE
+ SetWindowPos(scrimHandle, aboveHandle, 0, 0, 0, 0, 0x0002 | 0x0001 | 0x0010);
+ }
+
/// 뒤 창을 정확히 덮는다 — 최대화 상태도 그 사각형 그대로다
private static void Cover(Window scrim, Window behind)
{