我正在尝试创建一个外观类似于Visual Studio 2012的应用程序。我已使用WindowChrome删除了窗口边框,并更改了xaml中的边框颜色。
我不知道该怎么做是绘制窗口的阴影,在这里您可以看到我在说什么的屏幕截图:
如您所见,有一个阴影,它的颜色也是边框颜色
您知道如何使用WPF实现吗?
最佳答案
更新(17年10月)
到现在已经四年了,我有兴趣再次解决这个问题,因此我一直在与MahApps.Metro和derived my own library based on it纠缠不清。我的ModernChrome库提供了一个类似于Visual Studio 2017的自定义窗口:
由于您最可能只对发光边框的部分感兴趣,因此您应该使用MahApps.Metro本身,或者查看我如何创建类GlowWindowBehavior
来将发光边框附加到我的自定义ModernWindow
类上。它高度依赖于MahApps.Metro的某些内部组件以及两个依赖项属性GlowBrush
和NonActiveGlowBrush
。
如果只想在自定义应用程序中包含发光边框,则只需引用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/