简体   繁体   English

带阴影的 WPF 无边框窗口 VS2012 样式

[英]WPF borderless window with shadow VS2012 style

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.我正在尝试创建一个看起来像 Visual Studio 2012 的应用程序。我使用WindowChrome删除了窗口边框,并在我的 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 无边框窗口

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?你知道如何使用 WPF 实现它吗?

Update (October '17)更新(2017 年 10 月)

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 .现在已经四年了,我对再次解决这个问题很感兴趣,因此我又一次搞砸了MahApps.Metro基于它派生了我自己的库 My ModernChrome library provides a custom window that looks like Visual Studio 2017:我的ModernChrome库提供了一个类似于 Visual Studio 2017 的自定义窗口:

ModernChrome 示例

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.由于您很可能只对有关发光边框的部分感兴趣,因此您应该使用MahApps.Metro本身或查看我如何创建一个GlowWindowBehavior类, GlowWindowBehavior发光边框附加到我的自定义ModernWindow类。 It is hevily dependant on some internals of MahApps.Metro and the two dependency properties GlowBrush and NonActiveGlowBrush .它严重依赖于 MahApps.Metro 的一些内部结构和两个依赖属性GlowBrushNonActiveGlowBrush

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.如果您只想在自定义应用程序中包含发光边框,只需引用MahApps.Metro并复制我的GlowWindowBehavior.cs并创建自定义窗口类并相应地调整引用。 This is a matter of 15 minutes at most.这最多是 15 分钟的问题。

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)原帖(2013 年 2 月)

I have been working on such a library to copy the Visual Studio 2012 user interface.我一直在研究这样一个库来复制 Visual Studio 2012 用户界面。 A custom chrome isn't that difficult but what you should take care of is this glowing border which is hard to implement.自定义 chrome 并不难,但您应该注意的是这个难以实现的发光边框。 You could just say set the background color of your window to transparent and set the padding of the main grid to about 30px.您可以说将窗口的背景颜色设置为透明,并将主网格的填充设置为大约 30 像素。 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!网格周围的边框可以着色并与彩色阴影效果相关联,但这种方法会强制您将AllowsTransparency设置为 true,这会大大降低应用程序的视觉性能,这是您绝对不想做的事情!

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.这是必要的,因为 DWM 库没有提供一种为 Windows 提供彩色阴影效果的方法,我认为 Visual Studio 2012 与我尝试过的类似。

And to extend this post with more information: Office 2013 does that differently.并用更多信息扩展这篇文章:Office 2013 的做法有所不同。 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.围绕窗口的边框仅仅是1px的厚,着色,但阴影是由DWM与像一个代码绘制的这一个在这里。 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.只是不要将AllowsTransparency设置为 true,否则你就输了。

And here is a screenshot of my window with strange color to highlight what it looks like:这是我的窗口的屏幕截图,用奇怪的颜色突出显示它的样子:

地铁界面


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.首先,我将假设我们以某种方式设计了我们的主窗口(手动或使用我昨天尝试的MahApps.Metro包 - 对源代码进行了一些修改,这真的很好(1) ),我们目前正在努力实现发光阴影边框,从现在开始我将称之为GlowWindow The easiest approach is to create a window with the following XAML code最简单的方法是使用以下 XAML 代码创建一个窗口

<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.生成的窗口应如下图所示。

辉光窗

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.接下来的步骤非常困难 - 当我们的主窗口产生时,我们想让 GlowWindow 可见但在主窗口后面,我们必须在主窗口移动或调整大小时更新 GlowWindow 的位置。 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.我建议防止可能和将发生的视觉故障是在窗口位置或大小的每次更改期间隐藏 GlowWindow。 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:此方法主要在我附加到主窗口的自定义WndProc调用:

/// <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.然而,仍然存在一个问题——一旦您调整主窗口的大小,GlowWindow 将无法以其大小覆盖整个窗口。 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.也就是说,如果您将主窗口的大小调整为屏幕的 MaxWidth 左右,那么 GlowWindow 的宽度将是相同的值 + 20,因为我为其添加了 10 的边距。 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:为了防止这种情况,我使用了一个钩子来使 GlowWindow 成为一个工具窗口:

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:我们仍然会遇到一些问题 - 当您将鼠标移到 GlowWindow 上并左键单击时,它将被激活并获得焦点,这意味着它将与主窗口重叠,如下所示:

重叠发光窗口

To prevent that just catch the Activated event of the border and bring the main window to the foreground.为了防止这种情况,只需捕获边框的Activated事件并将主窗口置于前台。

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 那样的方法 - 彩色边框和带有 DWM API 调用的通常阴影 -没有别的,它看起来还是不错的。

办公室 2013


(1) I have just edited some files to enable the border around the window which is disabled on Window 8 for me. (1)我刚刚编辑了一些文件,以启用在 Window 8 上为我禁用的窗口周围的边框。 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.此外,我操纵了标题栏的Padding ,使其看起来不像原地那样被挤压,最后我更改了 All-Caps 属性以模仿 Visual Studio 呈现标题的方式。 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.到目前为止, MahApps.Metro是绘制主窗口的更好方法,因为它甚至支持 AeroSnap,我无法用通常的 P/Invoke 调用来实现。

You can use this simple xaml code您可以使用这个简单的 xaml 代码

<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> 

This is called "Metro style" (Windows 8 style).这称为“Metro 风格”(Windows 8 风格)。 I think that this Code Project article is interesting for you and it will help you.我认为这篇Code Project 文章对你很有趣,它会对你有所帮助。

You can try Elysium , which licensed under MIT license and included ApplicationBar and ToastNotification classes, or MetroToolKit , from codeplext, too.您可以尝试Elysium ,它在 MIT 许可下获得许可并包含 ApplicationBar 和 ToastNotification 类,或来自 codeplext 的MetroToolKit

This is a great tutorial about Elysium, I think that it helps you. 是一个关于 Elysium 的很棒的教程,我认为它对你有帮助。

For shadow, just add a BitmapEffect to a Border from your Grid in XAML:对于阴影,只需将BitmapEffect到 XAML Grid中的Border

<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>

在此处输入图片说明

<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>

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).我试图获得相同的效果,我的应用程序使用 .NET 4,因此我不能直接使用WindowChrome (因此,我使用Microsoft Windows Shell库来获得相同的效果)。

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.在这个线程中正确地指出,使用 spy++ 可以看到 Visual Studio 有四个称为VisualStudioGlowWindow 的窗口来实现发光效果。 It has been already described in many places how the AllowsTransparency property to true can reduce the performances.已经在很多地方描述了AllowsTransparency属性是如何降低性能的。

So, I tried to go the VS way and the result is not bad (at least, for me);所以,我尝试走 VS 的方式,结果还不错(至少,对我来说); 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.我已经把所有需要的东西都放在了github 上——希望这能有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM