Skip to content

Commit

Permalink
Fix issue where window shadow appeared behind owner window
Browse files Browse the repository at this point in the history
  • Loading branch information
yariker committed Jul 18, 2022
1 parent e4b7b1e commit 1c3b7a3
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 47 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ Extensions are provided as attached properties, accessible as `winex:WindowEx.*`

You can download the [Demo app](https://github.com/yariker/WindowExtras.Wpf/releases) to see these properties in action.

# WindowShadow
## WindowShadow

You can attach a drop shadow to a window without affecting the window template or conent. The drop shadow is:
You can attach a drop shadow to a window without affecting the window template or content. The drop shadow is:
* Completely customizable (color, amount of blur, opacity, offset, corner radius, etc.)
* Transparent to mouse clicks (just like any standard window shadow)
* Does not affect the actual size of the host window
Expand Down Expand Up @@ -53,7 +53,7 @@ Here's a minimal example:
</Window>
```

## Gallery
### Gallery

<p float='middle'>
<img src='doc/Demo1.png' width='32%' />
Expand Down
4 changes: 2 additions & 2 deletions src/WindowExtras.Wpf/Menu/MenuItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ private static object OnCoerceIsEnabled(DependencyObject sender, object baseValu
/// <summary>
/// Gets or sets the value determining the menu item kind.
/// </summary>
[Category("Common")]
[Category("Appearance")]
public MenuItemKind Kind
{
get => (MenuItemKind)GetValue(KindProperty);
Expand All @@ -77,7 +77,7 @@ public MenuItemKind Kind
/// <summary>
/// Gets or sets the menu item text.
/// </summary>
[Category("Common")]
[Category("Appearance")]
public string Text
{
get => (string)GetValue(TextProperty);
Expand Down
122 changes: 80 additions & 42 deletions src/WindowExtras.Wpf/Shadows/ShadowWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,23 +45,27 @@ internal static void SetShadowWindow(Window element, ShadowWindow? value)

#endregion

private Window? _owner;
private HWND _hwnd;
private HWND _ownerHwnd;
private HwndTarget? _hwndTarget;

private Window? _hostWindow;
private HWND _hostHwnd;

private HwndSource? _ownerSource;

private int _left;
private int _top;
private int _width;
private int _height;

internal ShadowWindow()
{
InitializeComponent();
}

internal void TryShow()
{
if (!IsVisible && _owner?.IsLoaded == true)
if (!IsVisible && _hostWindow?.IsLoaded == true)
{
Show();
}
Expand Down Expand Up @@ -122,44 +126,61 @@ internal void Attach(Window window)
throw new ArgumentNullException(nameof(window));
}

if (_owner != window)
if (_hostWindow != window)
{
_owner = window;
_owner.Loaded += OnOwnerLoaded;
_owner.LocationChanged += OnOwnerLocationChanged;
_owner.SizeChanged += OnOwnerSizeChanged;
_owner.Activated += OnOwnerActivated;
_owner.StateChanged += OnOwnerStateChanged;
_owner.Closed += OnOwnerClosed;
_hostWindow = window;
_hostWindow.Loaded += OnHostWindowLoaded;
_hostWindow.LocationChanged += OnHostWindowLocationChanged;
_hostWindow.SizeChanged += OnHostWindowSizeChanged;
_hostWindow.Activated += OnHostWindowActivated;
_hostWindow.StateChanged += OnHostWindowStateChanged;
_hostWindow.Closed += OnHostWindowClosed;
}
}

internal void Detach()
{
if (_owner != null)
if (_hostWindow != null)
{
_hostWindow.Loaded -= OnHostWindowLoaded;
_hostWindow.LocationChanged -= OnHostWindowLocationChanged;
_hostWindow.SizeChanged -= OnHostWindowSizeChanged;
_hostWindow.Activated -= OnHostWindowActivated;
_hostWindow.StateChanged -= OnHostWindowStateChanged;
_hostWindow.Closed -= OnHostWindowClosed;
_hostWindow = null;
}

if (_ownerSource != null)
{
_owner.Loaded -= OnOwnerLoaded;
_owner.LocationChanged -= OnOwnerLocationChanged;
_owner.SizeChanged -= OnOwnerSizeChanged;
_owner.Activated -= OnOwnerActivated;
_owner.StateChanged -= OnOwnerStateChanged;
_owner.Closed -= OnOwnerClosed;
_owner = null;
_ownerSource.RemoveHook(OwnerHook);
_ownerSource = null;
}
}

private void OnSourceInitialized(object? sender, EventArgs e)
{
var ownerInterop = new WindowInteropHelper(_owner ?? throw new InvalidOperationException());
_ownerHwnd = (HWND)ownerInterop.Handle;
if (_hostWindow == null)
{
throw new InvalidOperationException();
}

var windowInterop = new WindowInteropHelper(this);
_hwnd = (HWND)windowInterop.Handle;
// Host window.
var hostSource = (HwndSource)PresentationSource.FromVisual(_hostWindow)!;
_hostHwnd = (HWND)hostSource.Handle;

var hwndSource = HwndSource.FromHwnd(windowInterop.Handle)!;
hwndSource.AddHook(WindowHook);
// Owner's owner window.
if (_hostWindow.Owner is Window hostOwner)
{
_ownerSource = (HwndSource)PresentationSource.FromVisual(hostOwner)!;
_ownerSource.AddHook(OwnerHook);
}

// Shadow window.
var hwndSource = (HwndSource)PresentationSource.FromVisual(this)!;
_hwnd = (HWND)hwndSource.Handle;
_hwndTarget = hwndSource.CompositionTarget;
hwndSource.AddHook(WindowHook);

SetTransparent();
}
Expand All @@ -169,30 +190,30 @@ private void OnClosed(object? sender, EventArgs e)
Detach();
}

private void OnOwnerLoaded(object sender, RoutedEventArgs e)
private void OnHostWindowLoaded(object sender, RoutedEventArgs e)
{
UpdatePosition(true, true);
TryShow();
}

private void OnOwnerLocationChanged(object? sender, EventArgs e)
private void OnHostWindowLocationChanged(object? sender, EventArgs e)
{
UpdatePosition(true, false);
}

private void OnOwnerSizeChanged(object sender, SizeChangedEventArgs e)
private void OnHostWindowSizeChanged(object sender, SizeChangedEventArgs e)
{
UpdatePosition(false, true);
}

private void OnOwnerActivated(object? sender, EventArgs e)
private void OnHostWindowActivated(object? sender, EventArgs e)
{
UpdatePosition(true, true);
}

private void OnOwnerStateChanged(object? sender, EventArgs e)
private void OnHostWindowStateChanged(object? sender, EventArgs e)
{
switch (_owner?.WindowState)
switch (_hostWindow?.WindowState)
{
case WindowState.Normal:
Visibility = Visibility.Visible;
Expand All @@ -205,7 +226,7 @@ private void OnOwnerStateChanged(object? sender, EventArgs e)
}
}

private void OnOwnerClosed(object? sender, EventArgs e)
private void OnHostWindowClosed(object? sender, EventArgs e)
{
Close();
}
Expand All @@ -224,7 +245,7 @@ private void SetTransparent()

private void UpdatePosition(bool move, bool resize)
{
if (_owner == null)
if (_hostWindow == null)
{
return;
}
Expand All @@ -234,12 +255,12 @@ private void UpdatePosition(bool move, bool resize)
SET_WINDOW_POS_FLAGS.SWP_NOSENDCHANGING;

var location = new Point(
_owner.Left - margin.Left,
_owner.Top - margin.Top);
_hostWindow.Left - margin.Left,
_hostWindow.Top - margin.Top);

var size = new Point(
_owner.Width + margin.Left + margin.Right,
_owner.Height + margin.Top + margin.Bottom);
_hostWindow.Width + margin.Left + margin.Right,
_hostWindow.Height + margin.Top + margin.Bottom);

if (!move)
{
Expand Down Expand Up @@ -267,7 +288,7 @@ private void UpdatePosition(bool move, bool resize)
_height = DpiHelper.Round(point.Y);
}

SetWindowPos(_hwnd, _ownerHwnd, _left, _top, _width, _height, flags);
SetWindowPos(_hwnd, _hostHwnd, _left, _top, _width, _height, flags);
}
else
{
Expand All @@ -285,19 +306,36 @@ private void UpdatePosition(bool move, bool resize)
}
}

private unsafe IntPtr WindowHook(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
private unsafe nint WindowHook(nint hwnd, int msg, nint wparam, nint lparam, ref bool handled)
{
if (msg == WM_WINDOWPOSCHANGING)
{
var position = (WINDOWPOS*)lparam;
position->hwndInsertAfter = _ownerHwnd;
position->hwndInsertAfter = _hostHwnd;
position->x = _left;
position->y = _top;
position->cx = _width;
position->cy = _height;
handled = true;
}

return IntPtr.Zero;
return 0;
}

private unsafe nint OwnerHook(nint hwnd, int msg, nint wparam, nint lparam, ref bool handled)
{
if (msg == WM_WINDOWPOSCHANGED)
{
var position = (WINDOWPOS*)lparam;

if (position->hwndInsertAfter != _hwnd)
{
UpdatePosition(false, false);
}

handled = true;
}

return 0;
}
}

0 comments on commit 1c3b7a3

Please # to comment.