WPF borderless window with shadow VS2012 style

36,040

Solution 1

Update (October '17)

It has been four years now and I was interested in tackling this again and thus I have been messing around with MahApps.Metro once again and derived my own library based on it. My ModernChrome library provides a custom window that looks like Visual Studio 2017:

ModernChrome Sample

Since you are most likely only interested in the part about the glowing border, you should either use MahApps.Metro itself or see how I created a class GlowWindowBehavior which attaches glow borders to my custom ModernWindow class. It is hevily dependant on some internals of MahApps.Metro and the two dependency properties GlowBrush and NonActiveGlowBrush.

If you only want to include the glowing borders to your custom applications just reference MahApps.Metro and copy over my GlowWindowBehavior.cs and create a custom window class and adapt the references accordingly. This is a matter of 15 minutes at most.

This question and my answer have been accessed very frequently so I hope you will find my newest proper solution useful :)


Original post (February '13)

I have been working on such a library to copy the Visual Studio 2012 user interface. A custom chrome isn't that difficult but what you should take care of is this glowing border which is hard to implement. You could just say set the background color of your window to transparent and set the padding of the main grid to about 30px. A border around the grid could be colored and associated with a colored shadow effect but this approach forces you to set AllowsTransparency to true which drastically reduces visual performance of your application and this is something you definitely do not want to do!

My current approach to create such a window which just has a colored shadow effect on a border and is transparent but has no content at all. Evertime the position of my main window changes I just update the position of the window which holds the border. So in the end I am handling two windows with messages to fake that the border would be part of the main window. This was necessary because the DWM library doesn't provide a way to have a colored drop shadow effect for windows and I think Visual Studio 2012 does that similiar like I tried.

And to extend this post with more information: Office 2013 does that differently. The border around a window is just 1px thick and colored, yet the shadow is drawn by DWM with a code like this one here. If you can live without having blue/purple/green borders and just usual ones this is the approach I would choose! Just don't set AllowsTransparency to true, otherwise you have lost.

And here is a screenshot of my window with strange color to highlight what it looks like:

Metro UI


Here are some hints on how to start

Please keep in mind that my code is quite long, such that I will only be able to show you the basic things to do and you should be able to at least start somehow. First of all I'm going to assume that we have designed our main window somehow (either manually or with the MahApps.Metro package I tried out yesterday - with some modifications to the sourcecode this is really good(1)) and we are currently working to implement the glowing shadow border, which I will call GlowWindow from now on. The easiest approach is to create a window with the following XAML code

<Window x:Class="MetroUI.Views.GlowWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="GlowWindow"
    Title="" Width="300" Height="100" WindowStartupLocation="Manual"
    AllowsTransparency="True" Background="Transparent" WindowStyle="None"
    ShowInTaskbar="False" Foreground="#007acc" MaxWidth="5000" MaxHeight="5000">
    <Border x:Name="OuterGlow" Margin="10" Background="Transparent"
            BorderBrush="{Binding Foreground, ElementName=GlowWindow}"
            BorderThickness="5">
        <Border.Effect>
            <BlurEffect KernelType="Gaussian" Radius="15" RenderingBias="Quality" />
        </Border.Effect>
    </Border>
</Window>

The resulting window should look like the following picture.

GlowWindow

The next steps are quite difficult - when our main window spawns we want to make the GlowWindow visible but behind the main window and we have to update the position of the GlowWindow when the main window is being moved or resized. What I suggest to prevent visual glitches that can AND will occur is to hide the GlowWindow during every change of either location or size of the window. Once finished with such action just show it again.

I have some method which is called in different situations (it might be a lot but just to get sure)

private void UpdateGlowWindow(bool isActivated = false) {
    if(this.DisableComposite || this.IsMaximized) {
        this.glowWindow.Visibility = System.Windows.Visibility.Collapsed;
        return;
    }
    try {
        this.glowWindow.Left = this.Left - 10;
        this.glowWindow.Top = this.Top - 10;
        this.glowWindow.Width = this.Width + 20;
        this.glowWindow.Height = this.Height + 20;
        this.glowWindow.Visibility = System.Windows.Visibility.Visible;
        if(!isActivated)
            this.glowWindow.Activate();
    } catch(Exception) {
    }
}

This method is mainly called in my custom WndProc I have attached to the main window:

/// <summary>
/// An application-defined function that processes messages sent to a window. The WNDPROC type
/// defines a pointer to this callback function.
/// </summary>
/// <param name="hwnd">A handle to the window.</param>
/// <param name="uMsg">The message.</param>
/// <param name="wParam">Additional message information. The contents of this parameter depend on
/// the value of the uMsg parameter.</param>
/// <param name="lParam">Additional message information. The contents of this parameter depend on
/// the value of the uMsg parameter.</param>
/// <param name="handled">Reference to boolean value which indicates whether a message was handled.
/// </param>
/// <returns>The return value is the result of the message processing and depends on the message sent.
/// </returns>
private IntPtr WindowProc(IntPtr hwnd, int uMsg, IntPtr wParam, IntPtr lParam, ref bool handled) {
    // BEGIN UNMANAGED WIN32
    switch((WinRT.Message)uMsg) {
        case WinRT.Message.WM_SIZE:
            switch((WinRT.Size)wParam) {
                case WinRT.Size.SIZE_MAXIMIZED:
                    this.Left = this.Top = 0;
                    if(!this.IsMaximized)
                        this.IsMaximized = true;
                    this.UpdateChrome();
                    break;
                case WinRT.Size.SIZE_RESTORED:
                    if(this.IsMaximized)
                        this.IsMaximized = false;
                    this.UpdateChrome();
                    break;
            }
            break;

        case WinRT.Message.WM_WINDOWPOSCHANGING:
            WinRT.WINDOWPOS windowPosition = (WinRT.WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WinRT.WINDOWPOS));
            Window handledWindow = (Window)HwndSource.FromHwnd(hwnd).RootVisual;
            if(handledWindow == null)
                return IntPtr.Zero;
            bool hasChangedPosition = false;
            if(this.IsMaximized == true && (this.Left != 0 || this.Top != 0)) {
                windowPosition.x = windowPosition.y = 0;
                windowPosition.cx = (int)SystemParameters.WorkArea.Width;
                windowPosition.cy = (int)SystemParameters.WorkArea.Height;
                hasChangedPosition = true;
                this.UpdateChrome();
                this.UpdateGlowWindow();
            }
            if(!hasChangedPosition)
                return IntPtr.Zero;
            Marshal.StructureToPtr(windowPosition, lParam, true);
            handled = true;
            break;
    }
    return IntPtr.Zero;
    // END UNMANAGED WIN32
}

However there is still an issue left - once you resize your main window the GlowWindow will not be able to cover the whole window with its size. That is if you resize your main window to about MaxWidth of your screen, then the widt of the GlowWindow would be the same value + 20 as I have added a margin of 10 to it. Therefore the right edge would be interrupted right before the right edge of the main window which looks ugly. To prevent this I used a hook to make the GlowWindow a toolwindow:

this.Loaded += delegate {
    WindowInteropHelper wndHelper = new WindowInteropHelper(this);
    int exStyle = (int)WinRT.GetWindowLong(wndHelper.Handle, (int)WinRT.GetWindowLongFields.GWL_EXSTYLE);
    exStyle |= (int)WinRT.ExtendedWindowStyles.WS_EX_TOOLWINDOW;
    WinRT.SetWindowLong(wndHelper.Handle, (int)WinRT.GetWindowLongFields.GWL_EXSTYLE, (IntPtr)exStyle);
};

And still we will have some issues - when you go with the mouse over the GlowWindow and left click it will be activated and get the focus which means it will overlap the main window which looks like this:

Overlapping GlowWindow

To prevent that just catch the Activated event of the border and bring the main window to the foreground.

How should YOU do this?

I suggest NOT to try this out - it took me about a month to achieve what I wanted and still it has some issues, such that I would go for an approach like Office 2013 does - colored border and usual shadow with the DWM API calls - nothing else and still it looks good.

Office 2013


(1) I have just edited some files to enable the border around the window which is disabled on Window 8 for me. Furthermore I have manipulated the Padding of the title bar such that it doesn't look that sqeezed inplace and lastly I have change the All-Caps property to mimic Visual Studio's way of rendering the title. So far the MahApps.Metro is a better way of drawing the main window as it even supports AeroSnap I couldn't implement with usual P/Invoke calls.

Solution 2

You can use this simple xaml code

