16

I have a custom WPF window defined as:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" MinHeight="300" Height="350" MinWidth="600" Width="700"      ResizeMode="CanResizeWithGrip" AllowsTransparency="True" WindowStyle="None">

I found a class online that creates drop shadows, shown below. This works well, even with a resize grip, until I maximise the window. Once I maximise the window or change the window state of another window (eg. Visual Studio), I loose the drop shadow and I cannot get it back. Any Ideas?


Drop Shadow Class:

Public Class DropShadow

Private Shared _handler As EventHandler = New EventHandler(AddressOf window_SourceInitialized)

<DllImport("dwmapi.dll", PreserveSig:=True)> _
Private Shared Function DwmSetWindowAttribute(hwnd As IntPtr, attr As Integer, ByRef attrValue As Integer, attrSize As Integer) As Integer

End Function

<DllImport("dwmapi.dll")> _
Private Shared Function DwmExtendFrameIntoClientArea(hWnd As IntPtr, ByRef pMarInset As Margins) As Integer
End Function

Public Shared Sub DropShadowToWindow(window As Window)
    If Not DropShadow(window) Then
        AddHandler window.SourceInitialized, _handler
        AddHandler window.SizeChanged, New SizeChangedEventHandler(AddressOf windowSizeChanged)
    End If
End Sub

Private Shared Sub window_SourceInitialized(sender As Object, e As EventArgs)
    Dim window As Window = DirectCast(sender, Window)

    DropShadow(window)

    RemoveHandler window.SourceInitialized, _handler
End Sub


Private Shared Function DropShadow(window As Window) As Boolean
    Try
        Dim helper As New WindowInteropHelper(window)
        Dim val As Integer = 2
        Dim ret1 As Integer = DwmSetWindowAttribute(helper.Handle, 2, val, 4)

        If ret1 = 0 Then
            Dim m As New Margins() With { _
             .Bottom = 0, _
             .Left = 0, _
             .Right = 0, _
             .Top = 0 _
            }
            Dim ret2 As Integer = DwmExtendFrameIntoClientArea(helper.Handle, m)
            Return ret2 = 0
        Else
            Return False
        End If
    Catch ex As Exception
        ' Probably dwmapi.dll not found (incompatible OS)
        Return False
    End Try
End Function

Private Shared Sub windowSizeChanged(sender As Object, e As SizeChangedEventArgs)
    Dim window As Window = DirectCast(sender, Window)
    DropShadow(window)
End Sub
End Class
H.B.
  • 166,899
  • 29
  • 327
  • 400
Stuart Blackler
  • 3,732
  • 5
  • 35
  • 60
  • When you debug it, what is the first return value from DropShadow? If it is returning False, the event handler won't be hooked up and the DS will disappear when you resize it. – Chris Shain Sep 10 '11 at 02:42
  • It was setting the handlers correctly. I have decided to remove it and try it a different way. I'll post the way I did it in a bit... – Stuart Blackler Sep 10 '11 at 10:01

3 Answers3

23

So I found out a way to get this to work.

You need to use the WPF Shell Integration Library (here) to do the work for you. As it's been written by MS, they have fixed (it seems) any issues with doing to the P/Invoke code.

So it is easy to get a Window that has no Aero glass, is resizable on the edges, has a caption area that behaves with Aero snap, and has a drop shadow that reappears after min/maxing.

This is the code for my window (note, you need to have referenced Microsoft.Windows.Shell)

<Window x:Class="MyLibrary.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:shell="http://schemas.microsoft.com/winfx/2006/xaml/presentation/shell"
        Title="MainWindow"
        WindowStyle="SingleBorderWindow"
        ResizeMode="CanResizeWithGrip"
        mc:Ignorable="d"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        d:DesignHeight="449"
        d:DesignWidth="677"
        Foreground="White"
        Background="Black">

    <shell:WindowChrome.WindowChrome>
        <shell:WindowChrome CaptionHeight="35"
                            GlassFrameThickness="0,0,0,1"
                            ResizeBorderThickness="5" />
    </shell:WindowChrome.WindowChrome>

    <Grid x:Name="LayoutRoot">

    </gGrid>
