WPF繪制自定義窗口


WPF是制作界面的一大利器,下面就用WPF模擬一下360的軟件管理界面,360軟件管理界面如下:

 

界面不難,主要有如下幾個要素:

  1. 窗體的圓角
  2. 自定義標題欄及按鈕
  3. 自定義狀態欄
  4. 窗體的半透明效果
  5. 窗體4周有一圈半透明陰影(抓的圖上看不出來)

實現思路很簡單,首先隱藏默認窗口的標題欄和邊框,然后用WPF的Border或Canvas等元素模擬定義窗體的標題欄、內容區和狀態欄。

 

具體實現如下:

第一步:定義義個窗口基類,繼承自Window,在構造函數中加載自定義窗口的樣式文件,代碼如下:

View Code
public partial class WindowBase:Window

{

InitializeTheme();

}

private void InitializeTheme()

{

string themeName = ConfigManage.CurrentTheme;//樣式所在的文件夾

App.Current.Resources.MergedDictionaries.Add(Application.LoadComponent(new Uri(string.Format("../Theme/{0}/WindowBaseStyle.xaml", themeName), UriKind.Relative)) as ResourceDictionary);

}

 

  接下來,就可以在WindowBaseStyle.xaml樣式文件中定義窗口元素及樣式了。

 

  第二步:重寫窗口模板

  因為Window和Button等控件一樣,實際是繼承自System.Windows.Controls.Control類,所以可以通過使用 ControlTemplate 自定義控件的外觀 ,代碼如下:

View Code
<!--基窗口樣式-->

<Style x:Key="BaseWindowStyle" TargetType="{x:Type Window}">

<Setter Property="Template" Value="{StaticResource BaseWindowControlTemplate}"/>

<Setter Property="Background"

Value="Transparent" />

<Setter Property="WindowStyle"

Value="None" />

<Setter Property="AllowsTransparency"

Value="True" />

</Style>

<!--基窗口模板-->

<ControlTemplate x:Key="BaseWindowControlTemplate" TargetType="{x:Type Window}">

<Grid Width="{Binding ElementName=w, Path=Width}" Height="{Binding ElementName=w, Path=Height}">

<!—第四步介紹如下Border元素的作用—>

<Border BorderThickness="5" CornerRadius="6" BorderBrush="#000000" Opacity=".08"></Border>

<!—第三步介紹borderBg元素的作用—>

<Border x:Name="borderBg" Margin="5" Background="#000000" BorderBrush="#ffffff" Opacity=".8" BorderThickness="2" CornerRadius="{StaticResource winCorner}" Style="{StaticResource winStyle}">

<!—定義窗口的元素,Grid的四行分別為標題欄、內容、狀態欄上的橫線、狀態欄-->

<Grid>

<Grid.RowDefinitions>

<RowDefinition Height="auto"></RowDefinition>

<RowDefinition Height="*"></RowDefinition>

<RowDefinition Height="1"></RowDefinition>

<RowDefinition Height="30"></RowDefinition>

</Grid.RowDefinitions>

<Border Grid.Row="0" Background="#4f535d" CornerRadius="{StaticResource winTitleCorner}" Style="{StaticResource titleStyle}"></Border>

<Canvas Grid.Row="2" Background="#42464d"></Canvas>

<Border Grid.Row="3" CornerRadius="{StaticResource winStatusCorner}"></Border>

</Grid>

</Border>

 

<Grid Margin="7">

<Grid.RowDefinitions>

<RowDefinition Height="auto"></RowDefinition>

<RowDefinition Height="*"></RowDefinition>

<RowDefinition Height="1"></RowDefinition>

<RowDefinition Height="30"></RowDefinition>

</Grid.RowDefinitions>

<!--標題欄框-->

 

<Border x:Name="borderTitle" Grid.Row="0" CornerRadius="{StaticResource winTitleCorner}" Style="{StaticResource titleStyle}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">

