WPF中自定義標題欄時窗體最大化處理之WindowChrome

来源:https://www.cnblogs.com/huaxia283611/archive/2018/11/11/wpf-maximize-windowchrome.html
-Advertisement-
Play Games

註意: 本文方法基礎是WindowChrome,而WindowChrome在 之後才集成發佈的。見: "WindowChrome Class" 在 中使用WindowChrome,需要安裝 "Ribbon" 來支持 "WindowChrome" ) 目前官方文檔的內容較為陳舊(但仍有參考價值),其中 ...


註意:

  • 本文方法基礎是WindowChrome,而WindowChrome在.NET Framework 4.5之後才集成發佈的。見:WindowChrome Class
  • .NET Framework 4.0中使用WindowChrome,需要安裝Ribbon來支持WindowChrome
  • 目前官方文檔的內容較為陳舊(但仍有參考價值),其中提到了SystemParameters2,這個應該是Ribbon里的東西,4.5想用可以安裝Xceed.Wpf.AvalonDock庫,這裡面有現成的Microsoft.Windows.Shell.SystemParameters2實現——當然,自己不怕麻煩,可以自己實現來獲取要用的系統變數。(一般用不著,4.5SystemParameters添加了許多系統變數的實現)

實現步驟

第一步:基本實現【保留系統基礎操作按鈕】

  • 添加Window的Style定義,並設置WindowChrome.WindowChrome屬性;
  • 設置WindowChrome標題欄:
    • CaptionHeight——主要用於拖動有效區;
    • GlassFrameThickness——影響標題欄系統按鈕顯示,0表示不使用系統按鈕【後面介紹】,-1表示用的系統預設值,如下示例則表示標題欄高度30;
    • 自定義窗體Title

註意:控制項模板中的定義:

  • 1、最外層Border背景無顏色,否則會覆蓋標題欄,看不到系統按鈕。
  • 2、內部佈局定義,使用Grid隔出30的標題欄高度,也可以直接對ContentPresenter設置Margin【主要是為了讓頂部顯示出標題欄】。
    <Style x:Key="WindowStyle1" TargetType="{x:Type Window}">
        <Setter Property="WindowChrome.WindowChrome">
            <Setter.Value>
                <WindowChrome CaptionHeight="30" GlassFrameThickness="0,30,0,0"/>
            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Window}">
                    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" >
                        <AdornerDecorator  >
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="30"/>
                                    <RowDefinition Height="*"/>
                                </Grid.RowDefinitions>
                                <ContentPresenter Grid.Row="1"/>
                                <TextBlock Text="{TemplateBinding Title}" HorizontalAlignment="Left" VerticalAlignment="Center" />
                            </Grid>
                        </AdornerDecorator>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

存在的問題: 最大化邊框問題 —— 會有部分溢出。

根據觀察,這個溢出寬度應該是8,此值的來源:(SystemParameters.MaximizedPrimaryScreenWidth - SystemParameters.WorkArea.Width)/2 ,高度也可以這樣計算。

第二步:優化邊界處理

  • 方法1:模板添加最大化觸發器,設置最大化時,內部佈局Margin設為8
  • 方法2:模板添加最大化觸發器,設置最大化時,限制佈局最大化的寬高最大值

不管使用哪種方法,最大化時,系統的標題欄高度都發生了變化,故,需要重新設置模板中定義的標題欄高度。

    <ControlTemplate TargetType="{x:Type Window}">
        <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" >
            <AdornerDecorator  >
                <Grid Name="win_content">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="30" x:Name="row_title"/>
                        <RowDefinition Height="*"/>
                    </Grid.RowDefinitions>
                    <ContentPresenter Grid.Row="1"/>
                    <TextBlock Text="{TemplateBinding Title}" HorizontalAlignment="Left" VerticalAlignment="Center" />
                </Grid>
            </AdornerDecorator>
        </Border>
        <ControlTemplate.Triggers>
            <Trigger Property="WindowState" Value="Maximized">
                <Setter Property="Margin" TargetName="win_content" Value="8"/>
                <!--二選一-->
                <Setter Property="MaxWidth" TargetName="win_content" Value="{Binding Source={x:Static SystemParameters.WorkArea},Path=Width}" />
                <Setter Property="MaxHeight" TargetName="win_content" Value="{Binding Source={x:Static SystemParameters.WorkArea},Path=Height}"/>
        
                <Setter Property="Height" TargetName="row_title" Value="22" />
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>

至此,都是系統標題欄和我們自定義內容組合使用,但這樣總有些邊邊角角要修正,下麵就完全自定義標題欄

第三步:完全自定義標題欄【即,不使用系統的操作按鈕】

  • 初步操作類似第一步,其中將GlassFrameThickness設置為0
  • 在內容定義部分添加自定義的標題欄,添加操作按鈕,並設置按鈕屬性WindowChrome.IsHitTestVisibleInChrome="True"

如果不設置WindowChrome.IsHitTestVisibleInChrome,則由於我們之前設置CaptionHeight,則這個區域內,按鈕將失效。
但是,也不能將整個標題欄佈局設置這個屬性,那樣會完全覆蓋系統標題欄的操作,如拖動效果,即CaptionHeight設置的那個區域。

    <!--樣式定義-->
    <Style x:Key="WindowStyle2" TargetType="{x:Type Window}">
        <Setter Property="WindowChrome.WindowChrome">
            <Setter.Value>
                <WindowChrome UseAeroCaptionButtons="False" GlassFrameThickness="0" CaptionHeight="30"  />
            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Window}">
                    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"  >
                        <AdornerDecorator >
                            <ContentPresenter x:Name="win_content" />
                        </AdornerDecorator>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="WindowState" Value="Maximized">
                            <Setter Property="Margin" TargetName="win_content" Value="8"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <!--定義標題欄-->
    <Grid Background="Red" >
        <Grid Height="30" HorizontalAlignment="Stretch" VerticalAlignment="Top" Background="Blue">
            <StackPanel Orientation="Horizontal" WindowChrome.IsHitTestVisibleInChrome="True" HorizontalAlignment="Right" >
                <Button Name="btn_Min" Content="—" ></Button>
                <Button Name="btn_Max" Content="☐" ></Button>
                <Button Name="btn_Close" Content="✕" ></Button>
            </StackPanel>
        </Grid>
    </Grid>
    //標題欄按鈕功能實現
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            this.btn_Min.Click += Btn_Min_Click;
            this.btn_Max.Click += Btn_Max_Click;
            this.btn_Close.Click += Btn_Close_Click;
        }

        private void Btn_Close_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        private void Btn_Max_Click(object sender, RoutedEventArgs e)
        {
            this.WindowState = WindowState.Maximized == this.WindowState ? WindowState.Normal : WindowState.Maximized;
        }

        private void Btn_Min_Click(object sender, RoutedEventArgs e)
        {
            this.WindowState = WindowState.Minimized;
        }
    }

關聯瞭解:
如果你平時也使用Visual Studio Code【VSCode】,文中的第一步和第三步,在vscode的設置中有對應效果:

  • 第一步 —— 將vscode用戶設置中Title Bar Style值設為native;
  • 第三步 —— 將vscode用戶設置中Title Bar Style值設為custom。

相關下載

點擊查看完整源代碼

解:奇葩史


落後要挨打,個人以前一直局限於老舊方式實現窗體處理,沒有跟進WPF技術改進,在此,非常感謝@vbfool的提醒。【之前說周末搞出來,自己這幾周浪了,沒研究。今天趁著別人雙11買買買,咱就擼擼代碼】


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.字元編碼過濾器 實現功能,在a.jsp中填寫用戶名提交到b.jsp,在b.jsp中讀取參數名。 a.jsp b.jsp 若需要讀取參數的頁面太多,需要在每一個頁面都添加<% request.setCharacterEncoding("UTF-8");%>,該方法行不通。字元編碼過濾器通過配置參數 ...
  • 餘近日複習C#之基礎知識,故作一隨筆,也是對此前幾篇博客中所記錄的傳值參數相關內容之彙總,還望諸位加以批評指正。 該博客包括以下內容: 傳值參數 引用參數 輸出參數 數組參數 具名參數 可選參數 擴展方法(this參數) 傳值參數 C#語言規範中道:“聲明時不帶修飾符的形參是值形參。一個值形參對應於 ...
  • 大數據處理 1、資料庫 垂直拆分:根據業務把表放到不同的資料庫,解決表之間的IO競爭 水平拆分:根據某種規則把單表數據分成多張表存儲,解決單表數據量大的問題 索引:根據業務場景創建合理的索引,如果數據量很小建議使用索引(300條以內) 索引使用場景: 動作描述 聚集索引 非聚集索引 主鍵列 是 是 ...
  • " 【.NET Core項目實戰 統一認證平臺】開篇及目錄索引 " 本篇將介紹如何擴展Ocelot中間件實現自定義網關,並使用2種不同資料庫來演示Ocelot配置信息存儲和動態更新功能,內容也是從實際設計出發來編寫我們自己的中間件,本文內容涵蓋設計思想內容和代碼內容,我希望園友們最好跟著我這個文章的 ...
  • 大數據處理 1、資料庫 垂直拆分:根據業務把表放到不同的資料庫,解決表之間的IO競爭 水平拆分:根據某種規則把單表數據分成多張表存儲,解決單表數據量大的問題 索引:根據業務場景創建合理的索引,如果數據量很小建議使用索引(300條以內) 索引使用場景: 動作描述 聚集索引 非聚集索引 主鍵列 是 是 ...
  • 第二章 C#語言快速熱身 ***********一.選擇結構**************** 1:if選擇結構與java的if選擇結構語法完全相同 2:switch選擇結構 ①:常量表達式的值可以是,string char int ②:case表達式的值不能重覆 ③:case語句段的break關鍵字 ...
  • 使用中文寫文章,當篇幅超過一定程度,必然會使用到諸如:“的”、“你”、“我”這樣的常用字。本類思想便是提取中文最常用的一百個字,使用中文世界常用編碼(主要有GBK、GB2312、GB18030、UTF-8、UTF-32、Unicode、BigEndianUnicode及UTF-7等)獲得其編碼位元組, ...
  • 我們在日常的開發任務中,經常遇到對不同的數據結構執行相同的操作,例如有一個方法傳入的參數可能是字元串也可能是數字,這樣的情況下,一般我們是直接傳入一個object類型的參數,以便於可以實現這樣的功能。例如以下代碼。 private static void Show(object a) { Conso ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...