WPF之自定義窗體
使用wpf能夠輕鬆的製作華麗炫目的程式介面,但是其預設的窗體樣式太死板,在win7下看起來還不錯,在xp或2003下卻顯得很不協調;因此我們需要自定義窗體樣式,wpf應該如何自定義窗體呢?
最開始我想到的方法,也是最傳統的方法,就是是使用模板和樣式來自定義窗體,首先設定窗體的WindowStyle為None,然後在Template裡定義窗體佈局,還需要考慮標題欄拖動、雙擊最大化、右鍵系統選單、窗體操作按鈕,然後就是Resize邊框的放大,縮小,最後我也實現所有功能,但是也許我是個完美主義者,發現windows自帶的窗體Resize邊框拖動的時候會有虛線框,而我的方法卻是隨滑鼠移動而改變大小,於是又開始在google上找相關資料,終於找到最好用也是最高效的方法,就是通過wpf shell library的WindowChrome來實現,下面都是介紹如何使用WindowChrome建立自定義窗體。
最新的wpf shell library(v3.5)包含在微軟釋出的Microsoft Ribbon for WPF中,下載後安裝即可在專案中新增Microsoft.Windows.Shell引用;當然可以直接下載WPF Shell Integration Library,包含原始碼,這裡只介紹其中WindowChrome的用法,想知道更多功能或者WindowChrome如何實現的可以下載原始碼來自己研究。
下面將由淺到深介紹如何使用WindowChrome建立自定義窗體:
開發環境:VS2010/Blend4、VS2008
相關技術:WPF 基礎、樣式和模板、
示例一:Win7下透明玻璃窗體
2、開啟Vs2010 ,選擇建立WPF應用程式專案(注意在windows專案列表中我們會發現多了一個模板WPF Ribbon Application,但是這裡我們並不討論如何使用Ribbon,仍然選擇WPF應用程式)
3、新增專案引用,在.net引用列表中選擇Microsoft.Windows.Shell,如圖:
如果選擇你使用WPF Shell Integration Library,則需要在瀏覽標籤項裡選擇Microsoft.Windows.Shell所在的位置
4、為了體現區別,我們分別建立不同型別的子窗體來做例項,首先建立新的wpf窗體Windows1,在xaml中新增Shell名稱空間引用
<Window x:Class="SimpleWindowChrome.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shell="clr-namespace:Microsoft.Windows.Shell;assembly=Microsoft.Windows.Shell"
Title="Window2" Height="300" Width="300">
5、接著複製以下程式碼到Window定義中:
<shell:WindowChrome.WindowChrome>
<shell:WindowChrome GlassFrameThickness="-1" ResizeBorderThickness="4"
CaptionHeight="28" CornerRadius="0" />
shell:WindowChrome.WindowChrome>
這裡的WindowChrome實際上是一個附加屬性,通過它對設定Window的屬性,具體實現參考Shell原始碼WPF Shell Integration Library,WindowChrome的第一個屬性GlassFrameThickness是玻璃效果區域的高度,只有在開啟了Aero的玻璃效果才能看到,否則是系統預設的效果,GlassFrameThickness設定為-1表示對整個窗體起用玻璃效果;ResizeBorderThickness是Resize邊框的寬度;CaptionHeight是標題區域的高度,也就是窗體的操作區域,比如右鍵系統選單,操作按鈕,雙擊最大化還原等;CornerRadius是設定窗體的邊角的弧度。
加了這段程式碼後窗體就能實現點選標題拖動,右鍵系統選單,最大化,還原,拖動Resize邊框改變窗體大小,這樣就包含自定義窗體必須包含的最重要的要素,但是此時執行程式,我們發現整個窗體都是白色的,並沒有玻璃背景的效果,因為wpf窗體預設的背景是白色的,覆蓋了WindowChrome的效果,要解決很簡單,自定義Window的Template,在xaml中新增以下程式碼:
<Window.Template>
<ControlTemplate TargetType="{x:Type Window}">
<Border BorderThickness="1">
<DockPanel>
<Grid x:Name="WindowHeader" DockPanel.Dock="Top" Height="25">
<TextBlock Text="{TemplateBinding Title}"/>
Grid>
<ContentPresenter Margin="20,20,20,20"/>
DockPanel>
Border>
ControlTemplate>
Window.Template>
怎麼樣,是不是很簡單,當然win7下才有這種效果,xp或2003中會顯示為空白,為解決這個問題,下面我們再看看下面這個示例:
示例二:通用的窗體
在示例1的基礎上新增Wpf窗體Window2,新增Shell名稱空間和WindowChrome定義:
<shell:WindowChrome.WindowChrome>
<shell:WindowChrome GlassFrameThickness="0" ResizeBorderThickness="4"
CaptionHeight="28" CornerRadius="0" />
shell:WindowChrome.WindowChrome>
這裡我們將GlassFrameThickness設定為0,也就是去掉玻璃窗體效果,我們需要自己定義窗體顯示樣式,首先我們必須模擬win7下的透明玻璃效果,因為本人缺少一定的美工底子,這裡我用半透明顏色來代替玻璃效果,要讓wpf支援透明背景,我們必須將窗體的WindowStyle設定為"None",同時必須將AllowsTransparency設定為True(注意不能單獨設定AllowsTransparency為true,必須在WindowStyle="None"前提下)
接著在xaml中新增如下程式碼:
<Window.Template>
<ControlTemplate TargetType="{x:Type Window}">
<Border BorderThickness="1" BorderBrush="Black" Background="#CC6C779A">
<DockPanel>
<Grid x:Name="WindowHeader" DockPanel.Dock="Top" Height="25">
<DockPanel>
<Button DockPanel.Dock="Right" Foreground="Black"
shell:WindowChrome.IsHitTestVisibleInChrome="True"
Command="{x:Static shell:SystemCommands.CloseWindowCommand}"
Background="{x:Null}" BorderBrush="{x:Null}"
FontSize="16" Width="20" Content="X"/>
<TextBlock Text="{TemplateBinding Title}"/>
</DockPanel>
</Grid>
<ContentPresenter s/>
</DockPanel>
</Border>
</ControlTemplate>
</Window.Template>
這樣我們的窗體就具有了半透明背景,但是窗體操作的按鈕沒有了,需要我們自己新增按鈕,注意在CaptionHeight區域定義按鈕必須將其IsHitTestVisibleInChrome設定為True,否則它將變得不可點選;這裡我們新增一個關閉的按鈕,按鈕的Command繫結到路由命令shell:SystemCommands.CloseWindowCommand,因此需要給窗體繫結路由命令,在xaml中新增如下程式碼:
<Window.CommandBindings>
<CommandBinding Command="{x:Static shell:SystemCommands.CloseWindowCommand}"
Executed="_OnCloseWindowCommand"/>
</Window.CommandBindings>
在xaml.cs中新增事件處理方法:
private void _OnCloseWindowCommand(object sender, ExecutedRoutedEventArgs e)
{
Window _window = Window.GetWindow(this);
Microsoft.Windows.Shell.SystemCommands.CloseWindow(_window);
}
這樣我們點選關閉按鈕,就可以觸發OnCloseWindowCommand路由事件,在事件處理方法中關閉窗體,如果你不明白什麼是路由命令,參考瞭解 WPF 中的路由事件和命令;
OK,現在我們在Aero特效和無特效情況下執行得到同樣的效果:
示例三:建立窗體控制元件庫
方便起見,這裡我直接使用我在專案中建立好的自定義窗體控制元件,窗體外觀都在樣式模板中定義,我們可以根據需要修改窗體樣式,自定義控制元件使用方法:
首先在帖子最下方下載示例程式,在SimpleWindowChrome專案中新增CustomControl引用,然後新增wpf窗體window3,修改window3.xaml如下:
<controls:CustomChromeWindow xmlns:controls="clr-namespace:CustomControl;assembly=CustomControl"
x:Class="SimpleWindowChrome.Window3"
Title="Window3" Height="300" Width="300"
CornerRadius="10,10,5,5">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock TextWrapping="Wrap" Width="250" FontSize="15">
<TextBlock.Text>
引用自定義窗體控制元件,可以在WIN7/XP/2003上檢視
</TextBlock.Text>
</TextBlock>
</StackPanel>
</controls:CustomChromeWindow>
至於自定義控制元件部分,這裡就不貼程式碼了,大家可以帖子下方下載示例程式,關鍵地方我都在程式碼中有註釋;
示例中我儘可能多的使用xmal完成所需的功能,而儘量避免過多的後臺程式碼,因此對於初學wpf或者不習慣xaml者造成閱讀困難,但是我覺得xaml才是wpf的精髓所在,不明白者需要耐心閱讀,多上google查詢相關資料,本文示例下載:
更新下載VS2008版本示例,由於wpf3存在一個公所周知的Bug,就是在Style中不能設定Content的值,因此相對vs2010版本有一點點小改動,我在CaptionButton基類中添加了一個ControlTemplate型別的依賴項屬性,在CaptionButton繼承類物件中通過對ControlTemplate的設定來達到與設定Content同樣的效果,VS2008版本示例下載地址: