1. 程式人生 > >WPF之自定義窗體

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 基礎樣式和模板

依賴項屬性附加屬性WPF 中的路由事件和命令

    示例一:Win7下透明玻璃窗體

    2、開啟Vs2010 ,選擇建立WPF應用程式專案(注意在windows專案列表中我們會發現多了一個模板WPF Ribbon Application,但是這裡我們並不討論如何使用Ribbon,仍然選擇WPF應用程式)

    3、新增專案引用,在.net引用列表中選擇Microsoft.Windows.Shell,如圖:

    image

    如果選擇你使用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 LibraryWindowChrome的第一個屬性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下執行後,效果如下:image

    怎麼樣,是不是很簡單,當然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特效和無特效情況下執行得到同樣的效果:image

    示例三:建立窗體控制元件庫

    方便起見,這裡我直接使用我在專案中建立好的自定義窗體控制元件,窗體外觀都在樣式模板中定義,我們可以根據需要修改窗體樣式,自定義控制元件使用方法:

    首先在帖子最下方下載示例程式,在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>

    至於自定義控制元件部分,這裡就不貼程式碼了,大家可以帖子下方下載示例程式,關鍵地方我都在程式碼中有註釋;

    執行程式截圖:image

    示例中我儘可能多的使用xmal完成所需的功能,而儘量避免過多的後臺程式碼,因此對於初學wpf或者不習慣xaml者造成閱讀困難,但是我覺得xaml才是wpf的精髓所在,不明白者需要耐心閱讀,多上google查詢相關資料,本文示例下載:

   更新下載VS2008版本示例,由於wpf3存在一個公所周知的Bug,就是在Style中不能設定Content的值,因此相對vs2010版本有一點點小改動,我在CaptionButton基類中添加了一個ControlTemplate型別的依賴項屬性,在CaptionButton繼承類物件中通過對ControlTemplate的設定來達到與設定Content同樣的效果,VS2008版本示例下載地址: