Skip Navigation LinksHome > View Post

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:

  1. The Slider ControlTemplate
  2. 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.

 
Josh Post By Josh Twist
4:12 AM
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 are closed for this post.

Posted by Rob @ 16 Jul 2009 6:57 AM
This gotcha is a fine example the consequence of Silverlight's lack of NameScopes.

Posted by Ole Jak @ 05 Oct 2009 1:50 PM
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 AM
very gooood :))

Posted by GAUTAM @ 15 Apr 2010 12:51 AM
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

Posted by Hrishi @ 21 Jan 2011 2:06 PM
@Ole Jak:
Can you explain how can we create slider like link you have shared?

http://dougmccune.com/blog/2007/01/21/draggable-slider-component-for-flex/

Posted by gj @ 09 May 2011 6:21 PM
this is exactly what i am looking for, but i need vertical
i change everything horizontal to vertical, margins, left to top,...
anyway, i cannot get it to work vertically. any suggestions, or does source for that exist?

Posted by Aj @ 25 May 2011 1:42 PM
Thanks for the post. I needed such a control for my application and tried using this. But when I have the Minimum value as negative it is not working. Any guess why.

Posted by Bob @ 08 Sep 2011 2:12 PM
The range slider doesn't work if I change the minimum to anything other than 0, could you please check why it only works for the slider range from zero? Thanks!

Posted by Yauhen Safrankou @ 25 Dec 2011 3:35 PM
First of all, I thank Josh for sharing such control. I easily integrated the Range Slider control into my application and managed to use it with Minimum value other than 0. The control works when you specify Minimum and Maximum in exactly the right order: first Maximum and then Minimum! For the explanation, please refer to http://dotnetbyexample.blogspot.com/2009/08/bind-silverlight-3-slider-value-minimum.html

© 2005 - 2014 Josh Twist - All Rights Reserved.