Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating a discrete value slider in Silverlight

0.00/5 (No votes)
22 Dec 2013 1  
The article shows the proecess of creating and using a discrete value slider for Silverlight.

Introduction

Having spent some time with WPF, I thought of giving Silverlight a try. It is often best to start with simple little fun-stuff, right? So I created a couple of small puzzle applications (no longer online).

Update & Warning: The Silverlight games worked great. However, due to limited audience of SL, I made a decision and learned how to program in HTML5. Several weeks of wasted effort taught me that you should NOT build games using Silverlight. That technology is dead for most purposes. However, I am not deleting this article, as it might still be useful for someone. Also, the general techniques can be applied in other languages.

Here's a screenshot to show how the slider looked like (Again: No longer online!):

Using the provided slider

Using the slider is very easy. You have to give an array of integer values to be used as selection points. After that whenever the user changes the selection using the thumb, you get a PropertyChanged event. In the event handler code, you can retrieve the current selection by using the Value property of the slider.

Here is how the example project creates 3 sliders (please refer to the download for full source):

<StackPanel Orientation="Horizontal">
    <StackPanel Margin="100,10,10,20" Width="150">
        <local:DiscreteSlider Grid.Column="0" x:Name="discreteSlider1" 
                                PropertyChanged="discreteSlider1_ValueChanged"></local:DiscreteSlider>
        <TextBlock Margin="0,20,0,0" Name="slider1Text" 
          Grid.Column="0" Grid.Row="1">New Value:</TextBlock>
    </StackPanel>
    <StackPanel Margin="100,10,10,20"  Width="150">
        <local:DiscreteSlider Grid.Column="1" x:Name="discreteSlider2" SliderBrush="#FFFF00FF" 
                                ThumbBrush="#FFFFFF00" ThumbEffect="{StaticResource thumbEffect}" 
                                PropertyChanged="discreteSlider2_ValueChanged"></local:DiscreteSlider>
        <TextBlock Margin="0,20,0,0" Name="slider2Text" 
          Grid.Column="1" Grid.Row="1">New Value:</TextBlock>
    </StackPanel>
    <StackPanel Margin="100,10,10,20"  Width="150">
        <local:DiscreteSlider Grid.Column="2" x:Name="discreteSlider3" 
                                SliderBrush="{StaticResource greenToRedBrush}" 
                                PropertyChanged="discreteSlider3_ValueChanged"></local:DiscreteSlider>
        <TextBlock Margin="0,20,0,0" Name="slider3Text" 
          Grid.Column="2" Grid.Row="1">New Value:</TextBlock>
    </StackPanel>
</StackPanel> 

Please note the 'local:' prefix here. You will have to set it to the assembly that contains the slider control. Here it points to same assembly, as both the slider and the referring code are in the same assembly.

In the event handlers of the sample project, we do nothing except showing the selected value back using TextBlock objects below the sliders (which, BTW, is superfluous, since the slider already shows the selected value in a fancy way!). Here is what the sample does for the first event handler:

private void discreteSlider1_ValueChanged(object sender, PropertyChangedEventArgs e)
{
    slider1Text.Text = "New Value: " + discreteSlider1.Value;
}

Design

The beauty of creating simple little gadgets for your needs is simplicity. You do not create a thousand components like a general purpose control does on account of not knowing the requirements in advance. We know what we want to do and only implement just as much!

Keeping this philosophy in mind our slider contains just a few components, besides a couple of container Canvases, including a:

  • Rectangle: For the background bar.
  • Ellipse: For the Thumb.
  • TextBlock: To show the current value.
  • Path: To draw a fancy outline around the value TextBlock

A picture may help here:

Customization Options

The current implementation provides the following customization options:

  • SliderBrush: You can use this property to set the brush used to draw the surface of the background rectangle.
  • ThumbBrush: Use this to color the Thumb.
  • ThumbEffect: Use this to apply an Effect (like DropShadowEffect) to the Thumb.

Step by Step Guide for Beginners

For the designing, I used MS Blend for VS 2012 and for coding and debugging I used VS 2012. For people new to WPF/Silverlight it can be a very good exercise to create this kind of a thing from scratch. So I am giving the step by step process of how you can create it on your own.

First the designing part:

  1. Create a new Silverlight-5 application in VS 2010 or 12 and open it in Blend.
  2. Add a new User Control and give it a name.
  3. Change the type of the root panel from 'Grid' to Canvas (so we can do absolute positioning)
  4. We are using fixed sizes, so set Width and Height of the root Canvas to 100 and 310 pixels respectively.
  5. Drag the following from the Assets panel to be the children of the root (Please note that the hard coded values are just for reference and not carved in stone. You can change them according to your taste/needs):
    • Canvas for the ticks named : With=10 and Height=280 at coordinates 0 and 14
    • Rectangle named for the background bar: Width=12 and Height=300 at coordinates 14 and 2.
    • Canvas for the Thumb and value overlay: Width=88 and Height=30 at coordinates 8 and 100.
  6. Name the 3 new components as ticksCanvas, backgroundRect and sliderCanvas respectively, so we can later reference them in code.
  7. Now drag an Ellipse to the last Canvas we created in step-5 (i.e. sliderCanvas). This will be the thumb so size it according. For the overlay design, create a shape (for example a rounded Rectangle) and to show the value, add a TextBlock.
  8. Give the thumb Ellipse a name so we can reference them later in code.