</Window>

The <shell:WindowChrome> is where you set all the different variables for the interop.

  • CaptionHeight: This is the height of the caption area (headerbar) that allows for the Aero snap, double clicking behaviour as a normal title bar does.
  • GlassFrameThickness: Setting this to 0,0,0,1 for some reason removes the chrome (glass), keeps the square border, and adds a drop shadow.
  • ResizeBorderThickness: This is thickness at the edge of the window which is where you can resize the window.

Other things to note as that you keep the Window.WindowStyle property equal to SingleBorderWindow and let the Shell Library deal with removing the title, buttons and other chrome.

So I kinda wasted my bounty there, but it looks like a completely viable solution that works a treat!

EDIT:

Here is a picture of the result: Sample Metro WPF Application

I also put up a sample project on http://code.google.com/p/sample-metro-wpf-application/. It's an MIT license and people can use it however they want.

Alastair Pitts
  • 19,423
  • 9
  • 68
  • 97
18

To create a drop shadow effect whilst having the ability to re-size the form try the following:

  1. Set the following properties on the window:

    • ResizeMode="CanResizeWithGrip"
    • AllowsTransparency="True"
    • WindowStyle="None"
    • Background="Transparent"
    • BorderThickness="3"
  2. After the window declaration, add a Border element

  3. Create a Border.Effect element inside of the border
  4. For the border effect add the following:

    <DropShadowEffect BlurRadius="5" Color="Black" Opacity="0.8" ShadowDepth="0.5" />
    

This will create the following (without the control box in the top right):

enter image description here

Full XAML:

<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" MinHeight="500" Height="350" MinWidth="300" Width="700" ResizeMode="CanResizeWithGrip" AllowsTransparency="True" WindowStyle="None" Background="White" BorderThickness="3">
<Border>
    <Border.Effect>
        <DropShadowEffect BlurRadius="5" Color="Black" Opacity="0.8" ShadowDepth="0.5" />
    </Border.Effect>
                      <!-- Put your content in here -->
</Border>
</Window>
Stuart Blackler
  • 3,732
  • 5
  • 35
  • 60
  • These options didn't work for me... Stuart Blackler Can you ***please*** post the Entire XAML of the above picture? – Sam Apr 21 '13 at 13:52
  • I believe the only piece I didn't include was the buttons. I will see if I can find the project, but I can't guarantee anything, sorry. – Stuart Blackler Apr 21 '13 at 21:05
  • I created a new project from scratch... and it worked... All that I want to see is the buttons... I created them using `Webdings` font... How did you created? By the way the idea of `BorderThickness` rocks!!! Thanks!!!!!! :) – Sam Apr 22 '13 at 12:16
  • This worked for me, with one caveat. My window is programatically opened from a tray icon click, and does not display the border until minimized then maximized once. To get around this in the code which displays the window, I did (pipes denote new lines): theWindow = New MainWindow() | theWindow.Show() | theWindow.Visibility = Visibility.Hidden | theWindow.Visibility = Visibility.Visible – Sean Jan 16 '14 at 18:42
  • This works, but you lose the window animation when minimizing to taskbar and restoring from taskbar. Any idea how to reimplement that. – The Muffin Man Oct 22 '20 at 21:10
5

Here's some minimal code that does what you're after.

<Window x:Class="WindowChromeSpike.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <WindowChrome.WindowChrome>
    <WindowChrome GlassFrameThickness="0,0,0,1" CornerRadius="0" />
  </WindowChrome.WindowChrome>

  <!-- window contents: just a blue rectangle for demo purposes -->
  <Border Background="#0093C0" />

</Window>

This window behaves like a usual window in that it can be:

  • resized via its edges
  • dragged within the title area
  • right-clicked the title area to show the system menu
  • maximised/restored by double clicking the title area
  • snapped to the sides of your screens by dragging or using hotkeys (Win 10)

It also has a drop shadow.


The end result looks like this:

enter image description here

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
  • How did you add the shadow effect to the window? Is this the only code you need to add a shadow or this is along with the code posted by OP? – Gaurav Mar 10 '21 at 22:05