最大化自定义窗口失去了阴影效果

17

我有一个自定义的WPF窗口,定义如下:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" MinHeight="300" Height="350" MinWidth="600" Width="700"      ResizeMode="CanResizeWithGrip" AllowsTransparency="True" WindowStyle="None">

我在网上找到了一个创建阴影的类,如下所示。这个类很好用,即使有调整大小的手柄,但当我最大化窗口或改变另一个窗口(例如 Visual Studio)的窗口状态时,阴影会消失,我无法再次获得它。有什么想法吗?


阴影类:

Public Class DropShadow

Private Shared _handler As EventHandler = New EventHandler(AddressOf window_SourceInitialized)

<DllImport("dwmapi.dll", PreserveSig:=True)> _
Private Shared Function DwmSetWindowAttribute(hwnd As IntPtr, attr As Integer, ByRef attrValue As Integer, attrSize As Integer) As Integer

End Function

<DllImport("dwmapi.dll")> _
Private Shared Function DwmExtendFrameIntoClientArea(hWnd As IntPtr, ByRef pMarInset As Margins) As Integer
End Function

Public Shared Sub DropShadowToWindow(window As Window)
    If Not DropShadow(window) Then
        AddHandler window.SourceInitialized, _handler
        AddHandler window.SizeChanged, New SizeChangedEventHandler(AddressOf windowSizeChanged)
    End If
End Sub

Private Shared Sub window_SourceInitialized(sender As Object, e As EventArgs)
    Dim window As Window = DirectCast(sender, Window)

    DropShadow(window)

    RemoveHandler window.SourceInitialized, _handler
End Sub


Private Shared Function DropShadow(window As Window) As Boolean
    Try
        Dim helper As New WindowInteropHelper(window)
        Dim val As Integer = 2
        Dim ret1 As Integer = DwmSetWindowAttribute(helper.Handle, 2, val, 4)

        If ret1 = 0 Then
            Dim m As New Margins() With { _
             .Bottom = 0, _
             .Left = 0, _
             .Right = 0, _
             .Top = 0 _
            }
            Dim ret2 As Integer = DwmExtendFrameIntoClientArea(helper.Handle, m)
            Return ret2 = 0
        Else
            Return False
        End If
    Catch ex As Exception
        ' Probably dwmapi.dll not found (incompatible OS)
        Return False
    End Try
End Function

Private Shared Sub windowSizeChanged(sender As Object, e As SizeChangedEventArgs)
    Dim window As Window = DirectCast(sender, Window)
    DropShadow(window)
End Sub
End Class

当你调试它时,DropShadow 的第一个返回值是什么?如果它返回 False,则事件处理程序不会被挂钩,且在调整大小时 DS 将消失。 - Chris Shain
它已经正确地设置了处理程序。我决定将其删除并尝试另一种方式。稍后我会发布我所做的方式... - Stuart Blackler
3个回答

23

我发现了一种让它工作的方法。

你需要使用WPF Shell Integration Library(这里)来为你完成这项工作。由于它是由微软编写的,他们似乎已经修复了与P/Invoke代码相关的任何问题。

因此,很容易获得一个没有Aero玻璃效果、可以在边缘调整大小、具有与Aero Snap相匹配的标题区域以及在最小化/最大化后重新出现阴影的窗口。

