Recently there was a feature request for out-of-the-box support for a ToolTip while dragging any of RadSlider's thumbs. Since we don't provide one yet we decided to check whether it is possible to accomplish this scenario with the current implementation of RadSlider. It turned out that it was actually quite trivial. Few attached properties, a little bit of knowledge about tooltips in Silverlight and voila!

Below is the class that does all the magic. It is pretty self explanatory.

public class SliderToolTipExtensions : DependencyObject
{
    private static string SingleThumbName = "SingleThumbHost";
    private static string RangeStartThumb = "RangeStartThumb";
    private static string RangeEndThumb = "RangeEndThumb";
 
    private static string ValuePropertyPath = "Value";
    private static string SelectionStartPropertyPath = "SelectionStart";
    private static string SelectionEndPropertyPath = "SelectionEnd";
 
    public static readonly DependencyProperty IsToolTipEnabledProperty =
    DependencyProperty.RegisterAttached("IsToolTipEnabled", typeof(bool), typeof(RadSlider), new PropertyMetadata(false, OnIsToolTipEnabledChanged));
 
    public static readonly DependencyProperty ToolTipProperty =
    DependencyProperty.RegisterAttached("ToolTip", typeof(ToolTip), typeof(RadSlider), new PropertyMetadata(null));
 
    public static readonly DependencyProperty ToolTipContextProperty =
    DependencyProperty.RegisterAttached("ToolTipContext", typeof(object), typeof(RadSlider), new PropertyMetadata(null));
 
    /// <summary>
    /// Gets the IsToolTipEnabled attached property.
    /// </summary>
    public static bool GetIsToolTipEnabled(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsToolTipEnabledProperty);
    }
 
    /// <summary>
    /// Sets the IsToolTipEnabled attached property.
    /// </summary>
    public static void SetIsToolTipEnabled(DependencyObject obj, bool value)
    {
        obj.SetValue(IsToolTipEnabledProperty, value);
    }
 
    /// <summary>
    /// Gets the TooTip attached property.
    /// </summary>
    public static ToolTip GetToolTip(DependencyObject obj)
    {
        return (ToolTip)obj.GetValue(ToolTipProperty);
    }
 
    /// <summary>
    /// Sets the ToolTip attached property.
    /// </summary>
    public static void SetToolTip(DependencyObject obj, ToolTip value)
    {
        obj.SetValue(ToolTipProperty, value);
    }
 
    /// <summary>
    /// Gets the ToolTipContext attached property.
    /// </summary>
    public static object GetToolTipContext(DependencyObject obj)
    {
        return obj.GetValue(ToolTipContextProperty);
    }
 
    /// <summary>
    /// Sets the ToolTipContext attached property.
    /// </summary>
    public static void SetToolTipContext(DependencyObject obj, object value)
    {
        obj.SetValue(ToolTipContextProperty, value);
    }
 
    /// <summary>
    /// Called when RadSlider's DragCompleted event is raised.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="Telerik.Windows.Controls.RadDragCompletedEventArgs" /> instance containing the event data.</param>
    private static void OnSliderDragCompleted(object sender, RadDragCompletedEventArgs e)
    {
        RadSlider slider = sender as RadSlider;
        if (slider != null)
        {
            SliderToolTipExtensions.CloseToolTips(slider);
        }
    }
 
    /// <summary>
    /// Called when RadSlider changes its Value property.
    /// </summary>
    private static void OnSliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        RadSlider slider = sender as RadSlider;
        if (slider != null)
        {
            CreateToolTip(slider, ValuePropertyPath, SingleThumbName);
        }
    }
 
    /// <summary>
    /// Called when RadSlider changes its SelectionStart property.
    /// </summary>
    private static void OnSliderSelectionStartChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        RadSlider slider = sender as RadSlider;
        if (slider != null)
        {
            CreateToolTip(slider, SelectionStartPropertyPath, RangeStartThumb);
        }
    }
 
    /// <summary>
    /// Called when RadSlider changes its SelectionEnd property.
    /// </summary>
    private static void OnSliderSelectionEndChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        RadSlider slider = sender as RadSlider;
        if (slider != null)
        {
            CreateToolTip(slider, SelectionEndPropertyPath, RangeEndThumb);
        }
    }
 
    /// <summary>
    /// Sets the ToolTip DataContext.
    /// </summary>
    private static void SetToolTipDataContext(FrameworkElement target, RadSlider owner, string propertyPath)
    {
        if (target.GetBindingExpression(FrameworkElement.DataContextProperty) == null)
        {
            target.SetBinding(FrameworkElement.DataContextProperty, new System.Windows.Data.Binding()
            {
                Source = owner,
                Path = new PropertyPath(propertyPath),
                Mode = BindingMode.TwoWay
            });
        }
    }
 
    /// <summary>
    /// Called when the ToolTip is enabled or disabled.
    /// </summary>
    private static void OnIsToolTipEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        RadSlider slider = d as RadSlider;
        bool activate = e.NewValue != null && (bool)e.NewValue;
        if (slider != null)
        {
            if (activate)
            {
                // activate behavior
                slider.SelectionStartChanged += OnSliderSelectionStartChanged;
                slider.SelectionEndChanged += OnSliderSelectionEndChanged;
                slider.ValueChanged += OnSliderValueChanged;
                slider.DragCompleted += OnSliderDragCompleted;
            }
            else
            {
                // deactivate behavior
                slider.ValueChanged -= OnSliderValueChanged;
                slider.DragCompleted -= OnSliderDragCompleted;
                slider.SelectionStartChanged -= OnSliderSelectionStartChanged;
                slider.SelectionEndChanged -= OnSliderSelectionEndChanged;
            }
        }
    }
 
    /// <summary>
    /// Gets a specific Thumb from RadSlider's ControlTemplate.
    /// </summary>
    private static Thumb GetThumb(RadSlider slider, string thumbName)
    {
        return slider.ChildrenOfType<Thumb>().Where(x => x.Name == thumbName).FirstOrDefault();
    }
 
    /// <summary>
    /// Closes the ToolTips.
    /// </summary>
    /// <param name="owner">The owner.</param>
    private static void CloseToolTips(RadSlider slider)
    {
        IEnumerable<Thumb> thumbs = slider.ChildrenOfType<Thumb>();
        foreach (Thumb thumb in thumbs)
        {
            ToolTip tooltip = ToolTipService.GetToolTip(thumb) as ToolTip;
            if (tooltip != null)
            {
                // throws exception
                // thumb.ClearValue(ToolTipService.ToolTipProperty);
                // close the tooltip and prevent it from opening
                tooltip.IsOpen = false;
                tooltip.Visibility = Visibility.Collapsed;
            }
        }
    }
 
    /// <summary>
    /// Creates the ToolTip.
    /// </summary>
    /// <param name="slider">The slider.</param>
    /// <param name="propertyPath">The property path.</param>
    /// <param name="thumbName">Name of the thumb.</param>
    private static void CreateToolTip(RadSlider slider, string propertyPath, string thumbName)
    {
        Thumb thumb = SliderToolTipExtensions.GetThumb(slider, thumbName);
        if (thumb != null)
        {
            thumb.ClearValue(ToolTipService.ToolTipProperty);
            ToolTip toolTip = slider.GetValue(SliderToolTipExtensions.ToolTipProperty) as ToolTip;
            toolTip.ClearValue(FrameworkElement.DataContextProperty);
            SliderToolTipExtensions.SetToolTipDataContext(toolTip, slider, propertyPath);
            toolTip.PlacementTarget = thumb;
            ToolTipService.SetToolTip(thumb, toolTip);
            toolTip.IsOpen = true;
            toolTip.Visibility = Visibility.Visible;
        }
    }
}

And this is how you use the SliderToolTipExtensions helper class.

<telerik:RadSlider Value="0.4" TickFrequency="0.1" TickPlacement="TopLeft" Margin="0 50"
        extensions:SliderToolTipExtensions.IsToolTipEnabled="True"
        extensions:SliderToolTipExtensions.ToolTipContext="{Binding RelativeSource={RelativeSource Self}}">
    <extensions:SliderToolTipExtensions.ToolTip>
        <ToolTip Placement="Bottom">
            <TextBlock Text="{Binding Path=., StringFormat=n2}" FontSize="14" />
        </ToolTip>
    </extensions:SliderToolTipExtensions.ToolTip>
</telerik:RadSlider>
<telerik:RadSlider SelectionStart="0.4" SelectionEnd="0.6" TickFrequency="0.1"
        TickPlacement="TopLeft" Margin="0 50" IsSelectionRangeEnabled="True"
        extensions:SliderToolTipExtensions.IsToolTipEnabled="True"
        extensions:SliderToolTipExtensions.ToolTipContext="{Binding RelativeSource={RelativeSource Self}}">
    <extensions:SliderToolTipExtensions.ToolTip>
        <ToolTip Placement="Bottom">
            <TextBlock Text="{Binding Path=., StringFormat=n2}" FontSize="14" />
        </ToolTip>
    </extensions:SliderToolTipExtensions.ToolTip>
</telerik:RadSlider>

That's it. Drop us a line if you got any comments.
About the Author

Kiril Stanoev

Hi, I'm Kiril and I'm the Product Manager of Telerik UI for Android, Windows Universal and Windows Phone. Feel free to ping me on +KirilStanoev or @KirilStanoev

Related Posts

Comments