有時候使用ListBox這個控制項時,可能會想要讓ListBox中的每一個項目可以做一點外觀上的變化,並且可以讓我的滑鼠再移動過去時,可以改變外觀,而這篇就是要來介紹如何達成這樣的目的。
前言
有時候使用ListBox這個控制項時,可能會想要讓ListBox中的每一個項目可以做一點外觀上的變化,並且可以讓我的滑鼠再移動過去時,可以改變外觀,而這篇就是要來介紹如何達成這樣的目的。
修改ListBoxItem樣式
基本上我們想要在滑鼠滑過ListBox中的項目時,可以改變項目的外觀,我們便要對ListBoxItem下手;ListBox控制項中會有ListBoxItem作為顯示的項目,因此當我們滑過ListBox中的項目時,等於是要改變每一個ListBoxItem的外觀
那麼我們要如何改變ListBoxItem外觀呢?
1. 我們需要透過控制項範本-ControlTemplate來自訂,透過控制項範本ControlTemplate,我們可以完全改變控制項本身的視覺化結構(視覺樹Visual Tree)與他的行為,最基本的例子就是MSDN中的Button改變成Ellispe的外觀形狀。
而本篇提到的滑鼠移動,在Windows 8是一種狀態,而我們需要透過ControlTemplate來改變ListBoxItem預設時滑鼠滑過時這個狀態所影響的外觀。
2. 在Windows 8 中,滑鼠移過這個狀態被稱做「PointerOver」,所以我們需要再發生PointerOver時去改變
3. 再來,我們為了要修改外觀,會使用樣式(Style)幫便我們修改控制項的屬性來改變外觀。
如下程式碼,我要改變在滑鼠滑過時,ListBoxItem的文字與背景顏色改變的例子
<Page.Resouce>
<Style x:Key="ListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<!-- ControlTemplate 改變的是外觀與行為,透過Grid作為ListBoxItem的背景-->
<ControlTemplate TargetType="ListBoxItem">
<BorderName="listBoxBorder" Background="#2DC4A5">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<!-- 透過Grid 改變背景顏色,不可指定contentPresenter,因為contentPresenter針對的是內容,所以沒有Background-->
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="Background" Storyboard.TargetName="listBoxBorder">
<DiscreteObjectKeyFrame KeyTime="0"
Value="White" />
</ObjectAnimationUsingKeyFrames>
<!-- contentPresenter包含Foreground-->
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="Foreground" Storyboard.TargetName="contentPresenter">
<DiscreteObjectKeyFrame KeyTime="0"
Value="#2DC4A5" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="Background" Storyboard.TargetName="listBoxGrid">
<DiscreteObjectKeyFrame KeyTime="0"
Value="White" />
</ObjectAnimationUsingKeyFrames>
<!-- contentPresenter包含Foreground-->
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="Foreground" Storyboard.TargetName="contentPresenter">
<DiscreteObjectKeyFrame KeyTime="0"
Value="Black" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<!-- TemplateBinding是延伸標記,指的是把控制項範本的屬性繫結到listboxitem屬性,這邊是透過ListBox指定影響到-->
<ContentPresenter x:Name="contentPresenter"
Content="{TemplateBinding Content}"
ContentTransitions="{TemplateBinding ContentTransitions}"
ContentTemplate="{TemplateBinding ContentTemplate}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="{TemplateBinding Padding}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resouce>
在Page.Resouce中透過Style樣式來修改(樣式的部分可以看這篇來了解),因為要改的是ListBoxItem,所以TargetType指定ListBoxItem,而要改變控制項範本的關係,所以Setter Property設定要改換Temlate
而ControlTemplate 中會看到又包了一層Border控制項其實時原本ListBoxItem預設控制項的樣式,視覺樹中的一部分。
而我們如果要改變PointerOver狀態,便要透過VisualState來完成。
VisualState用來控制特定狀態時的外觀,所以會看到像<VisualState x:Name="Normal"/>的標籤,並且PointerOver是被歸類在CommonState中,每個控制項有很多種類的狀態,所以會在區分,透過VisualStateGroup標籤來分類。
在Windows 8中,要改變狀態的外觀會需要用到StoryBoard完成,所以在這裡會透過StoryBoard去完成文字與背景的顏色更換。
而在背景的部分;
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="Background" Storyboard.TargetName="listBoxBorder">
<DiscreteObjectKeyFrame KeyTime="0"Value="White" />
</ObjectAnimationUsingKeyFrames>
指定listBoxBorder名稱透過Border這個控制項本身背景屬性來做為更換。
而在文字的顏色部分,則可以使用ContentPresnter標籤來改變ListBoxItem中文字這個內容的樣式。
ContentPresnter本身是用來為XAML顯示內容資訊的元件,所以在ListBoxItem中所顯示的文字內容便可以透過ContentPresnter修改,因此這邊會看到Storyboard.TargetName="contentPresenter。
而在ContentPresenter中,因為其餘的屬性都與原先ListBoxItem的屬性一樣,而因為LisBox標籤中的屬性會影響ListBoxItem屬性,如Froeground、FontSize等等,所以等於這邊細節到的屬性都是ListBox標籤中的設定的屬性。
如下是XAML中的ListBox
<ListBox ItemContainerStyle="{StaticResource ListBoxItemStyle}" Foreground="White" FontSize="40" FontWeight="Bold" Background="#2DC4A5" Name="todoPageList" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="30,20,30,0" >
</ListBox>
最後,我們在把上面的Styel樣式中所設定的Key = “ListBoxItemStyle”指定到ListBox的一個ItemContainerStyle屬性,指定ListBoxItem的樣式與狀態。
完成的結果如下圖:
平常的樣子(Normal) 滑鼠滑到(PointerOver)
參考資料
Silverlight和Metro中ListBox样式的添加及使用
ListBoxItem styles and templates
Is there any problem with having a ContentPresenter in ListBoxItem.ContentTemplate?
文章中的敘述如有觀念不正確錯誤的部分,歡迎告知指正 謝謝 =)
另外要轉載請附上出處 感謝