[Windows 8 | XAML]改變滑鼠滑過(MouseOver)ListBox中ListBoxItem時的外觀

有時候使用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)

Normal                 PointerOver

 

 

參考資料

Silverlight和Metro中ListBox样式的添加及使用

ListBoxItem styles and templates

Is there any problem with having a ContentPresenter in ListBoxItem.ContentTemplate?

ListBoxItem 樣式與範本

 


 

文章中的敘述如有觀念不正確錯誤的部分,歡迎告知指正 謝謝 =)

另外要轉載請附上出處 感謝