<Window x:Class="VS2012.MainWindow" 
         xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation 
         xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml 
         Title="MainWindow" 
         Height="100" Width="200" 
         AllowsTransparency="True" WindowStyle="None" Background="Transparent"> 
<Border BorderBrush="DarkOrange" BorderThickness="1" Background="White" Margin="5">
         <Border.Effect>
                <DropShadowEffect ShadowDepth="0" BlurRadius="5" Color="DarkOrange"/>
         </Border.Effect>
</Border>
</Window> 

Solution 3

This is called "Metro style" (Windows 8 style). I think that this Code Project article is interesting for you and it will help you.

You can try Elysium, which licensed under MIT license and included ApplicationBar and ToastNotification classes, or MetroToolKit, from codeplext, too.

This is a great tutorial about Elysium, I think that it helps you.

For shadow, just add a BitmapEffect to a Border from your Grid in XAML:

<Grid>
    <Border BorderBrush="#FF006900" BorderThickness="3" Height="157" HorizontalAlignment="Left" Margin="12,12,0,0" Name="border1" VerticalAlignment="Top" Width="479" Background="#FFCEFFE1" CornerRadius="20, 20, 20, 20">
        <Border.BitmapEffect>
          <DropShadowBitmapEffect Color="Black" Direction="320" ShadowDepth="10" Opacity="0.5" Softness="5" />
        </Border.BitmapEffect>
        <TextBlock Height="179" Name="textBlock1" Text="Hello, this is a beautiful DropShadow WPF Window Example." FontSize="40" TextWrapping="Wrap" TextAlignment="Center" Foreground="#FF245829" />
    </Border>
</Grid>

enter image description here

Solution 4

<Window x:Class="MyProject.MiniWindow"
    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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:MyProject"
    mc:Ignorable="d" 
    WindowStyle="None" 
    Title="MiniWindow" Background="Transparent"
    Height="200" Width="200" 
    >

<WindowChrome.WindowChrome>
    <WindowChrome 
    CaptionHeight="0"
    ResizeBorderThickness="4" />
</WindowChrome.WindowChrome>

<Grid Margin="0">
    <Border BorderThickness="3">
        <Border BorderThickness="1" Margin="0" BorderBrush="#ff007acc">
            <Border.Effect>
                <DropShadowEffect Color="#ff007acc" Direction="132" ShadowDepth="0" BlurRadius="8" />
            </Border.Effect>
            <Grid   Background="#ff2d2d30">
                
            </Grid>
        </Border>
    </Border>
    
    
</Grid>

Solution 5

I'm trying to obtain the same effect, my app is using .NET 4 and thus I cannot directly use WindowChrome (so, I'm using the Microsoft Windows Shell library to get the same).

In this thread it is correctly noted that using spy++ it can be seen that Visual Studio has four windows called VisualStudioGlowWindow to implement the glowing effect. It has been already described in many places how the AllowsTransparency property to true can reduce the performances.

So, I tried to go the VS way and the result is not bad (at least, for me); no need to use blur or similar effect on the main window, I just had to fight a bit with some window states (focus/visible/hidden).

I've put all the needed stuff on github - hope this can help.

Share:
36,040
Daniel Peñalba
Author by

Daniel Peñalba

Software Engineer at Unity Technologies, Valladolid, Spain. Currently developing gmaster, Plastic SCM and SemanticMerge. Areas: C# GUI development Winforms and WPF expert ASP .NET Core Multiplatform UI development with Mono (Linux and OSX, GTK# and MonoMac) Eclipse plugin, Java Automated testing, NUnit, Moq, PNUnit and TestComplete Email: dpenalba[AT]codicesoftware[DOT]com I play the guitar at Sharon Bates, the greatest Spanish rock band.

Updated on July 09, 2022

Comments

  • Daniel Peñalba
    Daniel Peñalba almost 2 years

    I'm trying to create an application that looks like Visual Studio 2012. I have used WindowChrome to remove the window borders, and changed the border color in my xaml.

    What I don't know how to do is paint the shadow of the window, here you can see an screenshot of what I'm saying:

    Visual Studio Borderless window with shadow

    As you can see there is a shadow and its color is also the border color

    Do you know how to implement it using WPF?

  • Daniel Peñalba
    Daniel Peñalba over 11 years
    But, what about my question, I only need to add a border shadow, I don't want a toolkit.
  • Daniel Peñalba
    Daniel Peñalba over 11 years
    Christian, thanks for your comments. Please, could you provider a simple code example for this approach?? "... a colored shadow effect on a border and is transparent but has no content at all". Thank you very much.
  • Christian Ivicevic
    Christian Ivicevic over 11 years
    @DanielPeñalba: I have added some more information - is this what you were looking for?
  • Ming Slogar
    Ming Slogar almost 11 years
    @ChristianIvicevic: Office 2013 does not use DWM to render its window shadow. If you open Spy++, you will see 5 separate windows: the main Office window, and 4 shadow windows. Also, if you minimize and then restore the window, you will see the shadow blip up a split second before the main window restores.
  • Christian Ivicevic
    Christian Ivicevic over 10 years
    I mentioned in my post that setting AllowsTransparency to True with the Background being set to Transparent dratically reduces performance which you can notice due to lags when changing the size of the window or dragging it. If I remember correctly DirectX is not using hardware acceleration for this setup. Therefore I do not recommend doing so!
  • Christian Ivicevic
    Christian Ivicevic over 10 years
    @MingSlogar: Though this topic is quite old now I would like to know more about how this works in Office. Can you provide some more details or ideas of your own how one could implement so many windows and update them properly?
  • Ming Slogar
    Ming Slogar over 10 years
    @ChristianIvicevic: To tell the truth, I am not sure exactly how Office 2013/VS2012+ renders the shadow, but in my app I created a controller class which was called by any window which needed a shadow. The controller handled placement of 4 separate drop shadow windows which had Owner set to the main window; this would force the shadow to be on top of the window. The size/placement of the shadow windows placed them outside the main window, so although they were on top they appeared to be in the background. I can post the entire classes if you want to see exactly how I did it.
  • Christian Ivicevic
    Christian Ivicevic about 10 years
    @MingSlogar: I have come once again to this topic and I am actually intrested in your approach you mentioned. Could you share it please?
  • Ming Slogar
    Ming Slogar about 10 years
    @ChristianIvicevic: Check out this post on my blog: mingslogar.comule.com/posts/2014/02/28_2.php. If you are unable to view that, let me know and I'll post the code here.
  • SiMoStro
    SiMoStro about 10 years
    I thought the question was "Do you know how to implement it using WPF?" and I gave a full implementation to this in WPF: I added a link to the repository that contains the full source code (I guess it makes little sense to copy/paste 500 lines of code in a post...).
  • David Murdoch
    David Murdoch about 9 years
    Add WS_EX_TRANSPARENT to the GlowWindow's exStyles in its this.Loaded event and the GlowWindow will no longer be clickable.
  • Stefano
    Stefano almost 9 years
    @MingSlogar could you please share the code that was on your blog, maybe putting it into a gist or in another answer here? When trying to open your link I get only a blank page
  • Ming Slogar
    Ming Slogar almost 9 years
    @Stefano My apologies for that; my website is down temporarily. You can view the raw code here: 1drv.ms/1C9N8xI (just open the HTML files in a browser). Another developer created windowglows.codeplex.com from the code on my site. I have not tested it, but feel free to check that out as well.
  • Stefano
    Stefano almost 9 years
    @MingSlogar As I'm not a C# developer but I'm trying to do the same thing on C++, if you could ping me when your site is up (I imagine the post was a tutorial showing how to use your code) it would be great
  • Ming Slogar
    Ming Slogar almost 9 years
    @Stefano My site is now up. However, there's not much on the post besides just the code.
  • Ahmad Sattout
    Ahmad Sattout almost 8 years
    @ChristianIvicevic can you please post a download link for you application that copies VS interface?
  • Christian Ivicevic
    Christian Ivicevic over 6 years
    @MingSlogar I have returned to this project and updated my post - please feel free to take a look at it.
  • Ming Slogar
    Ming Slogar over 6 years
    @ChristianIvicevic thanks for the update! I actually ended up building something: dimension4.codeplex.com/SourceControl/latest#Daytimer/… (and the entire Daytimer.Fundamentals package). Of course, I haven't revisited this problem (or the project in general) in a few years, so I'm not positive that it still works.
  • Andrew Mikhailov
    Andrew Mikhailov about 6 years
    Github link is dead :(