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

标签 c# wpf user-interface visual-studio-2012

我正在尝试创建一个外观类似于Visual Studio 2012的应用程序。我已使用WindowChrome删除了窗口边框,并更改了xaml中的边框颜色。

我不知道该怎么做是绘制窗口的阴影,在这里您可以看到我在说什么的屏幕截图:

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

您知道如何使用WPF实现吗?

最佳答案

更新(17年10月)

到现在已经四年了,我有兴趣再次解决这个问题,因此我一直在与MahApps.Metro和derived my own library based on it纠缠不清。我的ModernChrome库提供了一个类似于Visual Studio 2017的自定义窗口:

ModernChrome Sample

由于您最可能只对发光边框的部分感兴趣,因此您应该使用MahApps.Metro本身,或者查看我如何创建类GlowWindowBehavior来将发光边框附加到我的自定义ModernWindow类上。它高度依赖于MahApps.Metro的某些内部组件以及两个依赖项属性GlowBrushNonActiveGlowBrush

如果只想在自定义应用程序中包含发光边框,则只需引用MahApps.Metro并复制我的GlowWindowBehavior.cs并创建一个自定义窗口类并相应地修改这些引用即可。最多只需要15分钟。

这个问题和我的答案已被非常频繁地访问,所以希望您会发现我最新的正确解决方案有用:)

原帖(13年2月)

我一直在使用这样的库来复制Visual Studio 2012用户界面。自定义镶边并不难,但是您应该注意的是难以实现的发光边框。您可以说将窗口的背景色设置为透明,并将主网格的填充设置为约30px。网格周围的边框可能是彩色的,并带有彩色的阴影效果,但是这种方法迫使您将AllowsTransparency设置为true,这会大大降低应用程序的视觉性能,这是您绝对不希望做的事情!

我当前的创建这样一个窗口的方法,该窗口在边框上仅具有彩色阴影效果,并且是透明的,但根本没有内容。每当我主窗口的位置发生变化时,我只要更新包含边框的窗口的位置即可。因此,最后我要处理两个带有伪造消息的窗口,这些消息是边框将成为主窗口的一部分。这是必要的,因为DWM库没有为Windows提供彩色阴影效果的方法,而且我认为Visual Studio 2012像我尝试过的类似。

并通过更多信息扩展此帖子:Office 2013则以不同的方式进行。窗口周围的边框只有1px厚且有色,但是阴影是DWM在此处使用this one这样的代码绘制的。如果您可以没有蓝色/紫色/绿色边界,而只有通常的边界,这就是我会选择的方法!只是不要将AllowsTransparency设置为true,否则您会迷路。

这是我的窗口的屏幕截图,颜色奇怪,以突出显示它的外观:



以下是有关如何启动的一些提示

请记住,我的代码很长,因此我只能向您展示基本操作,并且您至少应该以某种方式开始。首先,我将假定我们已经以某种方式设计了主窗口(手动或使用我昨天尝试过的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>

出现的窗口应如下图所示。

下一步非常困难-生成主窗口时,我们希望使GlowWindow可见但在主窗口后面,并且当移动或调整主窗口大小时,我们必须更新GlowWindow的位置。我建议防止可能发生AND的视觉故障,是在每次更改窗口的位置或大小时都隐藏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) {
    }
}

该方法主要在我附加到主窗口的自定义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
}

但是,仍然存在一个问题-调整主窗口的大小后,GlowWindow将无法覆盖其大小的整个窗口。就是说,如果您将主窗口的大小调整为屏幕的最大宽度,那么GlowWindow的宽度将是相同的值+ 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);
};

仍然有一些问题-当您将鼠标移到GlowWindow上并单击鼠标左键时,它将被激活并获得焦点,这意味着它将与主窗口重叠,如下所示:

为了防止这种情况,只需捕获边框的Activated事件并将主窗口置于前台即可。

您应该怎么做?

我建议不要尝试这种方法-我花了一个月的时间才能实现自己想要的功能,但仍然存在一些问题,因此我会采用类似于Office 2013的方法-彩色边框和带有DWM API调用的常用阴影-没别的,看起来还是不错的。



(1)我刚刚编辑了一些文件,以使窗口周围的边框对我来说在Windows 8上被禁用。此外,我还对标题栏的Padding进行了处理,以使其看起来不像原地缩小,最后我更改了All-Caps属性,以模仿Visual Studio呈现标题的方式。到目前为止,MahApps.Metro是绘制主窗口的一种更好的方法,因为它甚至支持我无法通过常规P/Invoke调用实现的AeroSnap。

关于c# - WPF带阴影VS2012样式的无边框窗口,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14730311/

相关文章:

c# - 使形状最顶层

wpf - 从用户控件绑定(bind)到父用户控件的属性

c# - WPF:选择一个 jpg,调整它的大小然后保存它?最佳选择?

c# - WPF 类库中的 ResourceDictionary

java - 如何将 com.vaadin.ui 导入到 Java 项目

c# - 索引文件大小限制

c# - 如何在 datagridview 列标题中显示图像?

c# - 在 try block 中重新抛出异常 c#

java - Swing gui 不显示,但没有错误?

Java面板界面