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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...