1. 程式人生 > 其它 >WPF 製作高效能的透明背景異形視窗(使用 WindowChrome 而不要使用 AllowsTransparency=True)

WPF 製作高效能的透明背景異形視窗(使用 WindowChrome 而不要使用 AllowsTransparency=True)

WPF 26 篇文章3 訂閱 訂閱專欄

 

目錄

 

背景透明的異形視窗

如下是一個背景透明異形視窗的示例:

此視窗包含很大的圓角,還包含 DropShadowEffect 製作的陰影效果。對於非透明視窗來說,這是不可能實現的。

如何實現

要實現這種背景透明的異形視窗,需要為視窗設定以下三個屬性:

  • WindowStyle="None"
  • ResizeMode="CanMinimize" 或 ResizeMode="NoResize"
  • WindowChrome.GlassFrameThickness="-1"
     或設定為其他較大的正數(可自行嘗試設定之後的效果)

如下就是一個最簡單的例子,最關鍵的三個屬性我已經高亮標記出來了。

    <Window x:Class="Walterlv.Demo.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
++          WindowStyle="None" ResizeMode="CanMinimize"
            Title
="walterlv demo" Height="450" Width="800"> ++ <WindowChrome.WindowChrome> ++ <WindowChrome GlassFrameThickness="-1" /> ++ </WindowChrome.WindowChrome> <Window.Template> <ControlTemplate TargetType="Window"> <Border Padding="
64" Background="Transparent"> <Border CornerRadius="16" Background="White"> <Border.Effect> <DropShadowEffect BlurRadius="64" /> </Border.Effect> <ContentPresenter ClipToBounds="True" /> </Border> </Border> </ControlTemplate> </Window.Template> <Grid> <TextBlock FontSize="20" Foreground="#0083d0" TextAlignment="Center" VerticalAlignment="Center"> <Run Text="歡迎訪問呂毅的部落格" /> <LineBreak /> <Run Text="blog.walterlv.com" FontSize="64" FontWeight="Light" /> </TextBlock> </Grid> </Window>

 

網上流傳的主流方法

在網上流傳的主流方法中,AllowsTransparency="True" 都是一個必不可少的步驟,另外也需要 WindowStyle="None"。但是我一般都會極力反對大家這麼做,因為 AllowsTransparency="True" 會造成很嚴重的效能問題。

如果你有留意到我的其他部落格,你會發現我定製視窗樣式的時候都在極力避開設定此效能極差的屬性:

效能對比

既然特別說到效能,那也是口說無憑,我們要拿出資料來說話。

以下是我用來測試渲染效能所使用的例子:

相比於上面的例子來說,主要就是加了背景動畫效果,這可以用來測試幀率。

    <Window x:Class="Walterlv.Demo.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            WindowStyle="None" ResizeMode="CanMinimize"
            Title="walterlv demo" Height="450" Width="800">
        <WindowChrome.WindowChrome>
            <WindowChrome GlassFrameThickness="-1" />
        </WindowChrome.WindowChrome>
        <Window.Template>
            <ControlTemplate TargetType="Window">
                <Border Padding="64" Background="Transparent">
                    <Border CornerRadius="16" Background="White">
                        <Border.Effect>
                            <DropShadowEffect BlurRadius="64" />
                        </Border.Effect>
                        <ContentPresenter ClipToBounds="True" />
                    </Border>
                </Border>
            </ControlTemplate>
        </Window.Template>
        <Grid>
++          <Rectangle x:Name="BackgroundRectangle" Margin="0 16" Fill="#d0d1d6">
++              <Rectangle.RenderTransform>
++                  <TranslateTransform />
++              </Rectangle.RenderTransform>
++              <Rectangle.Triggers>
++                  <EventTrigger RoutedEvent="FrameworkElement.Loaded">
++                      <BeginStoryboard>
++                          <BeginStoryboard.Storyboard>
++                              <Storyboard RepeatBehavior="Forever">
++                                  <DoubleAnimation Storyboard.TargetName="BackgroundRectangle"
++                                                   Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
++                                                   From="800" To="-800" />
++                              </Storyboard>
++                          </BeginStoryboard.Storyboard>
++                      </BeginStoryboard>
++                  </EventTrigger>
++              </Rectangle.Triggers>
++          </Rectangle>
            <TextBlock FontSize="20" Foreground="#0083d0"
                   TextAlignment="Center" VerticalAlignment="Center">
                <Run Text="歡迎訪問呂毅的部落格" />
                <LineBreak />
                <Run Text="blog.walterlv.com" FontSize="64" FontWeight="Light" />
            </TextBlock>
        </Grid>
    </Window>

  

那麼效能資料表現如何呢?我們讓這個視窗在 2560×1080 的螢幕上全屏渲染,得出以下資料:

方案 WindowChrome AllowsTransparency
幀率(fps)數值越大越好,60 為最好 59 19
髒區重新整理率(rects/s)數值越大越好 117 38
視訊記憶體佔用(MB)數值越小越好 83.31 193.29
幀間目標渲染數(個)數值越大越好 2 1

另外,對於視訊記憶體的使用,如果我在 7680×2160 的螢幕上全屏渲染,WindowChrome 方案依然保持在 80+MB,而 AllowsTransparency 已經達到驚人的 800+MB 了。

可見,對於渲染效能,使用 WindowChrome 製作的背景透明異形視窗效能完虐使用 AllowsTransparency 製作的背景透明異形視窗,實際上跟完全沒有設定透明視窗的效能保持一致。

此效能差異的原理解讀,請參閱:

功能對比

既然 WindowChrome 方法在效能上完虐網上流傳的設定 AllowsTransparency 方法,那麼功能呢?

值得注意的是,由於在使用 WindowChrome 製作透明視窗的時候設定了 ResizeMode="None",所以你拖動視窗在螢幕頂部和左右兩邊的時候,Windows 不會再幫助你最大化視窗或者靠邊停靠視窗,於是你需要自行處理。不過視窗的標題欄拖動功能依然保留了下來,標題欄上的右鍵選單也是可以繼續使用的。

方案 WindowChrome AllowsTransparency
拖拽標題欄移動視窗 保留 自行實現
最小化最大化關閉按鈕 丟失 丟失
拖拽邊緣調整視窗大小 丟失 丟失
移動視窗到頂部可最大化 丟失 自行實現
拖拽最大化視窗標題欄還原視窗 保留 自行實現
移動視窗到螢幕兩邊可側邊停靠 丟失 自行實現
拖拽搖動視窗以最小化其他視窗 保留 自行實現
視窗開啟/關閉/最小化/最大化/還原動畫 丟失 丟失

表格中:

  • 保留 表示此功能無需任何處理即可繼續支援
  • 自行實現 表示此功能已消失,但僅需要一兩行程式碼即可補回功能
  • 丟失 表示此功能已消失,如需實現需要編寫大量程式碼

另外,以上表格僅針對滑鼠操作視窗。如果算上使用觸控來操作視窗,那麼所有標記為 自行實現 的都將變為 丟失。因為雖然你可以一句話補回功能,但在觸控操作下各種 Bug,你解不完……

這兩種實現的視窗之間還有一些功能上的區別:

方案 WindowChrome AllowsTransparency
點選穿透 在完全透明的部分點選依然點在自己的視窗上 在完全透明的部分點選會穿透到下面的其他視窗

然而,如果你希望在使用高效能的 WindowChrome 時也依然能點選穿透,那麼你需要使用到一點點的小技巧來繞過 WPF 對 WS_EX_LAYERED 視窗樣式的鎖定。請參見:WPF 製作支援點選穿透的高效能的透明背景異形視窗

本文會經常更新,請閱讀原文: https://blog.walterlv.com/post/wpf-transparent-window-without-allows-transparency.html ,以避免陳舊錯誤知識的誤導,同時有更好的閱讀體驗。