Reputation: 876
I'm creating my own slider template and style. It's similar to the basic slider but the track bar contains circles (Ellipses) with a proper separation. The number of circles in the bar should be such that the slider thumb will stop exactly over them.
In a quick approximation, there should be Slider.Maximum - Slider.Minimum / StepSize Ellipses in the control. But, to my knowledge, there is no way to specify a variable number of anything from xaml, is there?
This is, in summary, the code that represents what I'd want to achieve:
<ControlTemplate x:Key="SliderHorizontal" TargetType="{x:Type Slider}">
<Grid Height="4">
<Border x:Name="TrackBackground">
<Rectangle x:Name="PART_SelectionRange" Fill="{StaticResource SliderThumb.Track.BackgroundSelected}"
HorizontalAlignment="Left" Margin="0 0 16 0" Visibility="Hidden"/>
</Border>
<Grid>
<!-- Have a variable ammount of column definitions and ellipses -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4"/>
<ColumnDefinition Width="*"/>
...
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="4"/>
</Grid.ColumnDefinitions>
<Ellipse Grid.Column="0" Fill="White"/>
...
<Ellipse Grid.Column="X" Fill="White"/>
</Grid>
<Track x:Name="PART_Track">
<Track.Thumb>
...
</Track.Thumb>
</Track>
</Grid>
<ControlTemplate.Triggers>
...
</ControlTemplate.Triggers>
</ControlTemplate>
My code so far allows me to use a fixed ammount of ellipses, which looks very bad when it doesn't match the values that are represented by a slider.
I'm new to WPF so I don't know what doing this from code-behind would entail. Can I simply create a class inheriting from Slider and add the Ellipses there? If so, could I get a simple example doing it?
Upvotes: 0
Views: 84
Reputation: 37059
An ItemsControl
can display a variable amount of something. Bind its ItemsSource
property to a collection of the things you want to display, and use its ItemTemplate
property to determine how they're displayed.
Here's a rough implementation that you should be able to fine tune. In particular, TickConverter
should be IMultiValueConverter
, with Minimum
, Maximum
, and TickFrequency
bound separately via a MultiBinding
-- that way, it'll get automagically reinvoked when any of those properties changes.
public class TickConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var slider = (Slider)value;
var tickOffsets = new List<double>();
var sliderRange = (slider.Maximum - slider.Minimum);
var tickcount = (int)Math.Floor(sliderRange / slider.TickFrequency);
return Enumerable.Range(0, tickcount);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Template:
<ControlTemplate TargetType="{x:Type Slider}" x:Key="SliderHorizontal">
<ControlTemplate.Resources>
<local:TickConverter x:Key="TickConverter" />
</ControlTemplate.Resources>
<Grid Height="4">
<Border x:Name="TrackBackground">
<Rectangle
x:Name="PART_SelectionRange"
HorizontalAlignment="Left"
Margin="0 0 16 0"
Visibility="Hidden"
/>
</Border>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ItemsControl
Grid.Column="0"
ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource TickConverter}}"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<!-- UniformGrid spaces items out evenly -->
<UniformGrid
Rows="1"
/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- Align left to fill in start tick for each interval -->
<Ellipse
HorizontalAlignment="Left"
Fill="DeepSkyBlue"
Width="3"
Height="4"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- And add the end tick -->
<Ellipse
Grid.Column="1"
HorizontalAlignment="Left"
Fill="DeepSkyBlue"
Width="3"
Height="4"
/>
</Grid>
<!--
I don't know what you were doing with the thumb, but you didn't ask about it
so I just ignored it.
-->
<Track x:Name="PART_Track" />
</Grid>
</ControlTemplate>
Upvotes: 3