This is how things look in Blend after completing the above steps (I colored everytihng to give you an idea):

After some polishing and event attachment the final XAML becomes like this:

<Canvas x:Name="LayoutRoot" Background="#FFEEEEEE"  
  MouseMove="LayoutRoot_MouseMove" Width="96" Height="300">
    <Canvas Name="ticksCanvas" Width="10" Height="278" 
                Canvas.Top="14" UseLayoutRounding="True"/>
    <Rectangle Name="backgroundRect" Width="12" Height="300" Fill="{Binding SliderBrush}" 
                Canvas.Top="2" Canvas.Left="14" Stroke="#FF777777" 
                RadiusX="6" RadiusY="6"></Rectangle>
    <Canvas Name="sliderCanvas" Width="87" Height="30" 
                Canvas.Left="8" Canvas.Top="278">
        <Ellipse x:Name="sliderThumb" Stroke="#FF777777" 
                Cursor="Hand" Fill="{Binding ThumbBrush}" Effect="{Binding ThumbEffect}"
                MouseLeftButtonDown="Button_MouseLeftButtonDown" 
                MouseLeftButtonUp="Button_MouseLeftButtonUp"  
                Stretch="Fill" Height="24" 
                UseLayoutRounding="False" Width="24" Canvas.Top="2">
        </Ellipse>
        <Path Data="M0,20 L25,0 L71,0 L73.4167,0.25 L75.0833,0.75 L76.5833,1.25 L77.9167,
                      2.08333 L78.9167,3.08333 L79.75,4.41667 L80,6 L80,34 L79.75,35.4167 L79.4167,
                      36.75 L78.5833,37.9167 L77.4167,38.75 L75.1696,39.2493 L73.4221,39.5608 L71,40 L25,40 L0,20" 
                Fill="White" Height="30" UseLayoutRounding="False" 
                Width="60" Stretch="Fill" Canvas.Left="25">
            <Path.Effect>
                <DropShadowEffect ShadowDepth="2" Color="#FF999999"/>
            </Path.Effect>
        </Path>
        <TextBlock Text="{Binding Value}" Width="43" TextAlignment="Center" 
          FontWeight="Bold" FontFamily="Arial" Canvas.Top="6" 
          Canvas.Left="39" FontSize="18"></TextBlock>
    </Canvas>
</Canvas>

Now that we have done the design, we turn our attention to coding. There are only a couple of tasks left now. Moving the thumb and recording its position and determining the selected position. I am not going to reproduce the whole code as it is available in the download and is straightforward.

Enough to tell that we monitor MouseLeftButtonDown and MouseLeftButtonUP events on the Thumb ellipse. On MouseLeftButtonDown, we capture the mouse and start waiting for the movement. On MouseLeftButtonUp, we release the mouse capture. While the mouse is captured by the thumb, we monitor the thumb movement to see if it goes to a nearby tick; and if it does, we reset the selected Value. Here is how it is done:

private void LayoutRoot_MouseMove(object sender, MouseEventArgs e)
{
    if (isMoving && values != null && values.Length > 1)
    {
        double y = e.GetPosition(LayoutRoot).Y - lastMouseY;
        if (y >= yCoords.First() && y <= yCoords.Last())
        {
            sliderCanvas.SetValue(Canvas.TopProperty, y);
        }
        for (int i = 0; i < yCoords.Length; i++)
        {
            if (y > yCoords[i] - vGap / 2 && y < yCoords[i] + vGap / 2)
            {
                if (selectedIndex != i)
                {
                    Value = values[i];
                    selectedIndex = i;
                }
                break;
            }
        }
    }
}

Limitations

This slider is not a general purpose control and so it does not provide a whole lot of customization properties like the stock slider or other controls. The idea is to create a light-weight control suited to the specific needs of the task at hand.

One specific limitation is that you cannot tell the slider to move to certain value from code. Currently, this can only be set by the user moving the thumb. However, there is no reason why you cannot change this behavior. I avoided it to keep the sample code simple.

Another aspect of the current implementation is that, unlike the stock slider, this slider only supports vertical orientation and the direction is fixed from bottom to top.

Despite the above limitations, it is very easy from here to add more customization to it, if you so needed. Adding horizontal layout or changing the direction should also be trivial.

Conclusion

Thanks for reading till the end. I hope you found the slider smart and simple. As always, if you think something is missing, or I can improve this article or the code in any way, please let me know. Also, if you got any questions, I would be more than happy to reply.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here