这是我的窗口代码(注意,您需要引用Microsoft.Windows.Shell

<Window x:Class="MyLibrary.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:shell="http://schemas.microsoft.com/winfx/2006/xaml/presentation/shell"
        Title="MainWindow"
        WindowStyle="SingleBorderWindow"
        ResizeMode="CanResizeWithGrip"
        mc:Ignorable="d"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        d:DesignHeight="449"
        d:DesignWidth="677"
        Foreground="White"
        Background="Black">

    <shell:WindowChrome.WindowChrome>
        <shell:WindowChrome CaptionHeight="35"
                            GlassFrameThickness="0,0,0,1"
                            ResizeBorderThickness="5" />
    </shell:WindowChrome.WindowChrome>

    <Grid x:Name="LayoutRoot">

    </gGrid>
</Window>

<shell:WindowChrome>是你设置WPF和Win32之间交互的各种变量的地方。

  • CaptionHeight:标题栏区域(headerbar)的高度,它允许Aero snap、双击行为像普通标题栏一样工作。
  • GlassFrameThickness:将其设置为0,0,0,1可以删除Chrome(玻璃),保留正方形边框并添加阴影。
  • ResizeBorderThickness:这是窗口边缘处你可以调整窗口大小的厚度。

还有一些要注意的事项是,将Window.WindowStyle属性保持等于SingleBorderWindow,并让Shell库处理移除标题、按钮和其他Chrome。

所以我浪费了那里的奖励,但看起来这是一个完全可行的解决方案,而且效果非常好!

编辑:

这是结果的图片:Sample Metro WPF Application

我还在http://code.google.com/p/sample-metro-wpf-application/上放了一个示例项目。它使用MIT许可证,人们可以自由使用。


谢谢您提供的额外答案。您能否发布一张代码图片,以展示最终的效果。这将有助于任何回到此线程的人 :) - Stuart Blackler
当然,我会尽快上传一张图片。 - Alastair Pitts
1
请注意,如果您不想看到调整大小手柄,则可以在ResizeMode中使用CanResize选项。 - Thiru
2
现在你不需要Microsoft.Windows.Shell包。该类在PresentationFramework.dll中,名为System.Windows.Shell.WindowChrome - Drew Noakes
由于某种原因,当我在窗口的代码后台覆盖OnSourceInitialized时,它并没有起作用,现在改为使用Loaded,现在它可以工作了。 - David
显示剩余2条评论

18

要创建一个拥有调整大小能力的窗体并具有投影效果,可以尝试以下步骤:

  1. 在窗口上设置以下属性:

    • ResizeMode="CanResizeWithGrip"
    • AllowsTransparency="True"
    • WindowStyle="None"
    • Background="Transparent"
    • BorderThickness="3"
  2. 在窗口声明之后,添加一个 Border 元素

  3. 在边框内创建一个 Border.Effect 元素

  4. 为边框效果添加以下内容:

    <DropShadowEffect BlurRadius="5" Color="Black" Opacity="0.8" ShadowDepth="0.5" />
    
    这将创建以下内容(不包括右上角的控制框): enter image description here 完整的 XAML:
    <Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" MinHeight="500" Height="350" MinWidth="300" Width="700" ResizeMode="CanResizeWithGrip" AllowsTransparency="True" WindowStyle="None" Background="White" BorderThickness="3">
    <Border>
        <Border.Effect>
            <DropShadowEffect BlurRadius="5" Color="Black" Opacity="0.8" ShadowDepth="0.5" />
        </Border.Effect>
                          <!-- Put your content in here -->
    </Border>
    </Window>
    

这些选项对我没有用…… Stuart Blackler,您能否发布上面图片的完整XAML? - Sam
我相信我没有包括的唯一部分是按钮。我会看看能否找到这个项目,但我不能保证任何事情,抱歉。 - Stuart Blackler
我从零开始创建了一个新项目...它运行良好...我想要看到的只是按钮...我使用Webdings字体创建了它们...你是如何创建的?顺便说一句,BorderThickness的想法非常棒!!!谢谢!!!!!! :) - Sam
这对我有用,但有一个注意点。我的窗口是通过托盘图标单击程序打开的,并且在最小化然后再次最大化之前不显示边框。为了解决这个问题,在显示窗口的代码中,我做了以下操作(管道符表示换行):theWindow = New MainWindow() | theWindow.Show() | theWindow.Visibility = Visibility.Hidden | theWindow.Visibility = Visibility.Visible - Sean
这个可以运行,但是当最小化到任务栏并从任务栏恢复时,你会失去窗口动画。有什么想法如何重新实现它? - The Muffin Man

5
这是一些最简代码,可以实现你的需求。
<Window x:Class="WindowChromeSpike.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <WindowChrome.WindowChrome>
    <WindowChrome GlassFrameThickness="0,0,0,1" CornerRadius="0" />
  </WindowChrome.WindowChrome>

  <!-- window contents: just a blue rectangle for demo purposes -->
  <Border Background="#0093C0" />

</Window>

该窗口的行为类似于普通窗口,可以进行以下操作:
  • 通过边缘调整大小
  • 在标题区域内拖动
  • 右键单击标题区域以显示系统菜单
  • 双击标题区域可最大化/还原
  • 通过拖动或使用快捷键(Win 10)将其挂靠到屏幕侧边。
此外,它还具有阴影效果。
最终效果如下图所示:

enter image description here


你是如何给窗口添加阴影效果的?这是唯一需要添加阴影的代码吗?还是需要与OP发布的代码一起使用? - Gaurav

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接