WPF无边框窗口,带有影子VS2012风格

Dan*_*lba 21 c# wpf user-interface visual-studio-2012

我正在尝试创建一个看起来像Visual Studio 2012的应用程序.我使用WindowChrome删除了窗口边框,并更改了我的xaml中的边框颜色.

我不知道怎么做是画出窗户的阴影,在这里你可以看到我所说的截图:

带有阴影的Visual Studio无边框窗口

如你所见,有一个阴影,它的颜色也是边框颜色

你知道如何使用WPF实现它吗?

Chr*_*vic 33

更新(17年10月)

现在已经四年了,我有兴趣再次解决这个问题,因此我再一次搞乱了MahApps.Metro基于它衍生出我自己的库.我的ModernChrome库提供了一个类似Visual Studio 2017的自定义窗口:

ModernChrome样本

由于您很可能只对有关发光边框的部分感兴趣,因此您应该使用MahApps.Metro本身或者查看我是如何创建一个GlowWindowBehavior将发光边框附加到我的自定义ModernWindow类的类.这是hevily依赖的一些内部MahApps.Metro和两个依赖属性GlowBrushNonActiveGlowBrush.

如果您只想将发光边框包含在自定义应用程序中,只需引用MahApps.Metro并复制我的GlowWindowBehavior.cs并创建自定义窗口类并相应地调整引用.这最多只需15分钟.

这个问题和我的答案已被频繁访问,所以我希望你能找到我最新的正确解决方案:)


原帖(2013年2月)

我一直在研究这样一个库来复制Visual Studio 2012用户界面.定制镀铬并不是那么困难,但你应该注意的是这个发光的边框很难实现.您可以说将窗口的背景颜色设置为透明,并将主网格的填充设置为大约30px.网格周围的边框可以着色并与彩色阴影效果相关联,但这种方法会强制您设置AllowsTransparency为true,这会大大降低应用程序的视觉效果,这是您绝对不想做的事情!

我目前创建这样一个窗口的方法,它只对边框有一个彩色阴影效果,并且是透明的,但根本没有内容.Evertime我的主窗口的位置改变我只是更新保持边框的窗口的位置.所以最后我正在处理两个带有消息的窗口,假设边框是主窗口的一部分.这是必要的,因为DWM库没有为窗口提供彩色阴影效果的方法,我认为Visual Studio 2012就像我尝试过的那样.

并且通过更多信息扩展这篇文章:Office 2013以不同的方式做到这一点.围绕窗口的边框仅仅是1px的厚,色,但阴影是由DWM与像一个代码绘制的这一个在这里.如果你可以没有蓝色/紫色/绿色边框而只是平常的生活,这就是我选择的方法!只是不要设置AllowsTransparency为true,否则你就输了.

这是我的窗口的屏幕截图,有奇怪的颜色,以突出它的样子:

Metro UI


以下是一些如何开始的提示

请记住,我的代码很长,以至于我只能向您展示要做的基本事情,并且您应该能够以某种方式开始.首先,我将假设我们以某种方式设计了我们的主窗口(手动或使用MahApps.Metro我昨天尝试过的包 - 对源代码进行了一些修改,这非常好(1))我们正在努力实现发光的阴影边框,我GlowWindow将从现在开始打电话.最简单的方法是使用以下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>
Run Code Online (Sandbox Code Playgroud)

生成的窗口应如下图所示.

GlowWindow

接下来的步骤非常困难 - 当我们的主窗口产生时,我们想让GlowWindow可见但在主窗口后面,我们必须在移动或调整主窗口时更新GlowWindow的位置.我建议防止可能发生的视觉故障是在每次更改窗口的位置或大小时隐藏GlowWindow.完成此类操作后,再次显示.

我有一些在不同情况下调用的方法(它可能很多,但只是为了确保)

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) {
    }
}
Run Code Online (Sandbox Code Playgroud)

这个方法主要在我的自定义中调用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
}
Run Code Online (Sandbox Code Playgroud)

但是仍然存在一个问题 - 一旦你调整主窗口的大小,GlowWindow将无法覆盖整个窗口的大小.也就是说,如果你将主窗口的大小调整为大约屏幕的MaxWidth,那么GlowWindow的widt将是相同的值+20,因为我已经为它添加了10的余量.因此,右边缘会在主窗口的右边缘之前中断,看起来很丑.为了防止这种情况,我使用了一个钩子来使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);
};
Run Code Online (Sandbox Code Playgroud)

而且我们仍然会遇到一些问题 - 当你用鼠标滑过GlowWindow并左键单击时它将被激活并获得焦点,这意味着它将与主窗口重叠,如下所示:

重叠的GlowWindow

为了防止这只是捕获Activated边框的事件并将主窗口带到前台.

你应该怎么做?

我建议不要尝试这一点 - 我花了大约一个月的时间来实现我想要的东西,但它仍然存在一些问题,这样我会采用像Office 2013这样的方法 - 彩色边框和DWM API调用的常用阴影 - 没有别的,但看起来还不错.

Office 2013


(1)我刚刚编辑了一些文件以启用窗口周围的边框,这对我来说是在Window 8上禁用的.此外,我操纵了Padding标题栏,使得它看起来并不是那么平坦,最后我改变了All-Caps属性来模仿Visual Studio渲染标题的方式.到目前为止,这MahApps.Metro是一个更好的绘制主窗口的方法,因为它甚至支持AeroSnap我无法用通常的P/Invoke调用实现.


小智 5

您可以使用这个简单的 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> 
Run Code Online (Sandbox Code Playgroud)

  • 我在我的帖子中提到,将“AllowsTransparency”设置为“True”,将“Background”设置为“Transparent”会显着降低性能,由于更改窗口大小或拖动窗口时出现延迟,您可能会注意到这一点。如果我没记错的话,DirectX 没有为此设置使用硬件加速。因此我不建议这样做! (2认同)