<Grid Background="Transparent">

<TextBlock Text="{Binding ElementName=w, Path=Title}" Foreground="White" Opacity=".75" HorizontalAlignment="Left"></TextBlock>

<StackPanel HorizontalAlignment="Right" VerticalAlignment="Top" Visibility="Hidden"

Orientation="Horizontal">

<!--關閉按鈕-->

<Button x:Name="btnMin" Style="{StaticResource minBtnStyle}"></Button>

<Button x:Name="btnClose" Style="{StaticResource closeBtnStyle}"></Button>

</StackPanel>

</Grid>

</Border>

<!--內容-->

<Grid x:Name="gridContent" Grid.Row="1">

<ContentPresenter />

</Grid>

 

<Border Grid.Row="3" CornerRadius="{StaticResource winStatusCorner}">

<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" >

<Button x:Name="btnYes" Style="{StaticResource btnStyle}"></Button>

<Button x:Name="btnNo" Style="{StaticResource btnStyle}"></Button>

</StackPanel>

</Border>

</Grid>

</Grid>

</ControlTemplate>

 

  需要注意的是,實際代碼中應該將BaseWindowControlTemplate的定義放到BaseWindowStyle的前邊,否則會拋出異常。

 

  第三步:實現半透明

  用Js實現過遮罩型的模態窗口的同學都知道,如果將元素如按鈕直接放到一個半透明的Div中,會發現按鈕本身也半透明了,這顯然不是我們所希望的,我們只需要背景半透明,元素本身透明度不受影響,實現的思路應該是這樣,將按鈕和div平級,利用定位屬性將按鈕和半透明的div保持相對位置,而不是將按鈕放入Div中。

  WPF中,實現方式和其一樣,請看第二步中的x:Name="borderBg"的Border元素的定義。它和后面的Grid元素定義的實際窗口是同級的。

 

  第四步:實現窗體四周半透明的環繞

  這個先簡單用一個半透明具有一定寬度的Border來模擬,代碼如步驟二中所示,后續考慮做成類似外發光的效果。

 

  第五步:實現最小化、關閉、拖動等事件

  在WindowBase的構造函數中,添加如下代碼:

  

View Code
this.Loaded += delegate
{
            InitializeEvent();
};
private void InitializeEvent()
{
 ControlTemplate baseWindowTemplate = (ControlTemplate)App.Current.Resources["BaseWindowControlTemplate"];

            Border borderTitle = (Border)baseWindowTemplate.FindName("borderTitle", this);
            Button closeBtn = (Button)baseWindowTemplate.FindName("btnClose", this);
            Button minBtn = (Button)baseWindowTemplate.FindName("btnMin", this);
            YesButton = (Button)baseWindowTemplate.FindName("btnYes", this);
            NoButton = (Button)baseWindowTemplate.FindName("btnNo", this);

            minBtn.Click += delegate
            {
                MinWin();
            };

            closeBtn.Click += delegate
            {
                this.Close();
            };

            borderTitle.MouseMove += delegate(object sender, MouseEventArgs e)
            {
                if (e.LeftButton == MouseButtonState.Pressed)
                {
                    this.DragMove();
                }
            };
}

 

  注意, InitializeEvent方法必須放到Loaded事件處理函數中,否者會找不到按鈕。

 

  第六步: 定義最小化,關閉按鈕的樣式

  實際上也是定義Template屬性,定義方法類似於窗口模板的定義,詳細見附件中完整代碼。

 

  到此,自定義窗口的基類就打造完成,完整代碼見附件。

 

  使用方法很簡單,新建一個WPF窗口后將窗體的基類改成上面定義的WindowBase,同時注意將Xmal文件的根元素改成如下形式:

  

<my:WindowBase xmlns:my="clr-namespace:Ezhu.WPFWin"

 

  運行效果如下圖所示:

 

 

  附件:自定義窗口代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM