in Development

Filtering values with a silverlight slider and linq

Normally, when we want to filter a list of values in a web page, we introduce the filter condition in an input type and then we should press a button to apply the condition. If we are lucky enough the web page will include some Ajax features to avoid doing a post back. If not, we will need to do the post back a recharge the whole page. This doesn’t seem to be the most optimum way of working. However, lately I’ve seen some web pages that have introduced Slider controls as a filter mechanism. The way in which you apply the filter it’s very intuitive, you only need to move the thumb along its track and the filter will be applied.

In my opinion the Slider mechanism provides a much better user experience and looks nicer than the conventional input type + button. With Silverlight 1.1 and Linq we can implement this in a few minutes.

The first we need to do it’s to create the Slider. Instead of adding it directly in a Silverlight project we will build it as “Silverlight Control”, this will allow us reusing the Slider in other projects.

The XAML we will create it’s very simple, we will have a Path that will be our Thumb, a Line as the track and a Rectangle that we will use to be able to move the Thumb across the Track by clicking directly in the desired position.

Next you can see the full XAML code.

   1: <Canvas xmlns="http://schemas.microsoft.com/client/2007"
   2:         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   3:         Background="Transparent"
   4:         Width="200"
   5:         Height="50"
   6:         x:Name="Slider"
   7:         >
   8: 
   9:   <!-- Track -->
  10:     <Line x:Name="line"
  11:         Canvas.Left="15"
  12:         Stroke="black" StrokeThickness="1"
  13:         X1="0" Y1="25" X2="170" Y2="25" />
  14:
  15:   <!-- Boxing rectangle-->
  16:     <Rectangle x:Name="box" Canvas.Left="15"
  17:         Width="170" Height="50" Cursor="Hand" Fill="Transparent" />
  18: 
  19:   <!-- Thumb -->
  20:     <Path x:Name="thumb"
  21:         Canvas.Left="7.5" Canvas.Top="25"
  22:         Width="15" Height="20"
  23:         Fill="sc#1, 0.548430264, 0.5354195, 0.5354195" Stretch="Fill"
  24:         Stroke="#FF000000" Data="F1 M28.352564,0.5 L55.5,23.5 55.5,75.5 0.50000054,75.5 0.50000054,23.5 28.352564,0.5 z"
  25:         Cursor="Hand"/>
  26: </Canvas>

In the class associated to this XAML we add the necessary properties to delimit the values accepted by the Slider: MinValue, MaxValue and also to know and specify the current value. Note in the lines from 11 to 19 that when we set the “Value” property we perform two method calls: OnValueChanged and UpdatePosition. The first one will be in charge of firing the “ValueChanged” event you can see below. The second one will allow to update the position of the Thumb when we set the property directly by code, C# or XAML, if we don’t add this part the only way to update the Thumb position would be using the mouse.

   1: public double MaxValue { get; set; }
   2:
   3: public double MinValue { get; set; }
   4:
   5: public double Value
   6: {
   7:     get
   8:     {
   9:         return _value;
  10:     }
  11:     set
  12:     {
  13:         if (_value != value)
  14:         {
  15:             _value = value;
  16:             OnValueChanged();
  17:             UpdatePosition(_value);
  18:         }
  19:     }
  20: }

In addition to the properties we will include an event “ValueChanged” to notify users that the value of the Slider has been modified.

   1: public event EventHandler ValueChanged;
   2:
   3: protected virtual void OnValueChanged()
   4: {
   5:     EventHandler handler = ValueChanged;
   6:     if (handler != null)
   7:         ValueChanged(this, EventArgs.Empty);
   8:
   9: } double _value;

In order to be able to move the Thumb, it’s necessary we control the mouse events that occur over our Slider, for that we only need to add the necessary event handlers for the events MouseMove, MouseLeftButtonDown and MouseLeftButtonUp and to implement basic Drag & Drop functionality.

   1: void element_MouseLeftButtonDown(object sender, MouseEventArgs e)
   2: {
   3:     MoveThumb(e.GetPosition(null).X);
   4: }
   5:
   6: void thumb_MouseMove(object sender, MouseEventArgs e)
   7: {
   8:     if (this.isDragging)
   9:     {
  10:         MoveThumb(e.GetPosition(null).X);
  11:     }
  12: }
  13:
  14: void thumb_MouseLeftButtonUp(object sender, MouseEventArgs e)
  15: {
  16:     this.isDragging = false;
  17:     thumbElement.ReleaseMouseCapture();
  18: }
  19:
  20: void thumb_MouseLeftButtonDown(object sender, MouseEventArgs e)
  21: {
  22:     this.isDragging = true;
  23:     thumbElement.CaptureMouse();
  24: }

