One of the best feature in wpf/xaml is ability to completely redesign a built-in control without any line of code. Most of the time you will just define a custom theme for a selection of useful controls. But it becomes more interesting when “hacking” an existing control to make something new.
In my day work I had to make a star rating control and I thought it would be interesting to reuse the slider control for that purpose. I also wanted it to be pure xaml. I use a shape proposed by Kiran Kumar which I slightly modified. Here is a visual (nothing really surprising though):
If you want to try, simply copy/paste the following piece of xaml to your favorite xaml editor (I used Kaxaml, which is a great tool):
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="#2d2d20">
<Page.Resources>
<SolidColorBrush x:Key="NoStar" Color="#29ffffff" />
<SolidColorBrush x:Key="Star" Color="Yellow" />
<Style
x:Key="StarRatingShapeStyle"
TargetType="Polygon">
<Setter Property="Width" Value="12" />
<Setter Property="Height" Value="12" />
<Setter Property="Fill" Value="{StaticResource NoStar}" />
<Setter Property="Points" Value="9,2 11,7 17,7 12,10 14,15 9,12 4,15 6,10 1,7 7,7" />
<Setter Property="Stretch" Value="Fill" />
<Setter Property="Stroke" Value="White" />
<Setter Property="StrokeLineJoin" Value="Round" />
<Setter Property="StrokeThickness" Value=".5" />
<Setter Property="IsHitTestVisible" Value="False" />
</Style>
<Style x:Key="RatingSliderRepeatButtonStyle" TargetType="{x:Type RepeatButton}">
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="0" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border
Background="{TemplateBinding Background}"
BorderThickness="0"
/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="RatingSliderThumbStyle" TargetType="{x:Type Thumb}">
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Focusable" Value="False" />
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="0" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border
Background="{TemplateBinding Background}"
BorderThickness="0"
/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style
x:Key="StarRatingSliderStyle"
TargetType="Slider">
<Setter Property="Minimum" Value="0" />
<Setter Property="Maximum" Value="5" />
<Setter Property="SmallChange" Value="1" />
<Setter Property="LargeChange" Value="1" />
<Setter Property="TickFrequency" Value="1" />
<Setter Property="IsSnapToTickEnabled" Value="True" />
<Setter Property="IsMoveToPointEnabled" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="Slider">
<Grid>
<Track
x:Name="PART_Track"
>
<Track.DecreaseRepeatButton>
<RepeatButton
Style="{StaticResource RatingSliderRepeatButtonStyle}"
Command="{x:Static Slider.DecreaseLarge}"
/>
</Track.DecreaseRepeatButton>
<Track.IncreaseRepeatButton>
<RepeatButton
Style="{StaticResource RatingSliderRepeatButtonStyle}"
Command="{x:Static Slider.IncreaseLarge}"
/>
</Track.IncreaseRepeatButton>
<Track.Thumb>
<Thumb
x:Name="PART_Thumb"
Style="{StaticResource RatingSliderThumbStyle}"
/>
</Track.Thumb>
</Track>
<StackPanel
Orientation="{TemplateBinding Orientation}">
<Polygon
x:Name="S5"
Style="{StaticResource StarRatingShapeStyle}"
/>
<Polygon
x:Name="S4"
Style="{StaticResource StarRatingShapeStyle}"
/>
<Polygon
x:Name="S3"
Style="{StaticResource StarRatingShapeStyle}"
/>
<Polygon
x:Name="S2"
Style="{StaticResource StarRatingShapeStyle}"
/>
<Polygon
x:Name="S1"
Style="{StaticResource StarRatingShapeStyle}"
/>
</StackPanel>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Value" Value="1">
<Setter TargetName="S1" Property="Fill" Value="{StaticResource Star}"/>
<Setter TargetName="S2" Property="Fill" Value="{StaticResource NoStar}"/>
<Setter TargetName="S3" Property="Fill" Value="{StaticResource NoStar}"/>
<Setter TargetName="S4" Property="Fill" Value="{StaticResource NoStar}"/>
<Setter TargetName="S5" Property="Fill" Value="{StaticResource NoStar}"/>
</Trigger>
<Trigger Property="Value" Value="2">
<Setter TargetName="S1" Property="Fill" Value="{StaticResource Star}"/>
<Setter TargetName="S2" Property="Fill" Value="{StaticResource Star}"/>
<Setter TargetName="S3" Property="Fill" Value="{StaticResource NoStar}"/>
<Setter TargetName="S4" Property="Fill" Value="{StaticResource NoStar}"/>
<Setter TargetName="S5" Property="Fill" Value="{StaticResource NoStar}"/>
</Trigger>
<Trigger Property="Value" Value="3">
<Setter TargetName="S1" Property="Fill" Value="{StaticResource Star}"/>
<Setter TargetName="S2" Property="Fill" Value="{StaticResource Star}"/>
<Setter TargetName="S3" Property="Fill" Value="{StaticResource Star}"/>
<Setter TargetName="S4" Property="Fill" Value="{StaticResource NoStar}"/>
<Setter TargetName="S5" Property="Fill" Value="{StaticResource NoStar}"/>
</Trigger>
<Trigger Property="Value" Value="4">
<Setter TargetName="S1" Property="Fill" Value="{StaticResource Star}"/>
<Setter TargetName="S2" Property="Fill" Value="{StaticResource Star}"/>
<Setter TargetName="S3" Property="Fill" Value="{StaticResource Star}"/>
<Setter TargetName="S4" Property="Fill" Value="{StaticResource Star}"/>
<Setter TargetName="S5" Property="Fill" Value="{StaticResource NoStar}"/>
</Trigger>
<Trigger Property="Value" Value="5">
<Setter TargetName="S1" Property="Fill" Value="{StaticResource Star}"/>
<Setter TargetName="S2" Property="Fill" Value="{StaticResource Star}"/>
<Setter TargetName="S3" Property="Fill" Value="{StaticResource Star}"/>
<Setter TargetName="S4" Property="Fill" Value="{StaticResource Star}"/>
<Setter TargetName="S5" Property="Fill" Value="{StaticResource Star}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<StackPanel>
<Slider
x:Name="Ranking"
Orientation="Vertical"
HorizontalAlignment="Center"
Background="Transparent"
Style="{StaticResource StarRatingSliderStyle}"
/>
<Slider
x:Name="Test"
Orientation="Horizontal"
Minimum="0"
Maximum="5"
Width="200"
Height="200"
IsSnapToTickEnabled="True"
TickFrequency="1"
Value="{Binding Value, ElementName=Ranking}"
/>
</StackPanel>
</Page>
This xaml implementation is not completely usable because of two small issues:
Depending on where you click on a star (above or below the middle) you got different behaviors. But it could be easily solved with a converter that would ceil or floor the value. Horizontal and vertical orientation is handled, but in horizontal orientation the order is reversed. I guess I should not use the Stackpanel orientation but rather a LayoutTransform.