一.前言
申明:WPF自定義控件與樣式是一個系列文章,前后是有些關聯的,但大多是按照由簡到繁的順序逐步發布的等,若有不明白的地方可以參考本系列前面的文章,文末附有部分文章鏈接。
本文主要內容:
- ScrollViewer的樣式拆解及基本樣式定義;
- ListBox集合控件的樣式定義;
二.ScrollViewer自定義樣式
ScrollViewer在各種列表、集合控件中廣泛使用的基礎組建,先看看效果圖:
如上圖,ScrollViewer簡單來說分兩部分,一個橫向的滾動條,一個垂直滾動條,兩個樣式、模板、功能都基本一樣,他們都是ScrollBar。以垂直滾動條為例,分解一下,分解圖:
- 1:向上滑動的按鈕,用RepeatButton實現功能;
- 2:上部分滑塊,功能同1,也是一個RepeatButton來實現的;
- 3:中間可拖動滑塊,用一個Thumb來實現;
- 4:下部分滑塊,和5功能一樣,向下滑動,用一個RepeatButton來實現;
- 5:向下滑動的按鈕,用RepeatButton實現功能;
上面實現的是一個標准的垂直滑動條ScrollBar組成,實際可用根據需求定制,實現不同效果的滑動效果。以上各部分的樣式代碼:

<sys:Double x:Key="ScrollBarSize">12</sys:Double> <!--滾動兩邊按鈕樣式--> <Style x:Key="ScrollBarButton" TargetType="{x:Type RepeatButton}"> <Setter Property="Background" Value="Transparent"></Setter> <Setter Property="Foreground" Value="{StaticResource TextForeground}"></Setter> <Setter Property="VerticalAlignment" Value="Center"></Setter> <Setter Property="HorizontalAlignment" Value="Center"></Setter> <Setter Property="Width" Value="auto"></Setter> <Setter Property="Height" Value="auto"></Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type RepeatButton}"> <TextBlock x:Name="FIcon" FontSize="12" Text="{TemplateBinding local:ControlAttachProperty.FIcon}" Margin="1" Style="{StaticResource FIcon}" /> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Foreground" Value="{StaticResource MouseOverForeground}" TargetName="FIcon"/> </Trigger> <Trigger Property="IsPressed" Value="True"> <Setter Property="Foreground" Value="{StaticResource PressedForeground}" TargetName="FIcon"/> </Trigger> <Trigger Property="IsEnabled" Value="false"> <Setter Property="Opacity" Value="0.5" TargetName="FIcon"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <!--滾動條滑塊兩邊按鈕樣式--> <Style x:Key="ScrollBarTrackButton" TargetType="{x:Type RepeatButton}"> <Setter Property="Background" Value="Transparent"></Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type RepeatButton}"> <Border Background="Transparent"></Border> </ControlTemplate> </Setter.Value> </Setter> </Style> <!--滾動條滑塊樣式--> <ControlTemplate x:Key="ThumbTemplate" TargetType="Thumb"> <Grid> <Border x:Name="Bg" CornerRadius="4" Margin="2" SnapsToDevicePixels="True" Background="{StaticResource ScrollBarForeround}"> <!--<Border.Background> <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1"> <GradientStop Color="#C7C0C0" Offset="0.15"/> <GradientStop Color="#AFA9A9" Offset=".5"/> <GradientStop Color="#989494" Offset=".5"/> <GradientStop Color="#858585" Offset="1"/> </LinearGradientBrush> </Border.Background>--> </Border> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="{StaticResource MouseOverForeground}" TargetName="Bg"></Setter> </Trigger> <Trigger Property="IsEnabled" Value="False"> <Setter Property="Opacity" Value="0.5" TargetName="Bg"></Setter> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> <!--水平滾滾動條模板--> <ControlTemplate x:Key="HorizontalScrollBar" TargetType="{x:Type ScrollBar}"> <Grid x:Name="HorizontalRoot" Height="{TemplateBinding Height}"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <!--外部背景,好像不用更好看--> <!--<Border x:Name="Bg" Grid.Column="0" Grid.ColumnSpan="3" CornerRadius="0" Opacity="0" Background="#858585"/>--> <!--內部背景--> <Border x:Name="BgInner" Grid.Column="1" Margin="0" SnapsToDevicePixels="True" Opacity="0.3" CornerRadius="6" Background="{StaticResource ScrollBarBackground}"/> <!--左按鈕--> <Border Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Center"> <RepeatButton local:ControlAttachProperty.FIcon="" Style="{StaticResource ScrollBarButton}" x:Name="HorizontalSmallDecrease" IsTabStop="False" Interval="50" Margin="0,1,0,0" Command="ScrollBar.LineLeftCommand"/> </Border> <!--中間滑動區域--> <Track x:Name="PART_Track" IsDirectionReversed="False" Grid.Column="1"> <!--左滑塊--> <Track.DecreaseRepeatButton> <RepeatButton x:Name="HorizontalLargeDecrease" Command="ScrollBar.PageLeftCommand" IsTabStop="False" Interval="50" Style="{DynamicResource ScrollBarTrackButton}" /> </Track.DecreaseRepeatButton> <!--中間滑塊 Margin="1" VerticalAlignment="Center" VerticalContentAlignment="Center" --> <Track.Thumb> <Thumb Template="{StaticResource ThumbTemplate}" /> </Track.Thumb> <!--右滑塊--> <Track.IncreaseRepeatButton> <RepeatButton x:Name="HorizontalLargeIncrease" Command="ScrollBar.PageRightCommand" IsTabStop="False" Interval="50" Style="{DynamicResource ScrollBarTrackButton}" /> </Track.IncreaseRepeatButton> </Track> <!--右按鈕--> <Border Grid.Column="2" VerticalAlignment="Center" HorizontalAlignment="Center"> <RepeatButton local:ControlAttachProperty.FIcon="" Style="{StaticResource ScrollBarButton}" IsTabStop="False" Interval="50" Margin="0,1,0,0" Command="ScrollBar.LineRightCommand"/> </Border> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="true"> <Setter TargetName="BgInner" Property="Opacity" Value="0.5"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> <!--垂直滾滾動條模板--> <ControlTemplate x:Key="VerticalScrollBar" TargetType="{x:Type ScrollBar}"> <Grid x:Name="VerticalRoot" Height="{TemplateBinding Height}"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <!--外部背景,好像不用更好看--> <!--<Border x:Name="Bg" Grid.Row="0" Grid.RowSpan="3" CornerRadius="0" Opacity="0" Background="#858585"/>--> <!--內部背景--> <Border x:Name="BgInner" Grid.Row="1" Margin="0" CornerRadius="6" SnapsToDevicePixels ="True" Opacity="0.3" Background="{StaticResource ScrollBarBackground}"/> <!--上按鈕--> <Border Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Center" x:Name="VerticalSmallDecrease"> <RepeatButton local:ControlAttachProperty.FIcon="" Style="{StaticResource ScrollBarButton}" IsTabStop="False" Interval="50" Margin="0" Command="ScrollBar.LineUpCommand"/> </Border> <!--中間滑動區域--> <Track x:Name="PART_Track" IsDirectionReversed="true" Grid.Row="1"> <!--上滑塊--> <Track.DecreaseRepeatButton> <RepeatButton x:Name="HorizontalLargeDecrease" Command="ScrollBar.PageUpCommand" IsTabStop="False" Interval="50" Style="{DynamicResource ScrollBarTrackButton}" /> </Track.DecreaseRepeatButton> <!--中間滑塊--> <Track.Thumb> <Thumb Template="{StaticResource ThumbTemplate}" MinHeight="10"/> </Track.Thumb> <!--下滑塊--> <Track.IncreaseRepeatButton> <RepeatButton x:Name="HorizontalLargeIncrease" Command="ScrollBar.PageDownCommand" IsTabStop="False" Interval="50" Style="{DynamicResource ScrollBarTrackButton}" /> </Track.IncreaseRepeatButton> </Track> <!--下按鈕--> <Border Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Center" x:Name="VerticalSmallIncrease"> <RepeatButton local:ControlAttachProperty.FIcon="" Style="{StaticResource ScrollBarButton}" IsTabStop="False" Interval="50" Margin="0" Command="ScrollBar.LineDownCommand"/> </Border> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="true"> <Setter TargetName="BgInner" Property="Opacity" Value="0.5"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> <!--ScrollBar樣式--> <Style x:Key="DefaultScrollBar" TargetType="{x:Type ScrollBar}"> <Setter Property="SnapsToDevicePixels" Value="True" /> <Setter Property="OverridesDefaultStyle" Value="true" /> <Style.Triggers> <Trigger Property="Orientation" Value="Horizontal"> <Setter Property="Template" Value="{StaticResource HorizontalScrollBar}" /> <Setter Property="Height" Value="{StaticResource ScrollBarSize}" /> </Trigger> <Trigger Property="Orientation" Value="Vertical"> <Setter Property="Template" Value="{StaticResource VerticalScrollBar}" /> <Setter Property="Width" Value="{StaticResource ScrollBarSize}" /> </Trigger> </Style.Triggers> </Style>
最后ScrollViewer的樣式如下,其實就是兩個 ScrollBar組成:
<!--ScrollViewer樣式--> <Style x:Key="DefaultScrollViewer" TargetType="{x:Type ScrollViewer}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ScrollViewer}"> <Grid x:Name="Grid" Background="{TemplateBinding Background}"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" x:Name="leftColumn" /> <ColumnDefinition Width="Auto" x:Name="rightColumn" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Margin="{TemplateBinding Padding}" Grid.Row="0" Grid.Column="0" /> <!--垂直滾動條 --> <ScrollBar x:Name="PART_VerticalScrollBar" AutomationProperties.AutomationId="VerticalScrollBar" ViewportSize="{TemplateBinding ViewportHeight}" Cursor="Arrow" Grid.Column="1" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Grid.Row="0" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"/> <!--水平底部滾動條--> <ScrollBar x:Name="PART_HorizontalScrollBar" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Grid.Column="0" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" Grid.Row="1" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}" /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
使用很簡單,如果想通用,把上面定義的ScrollViewer設置為默認樣式即可:
<Style TargetType="{x:Type ScrollBar}" BasedOn="{StaticResource DefaultScrollBar}"></Style>
<Style TargetType="{x:Type ScrollViewer}" BasedOn="{StaticResource DefaultScrollViewer}"></Style>
三.ListBox樣式定義
ListBox是最基礎、常用的集合控件,效果:
ListBox的樣式比較簡單,包括兩部分:
- ListBoxItem項的樣式;
- ListBox的樣式;
完整代碼:
<Style x:Key="DefaultListBoxItem" TargetType="{x:Type ListBoxItem}"> <Setter Property="Foreground" Value="{StaticResource TextForeground}" /> <Setter Property="HorizontalContentAlignment" Value="Stretch" /> <!--<Setter Property="VerticalContentAlignment" Value="Center" />--> <Setter Property="MinHeight" Value="25" /> <Setter Property="Margin" Value="0" /> <Setter Property="Background" Value="Transparent" /> <Setter Property="Padding" Value="3,0,0,0" /> <Setter Property="SnapsToDevicePixels" Value="True" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBoxItem}"> <Border x:Name="Border" Background="{TemplateBinding Background}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"> <ContentPresenter Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" /> </Border> <ControlTemplate.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter TargetName="Border" Property="Background" Value="{StaticResource ItemSelectedBackground}" /> <Setter Property="Foreground" Value="{StaticResource ItemSelectedForeground}" /> </Trigger> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="Border" Property="Background" Value="{StaticResource ItemMouseOverBackground}" /> <Setter Property="Foreground" Value="{StaticResource ItemMouseOverForeground}" /> </Trigger> <Trigger Property="IsEnabled" Value="False"> <Setter TargetName="Border" Property="Opacity" Value="{StaticResource DisableOpacity}" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
<Style x:Key="DefaultListBox" TargetType="{x:Type ListBox}"> <Setter Property="BorderBrush" Value="{StaticResource ControlBorderBrush}" /> <Setter Property="Background" Value="{StaticResource ItemsContentBackground}" /> <Setter Property="BorderThickness" Value="1" /> <Setter Property="ItemContainerStyle" Value="{StaticResource DefaultListBoxItem}"></Setter> <Setter Property="SnapsToDevicePixels" Value="True" /> <Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="False"></Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBox}"> <Border Name="Border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <ScrollViewer> <ItemsPresenter /> </ScrollViewer> </Border> <ControlTemplate.Triggers> <Trigger Property="IsEnabled" Value="False"> <Setter TargetName="Border" Property="Opacity" Value="{StaticResource DisableOpacity}" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
ListBox默認是支持虛擬化的,當加載大數據時需要開啟虛擬化,或者定義一個虛擬化的樣式:
<!--支持虛擬化的ListBox--> <Style x:Key="VirtualListBox" TargetType="{x:Type ListBox}" BasedOn="{StaticResource DefaultListBox}"> <Setter Property="ScrollViewer.CanContentScroll" Value="True" /> <Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"></Setter> <Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling" /> <Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" /> </Style>
上面演示效果的代碼示例:
<ListBox Margin="5" SelectionMode="Multiple" > <ListBoxItem Style="{StaticResource RadioButtonListBoxItem}">男</ListBoxItem> <ListBoxItem Style="{StaticResource RadioButtonListBoxItem}">女</ListBoxItem> <ListBoxItem Style="{StaticResource RadioButtonListBoxItem}">其他</ListBoxItem> <CheckBox>2222333333333333333</CheckBox> <CheckBox>2222333333333333333</CheckBox> <CheckBox>2222333333333333333</CheckBox> <CheckBox>2222333333333333333</CheckBox> <CheckBox>2222333333333333333</CheckBox> <CheckBox>2222333333333333333</CheckBox> <CheckBox>2222333333333333333</CheckBox> <CheckBox>2222333333333333333</CheckBox> <TextBox Width="200"></TextBox> <ListBoxItem IsSelected="True">111</ListBoxItem> </ListBox> <ListBox Margin="5" Grid.Column="1"> <ListBoxItem Style="{StaticResource RadioButtonListBoxItem}">男</ListBoxItem> <ListBoxItem Style="{StaticResource RadioButtonListBoxItem}">女</ListBoxItem> <ListBoxItem Style="{StaticResource RadioButtonListBoxItem}">其他</ListBoxItem> </ListBox>
另外提供一個比較常用的需求,ListBox單選RadioButton效果樣式,如上圖右邊的那個ListBox效果:
<Style x:Key="RadioButtonListBoxItem" TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource DefaultListBoxItem}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBoxItem}"> <Border x:Name="Border" Background="{TemplateBinding Background}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"> <RadioButton IsChecked="{Binding IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"> <RadioButton.Content> <ContentPresenter Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" /> </RadioButton.Content> </RadioButton> </Border> <ControlTemplate.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter TargetName="Border" Property="Background" Value="{StaticResource ItemSelectedBackground}" /> <Setter Property="Foreground" Value="{StaticResource ItemSelectedForeground}" /> </Trigger> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="Border" Property="Background" Value="{StaticResource ItemMouseOverBackground}" /> <Setter Property="Foreground" Value="{StaticResource ItemMouseOverForeground}" /> </Trigger> <Trigger Property="IsEnabled" Value="False"> <Setter TargetName="Border" Property="Opacity" Value="{StaticResource DisableOpacity}" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
附錄:參考引用
WPF自定義控件與樣式(1)-矢量字體圖標(iconfont)
WPF自定義控件與樣式(3)-TextBox & RichTextBox & PasswordBox樣式、水印、Label標簽、功能擴展
WPF自定義控件與樣式(4)-CheckBox/RadioButton自定義樣式
WPF自定義控件與樣式(5)-Calendar/DatePicker日期控件自定義樣式及擴展
版權所有,文章來源:http://www.cnblogs.com/anding
個人能力有限,本文內容僅供學習、探討,歡迎指正、交流。