如果仔細的看我們當前的TTT游戲,會發現Button對象並沒有完全為我們工作 。哪些TTT面板有內圓角?
圖5-14
這裡,我們真正需要的是能夠保持按鈕的行為,如支持內容和點擊事件,但 是我們想要接管這些按鈕的外觀。WPF允許這種方式,因為內在的控件創建的時 候是缺少外觀性的,例如,他們提供行為,但是外觀可以被完全包裝在客戶端控 件的外面。
還記得我們是如何使用數據模板,來為非可視化對象提供外觀的麼?我們能 夠使用控件模板對控件做同樣的事情,這將是一組StoryBoard,觸發器,以及大 多數重要的提供控件外觀的元素。
為了修復我們的按鈕外觀,我們創建了一個控件模板的資源。讓我們從示例 5-31出發,這是一個帶有簡單的矩形,和以後考慮如何顯示實際的按鈕內容。
示例5-31
<Window.Resources>
<ControlTemplate x:Key="ButtonTemplate">
<Rectangle />
</ControlTemplate>
<!-- let's just try one button for now -->
<Button Template="{StaticResource ButtonTemplate}" />
</Window.Resources>
圖5-15顯示了設置一個單獨按鈕的Template屬性的結果。
注意到按鈕過去樣子的痕跡(保留在圖5-15中)。不幸的是,看不到我們的 矩形的痕跡。問題在於,缺少一個顯示的填充設置,這個矩形的默認填充是透明 的,顯示grid的黑色背景。讓我們將其設置為喜歡的萬聖節顏色:
<ControlTemplate x:Key=”ButtonTemplate”>
<Rectangle Fill=”Orange” />
</ControlTemplate>
圖5-15
現在我們在這個地方,如圖5-16所示。
圖5-16
注意,拐角處是如何成直角的?而且,一旦你點擊了按鈕,你不會獲得壓下 的效果。(而且我沒有意味“一個不爽的感覺”)
5.7.1控件模板和樣式
注意到我們在控件模板上取得的一些成果,讓我們將其復制到其它按鈕上。 我們可以手動設置每個按鈕上的模板屬性,或者,作為最普通的,我們可以用按 鈕的樣式包裝這個模板控件。如示例5-32。
示例5-32
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Fill="Orange" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<!-- No need to set the Template property for each button -->
<Button x:Name="cell00" />
正如示例5-32所示,模板屬性和使用樣式設置是一樣的。圖5-17顯示了結果 。
圖5-17
仍然,橙色是不和諧的,尤其是因為白色背景樣式上的設定。我們可以用模 板綁定來解決這個問題。
5.7.2模板綁定
為了回到我們的白色按鈕,我們可以硬編碼將矩形填充為白色,但是如果樣 式要改變它呢(正如在我們中斷的動畫)?取代以硬編碼填充矩形,我們使用模 板綁定來將模板應用到控件屬性中,正如示例5-33所示。
示例5-33
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="White" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate x:Key="ButtonTemplate">
<Rectangle Fill="{TemplateBinding Property=Background}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
模板綁定就像數據綁定,除了綁定的屬性來自被你替換了模板的控件(稱為 模板化的父級別)。在我們的情形中,像Background, HorizontalContentAlignment等等,是美麗的游戲,用來模板綁定自父級別。同 時,像數據綁定,模板綁定是相當小巧的——用來保持模板中的條目屬性是最新 的,隨著外界的屬性改變,如被樣式和動畫設置等等。舉例來說,圖5-18顯示了 混淆矩形的Fill屬性到按鈕的Background屬性的效果——仍然適當地通過我們的 點擊動畫和鼠標盤旋的行為。
圖5-18
盡管如此,我們還沒有徹底到達。如果我們將要改變圖畫樣品,這樣圖5-18 已經變為了一個可玩的游戲,我們不得不顯示所有的移動。為了這麼做,我們需 要一個內容推薦者。
5.7.3內容推薦者
如果你曾經被廣告牌或汽車站長椅上寫著的“這裡是你的廣告!”所驅動, 然後這就是所有你需要知道的理解內容推薦者。內容推薦者等價於WPF中的“這 裡是你的內容”,允許內容由插入的ContentContainer控件保持在運行期。
在我們的情形中,內容是可視化的PlayerMove對象。取代以復制所有的工作 到按鈕新的控件模板中,我們只想要去除它在正確的地方。內容推薦者的工作是 獲取內容——由內容模板化的父級別提供,以及所有必須要顯示的事物,包括樣 式,觸發器等等。內容推薦者可以添加到你的模板中——無論在哪裡看到的模板 (包括多次,如果它使你愉快,例如生成一個下拉陰影)。在我們的情形中,我 們在示例5-34中組成一個內容推薦者,使用第2章的技術在grid中放一個矩形。
示例5-34
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="White" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Rectangle Fill="{TemplateBinding Property=Background}" />
<ContentPresenter
Content="{TemplateBinding Property=ContentControl.Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
在示例5-34中,內容推薦者的Content屬性綁定到ContentControl.Content屬 性,為了內容成功使用。作為使用樣式,我們可以避免給模板綁定屬性名稱加上 類的前綴,通過在ContentTemplate元素上設置TargetAttribute屬性。
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Rectangle Fill="{TemplateBinding Property=Background}" />
<ContentPresenter
Content="{TemplateBinding Property=Content}" />
</Grid>
</ControlTemplate>
進一步,在恰當的位置使用TargetType屬性,你可以一起去除顯示地模板綁 定到Content,屬性上,同時它會進行自動設置。
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Rectangle Fill="{TemplateBinding Property=Background}" />
<!-- with TargetType set, the template binding for the -->
<!-- Content property is no longer required -- >
<ContentPresenter />
</Grid>
</ControlTemplate>
內容推薦者是我們需要的全部,使得我們的游戲回到具有功能性,正如圖5- 19所示。
圖5-19
5.7.4真實的工作
最後一小塊工作是獲取右間隙。由於內容推薦者沒有自身的Padding屬性,我 們不能直接綁定Padding屬性(它也沒有Background屬性,這是為什麼我們使用 Rectangle和其Fill屬性)。因為這些屬性並不匹配內容推薦者,你不得不找到 映射或者組合提供這些功能的元素。例如,padding是控件中一定數量的空白, 另一方面,Margin是控件周圍一定數量的空白。由於他們都是同樣的類型, System.Windows.Thickness,如果我們可以映射按鈕中的Padding到內容控件的 外面。我們的TTT游戲看起來就會很漂亮:
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="White" />
<Setter Property="Padding" Value="10,5" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Rectangle Fill="{TemplateBinding Property=Background}" />
<ContentPresenter
Content="{TemplateBinding Property=Content}"
Margin="{TemplateBinding Property=Padding}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
圖5-20顯示了我們最終的TTT變體。
圖5-20
就像Padding和Margin間的映射,建立一個元素提供給你想要的外觀,並且從 父級別的模板綁定到相應的屬性,將要做很多的工作來創建你自己的控件模板。