You can see the complete implementation of the MoveThumb function downloading the full code for this post.

This is all what we need to do to have a basic Slider control, of course for production environments you will need to add some more functionality to handle different sizes for the Slider, colors, etc.  Now that we have the slider, let’s see how we can make use of it.

The first thing we need to do is to create a Silverlight project and add a reference to our library. Then, the way in which we can add custom controls in our XAML pages is very easy. We only need to import the namespace and add the XAML code for the control doing something like this (Note that we can make use of our custom properties directly in the XAML code).

   1: <Canvas x:Name="parentCanvas"
   2:         xmlns="http://schemas.microsoft.com/client/2007"
   3:         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:         Loaded="Page_Loaded"
   5:         x:Class="SilverlightControlsTester.Page;assembly=ClientBin/SilverlightControlsTester.dll"
   6:         xmlns:Controls="clr-namespace:Silverlight.Controls;assembly=ClientBin/Silverlight.Controls.dll"
   7:         Width="640"
   8:         Height="480"
   9:         Background="White"
  10:         >
  11: 
  12:   <Controls:Slider x:Name="slider" Width="200" Height="45" MinValue="0" MaxValue="10" Value="5"/>
  13: 
  14:   <Canvas x:Name="container" Canvas.Top="100" />
  15: 
  16: </Canvas>

In the code below what we do is to create a few products with Id and Price (just imagine you retrieved from somewhere else) and we keep them in a list.

Now we only need to do the final touch, we add a handler for the event ValueChanged we created before in the Slider control. In the handler we add a Linq query that we will use to filter the products we want to display based on the price of the product and the “Value” property of the Slider. To finish we just iterate the elements and we create a TextBlock element for each product that we will add dynamically in the Canvas “container”.

   1: public partial class Page : Canvas
   2: {
   3:     List<Product> products;
   4:     public void Page_Loaded(object o, EventArgs e)
   5:     {
   6:         // Required to initialize variables
   7:         InitializeComponent();
   8:
   9:         this.slider.ValueChanged += new EventHandler(slider_ValueChanged);
  10:
  11:         // Initialize list of fake products
  12:         products = new List<Product>(10);
  13:         Random random = new Random(Environment.TickCount);
  14:
  15:         for (int i = 0; i < 10; i++)
  16:         {
  17:             products.Add(new Product() { Id = i, Price = random.Next(1,10) });
  18:         }
  19:
  20:         UpdateDisplayedProducts();
  21:     }
  22:
  23:     void UpdateDisplayedProducts()
  24:     {
  25:         // We create a Linq query and we filter the products based on
  26:         // their prices and the Value of the slider
  27:         var displayedProducts = from p in products
  28:                                 where p.Price < slider.Value
  29:                                 select p;
  30:
  31:         // we clear the contents of the container Canvas
  32:         this.container.Children.Clear();
  33:
  34:         // We add the elements we obtained with the Linq query
  35:         int line = 1;
  36:         foreach (var p in displayedProducts)
  37:         {
  38:             string xaml = string.Format("<TextBlock Canvas.Top=\"{2}\" Width=\"100\" Height=\"25\" Text=\"Product: {0}, Price:{1}\" />", p.Id, p.Price, 25 * line);
  39:
  40:             var element = XamlReader.Load(xaml) as Visual;
  41:
  42:             if (element != null)
  43:             {
  44:                 this.container.Children.Add(element);
  45:                 line++;
  46:             }
  47:         }
  48:     }
  49:
  50:     void slider_ValueChanged(object sender, EventArgs e)
  51:     {
  52:         UpdateDisplayedProducts();
  53:     }
  54: }
  55:
  56: public class Product
  57: {
  58:     public int Id { get; set; }
  59:     public double Price { get; set; }
  60: }

As we have seen with just a couple of minutes we can take profit of .NET Framework 3.5 and Silverlight to create better user experiences.

I hope you liked it.

SilverlightSlider.zip