Creating a Range Slider in Silverlight (and some of the binding gotchas in SL3)
Yesterday, I posted about how to
Create a Range Slider in WPF using composition within a UserControl.
Today, I want to do the same in Silverlight and
_try_ to use the same technique(s).
The obvious things that need to change are:
- The Slider ControlTemplate
- Removing the ElementName binding
I can hear you all now: "No! Josh! Silverlight 3 has ElementName binding support! You don't need to remove it! Nooooo!".
Sadly though, we can't use it the way I did in
yesterday's post. Let's recap the big tip I was using:
<UserControl x:Class="UserControlFun.RangeSlider"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="root">
<Slider x:Name="LowerSlider"
Minimum="{Binding ElementName=root, Path=Minimum}"
Maximum="{Binding ElementName=root, Path=Maximum}"
Value="{Binding ElementName=root, Path=LowerValue}" />
<Slider x:Name="UpperSlider"
Minimum="{Binding ElementName=root, Path=Minimum}"
Maximum="{Binding ElementName=root, Path=Maximum}"
Value="{Binding ElementName=root, Path=UpperValue}" />
</UserControl>
As you can see we give the UserControl element a name. This is supported in WPF but not recommended in Silverlight. This is because in Silverlight an element can only have one name and therefore if we specify a Name where we use the control, like this:
<local:RangeSlider x:Name="overridingName" />
the internal ElementName binding will now fail (silently) because we changed the name. One solution might be to *not* rename the element where it's used but there's another problem: we can only use this control once or we'll have two elements with the same name.
This is very sad and I hope this is fixed in future releases. However, there is a workaround that seems to achieve what I want to achieve.
The trick is to use ambient binding (no ElementName) by programmatically setting the DataContext in the control's constructor:
public RangeSlider()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(RangeSlider_Loaded);
LayoutRoot.DataContext = this;
}
Notice that we specifically don't set
this.DataContext = this;. That would break any bindings specified on the UserControl externally. Instead, we head for the first child of our UserControl - in this case, called 'LayoutRoot'.
Here's the Xaml for the UserControl:
<UserControl x:Class="UserControlFunSL3.RangeSlider"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Grid x:Name="LayoutRoot" VerticalAlignment="Top">
<!-- resources will go here -->
<Border BorderThickness="0,1,0,0" BorderBrush="Black" VerticalAlignment="Center" Height="1" Margin="5,0,5,0"/>
<Slider x:Name="LowerSlider"
Minimum="{Binding Minimum}"
Maximum="{Binding Maximum}"
Value="{Binding LowerValue, Mode=TwoWay}"
Margin="0,0,10,0"
Template="{StaticResource sliderTemplate}"
/>
<Slider x:Name="UpperSlider"
Minimum="{Binding Minimum}"
Maximum="{Binding Maximum}"
Value="{Binding UpperValue, Mode=TwoWay}"
Margin="10,0,0,0"
Template="{StaticResource sliderTemplate}"
/>
</Grid>
</UserControl>
Finally, we need our Silverlight specific Slider Template:
<Grid.Resources>
<ControlTemplate x:Key="buttonTemplate" TargetType="RepeatButton">
<!-- just empty-->
<Grid />
</ControlTemplate>
<ControlTemplate x:Key="sliderTemplate" TargetType="Slider">
<Grid x:Name="HorizontalTemplate" Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<RepeatButton Template="{StaticResource buttonTemplate}" IsTabStop="False" IsEnabled="False" x:Name="HorizontalTrackLargeChangeDecreaseRepeatButton" Grid.Column="0"/>
<Thumb IsTabStop="True" Height="18" x:Name="HorizontalThumb" Width="11" Grid.Column="1">
<Thumb.Template>
<ControlTemplate TargetType="Thumb">
<Rectangle Fill="Red"
Stroke="Black"
StrokeThickness="1" />
</ControlTemplate>
</Thumb.Template>
</Thumb>
<RepeatButton Template="{StaticResource buttonTemplate}" IsTabStop="False" IsEnabled="False" x:Name="HorizontalTrackLargeChangeIncreaseRepeatButton" Grid.Column="2"/>
</Grid>
</ControlTemplate>
</Grid.Resources>
And we're done. Almost everything else is the same as our WPF control. Why not have a play with the control right here:
Or, get the source here: Download Source (7KB). Usual disclaimers for demoware apply.

Post By
Josh Twist
04:12
16 Jul 2009
» Next Post:
Bindorama - Binding Craziness now for Silverlight too!
« Previous Post:
Creating a Range Slider in WPF (and other cool tips and tricks for UserControls)
Comments:
Posted by
Rob
@
16 Jul 2009
06:57
This gotcha is a fine example the consequence of Silverlight's lack of NameScopes.
Posted by
Ole Jak
@
05 Oct 2009
13:50
Maybe there should be a middle resizing point like here
http://dougmccune.com/blog/2007/01/21/draggable-slider-component-for-flex/
Posted by
andrew
@
07 Dec 2009
11:31
very gooood :))
Posted by
GAUTAM
@
15 Apr 2010
00:51
Hi
i am using this user control in application.i am facing problem in implementation .describing my scenerio..
i want to use range slider on dev.xaml in which already some filter is working besides all these range filter i wnat to use .on dev.xaml page i use the
<local:RangeSlider x:Name="slider" Margin="273,10,0,10" Minimum="0" Maximum="10" Height="27" HorizontalAlignment="Left" Width="136" />
on the usercontrol page range.xaml event are there on valuechanged but i want to use this event on my dev.xaml page how i will do this ...
Thanks