Ticktemplate based on ObservableCollection

6 posts, 0 answers
  1. Sodi We
    Sodi We avatar
    160 posts
    Member since:
    Apr 2010

    Posted 05 May 2010 Link to this post

    Hello,

    The idea we want to establish with the slidercontrol is the following. We fetch lists (scalesteps) from a database that need to be displayed with a slider.Setting the slider to a certain selected item from that list is not the issue. But we cannot figure out what the best way is to show the list as TickMarks. In fact the list we fetch from the database is not much more than a list of strings. So above the first TickMark we would like to have the first string (scalestep) in the list to be shown.

    Our first idea was to use the tick template with a converter. But then there is the issue of how to get the list of strings we fetch from a database to be known to the converter. The converter only knows the datacontext from the binding, being the decimal values. We could extend the RadSlider control with a dependency property "ObservableCollection<ScaleSteps>" and a converter. Every time an item is added to the ObservableCollection, we could re-set the ScaleSteps - property of the converter and re-set the Datatemplate of the RadSlider (to make sure the Converter is called again).

    This feels tricky... What would be your suggested way of handling this? Is there maybe a way we can set the ConverterParameter to hold the list of scalesteps? Is there a better way to call the conversion again than re-setting the datatemplate?

    Thanks in advance,
    Sodi

  2. Bobi
    Admin
    Bobi avatar
    513 posts

    Posted 11 May 2010 Link to this post

    Hi Sodi We,

    Please find attached a sample project that demonstrates how to use TickTemplate with some data and Convertor.
    I hope that this will help you. If not please let us know.

    Greetings,
    Bobi
    the Telerik team

    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
  3. DevCraft banner
  4. Sodi We
    Sodi We avatar
    160 posts
    Member since:
    Apr 2010

    Posted 11 May 2010 Link to this post

    Thank you for the response.

    We have in fact implemented something similar. One question still remains though. If the data comes from a database, how can you ensure that the converters already have that data before Convert() is called?

    What we've done is put the Entries-binding in a new Usercontrol that wraps the SliderControl. Each time an Entry is added to the ObservableCollection Entries via binding, we reset the RadSliders Maximum property to make sure the conversion is reapplied. This doesn't seem like the right approach. Any thoughts?

    Thanks,
    Sodi
  5. Bobi
    Admin
    Bobi avatar
    513 posts

    Posted 14 May 2010 Link to this post

    Hi Sodi We,

    Can you please send us some sample project in order to fix it or explain in more details the structure of your application?

    Regards,
    Bobi
    the Telerik team

    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
  6. Sodi We
    Sodi We avatar
    160 posts
    Member since:
    Apr 2010

    Posted 17 May 2010 Link to this post

    Here is some sample code:

    Our scaleSliderControl.xaml:
    <UserControl  
        x:Class="Controls.ScaleSlider.ScaleSliderControl" 
        xmlns:telerik="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls" 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        d:DesignWidth="500" d:DesignHeight="50"
         
        <UserControl.Resources> 
            <local:ScaleTickConverter x:Key="ScaleTickConverter" /> 
        </UserControl.Resources> 
     
        <StackPanel x:Name="LayoutRoot"
            <telerik:RadSlider x:Name="slider" Orientation="Horizontal" Minimum="0" Maximum="0" ValueChanged="slider_ValueChanged" 
                                    EnableSideTicks="True" TickPlacement="BottomRight" TickFrequency="1" IsSnapToTickEnabled="True" Height="36" Canvas.ZIndex="102"
                <telerik:RadSlider.TickTemplate> 
                    <DataTemplate> 
                        <Grid> 
                            <Grid.RowDefinitions> 
                                <RowDefinition Height="Auto" /> 
                                <RowDefinition Height="Auto" /> 
                            </Grid.RowDefinitions> 
                            <Rectangle Fill="Black" Width="1" Height="5" Grid.Row="0"/> 
                            <TextBlock Text="{Binding Converter={StaticResource ScaleTickConverter}}" FontSize="10" Width="100" TextAlignment="Center" Grid.Row="1" /> 
                        </Grid> 
                    </DataTemplate> 
                </telerik:RadSlider.TickTemplate> 
            </telerik:RadSlider> 
        </StackPanel> 
    </UserControl> 

    ScaleSliderControl.xaml.cs
    :
    using System; 
    using System.Collections.Generic; 
    using System.ComponentModel; 
    using System.Linq; 
    using System.Windows; 
    using System.Windows.Controls; 
    using System.Windows.Input; 
    using System.Collections.ObjectModel; 
     
    namespace Controls.ScaleSlider 
        public partial class ScaleSliderControl : UserControl 
        { 
            #region Fields (4)  
     
            public static readonly DependencyProperty ScaleStepDtosProperty = 
                DependencyProperty.Register("ScaleStepDtos"typeof(ObservableCollection<ScaleStepDto>), typeof(ScaleSliderControl), new PropertyMetadata(null, ScaleStepsProperty_Changed)); 
            #endregion Fields  
            #region Constructors (1)  
     
            /// <summary> 
            /// Constructor 
            /// </summary> 
            public ScaleSliderControl() 
            { 
                InitializeComponent(); 
                LayoutRoot.DataContext = this
            } 
            #endregion Constructors  
            #region Properties (4)  
     
            /// <summary> 
            /// Property for the ScaleStepDtos 
            /// </summary> 
            public ObservableCollection<ScaleStepDto> ScaleStepDtos 
            { 
                get { return (ObservableCollection<ScaleStepDto>)GetValue(ScaleStepDtosProperty); } 
                set 
                { 
                    SetValue(ScaleStepDtosProperty, value); 
                    NotifyPropertyChanged("ScaleStepDtos"); 
                } 
            } 
            #endregion Properties  
            #region Delegates and Events (1)  
     
            // Events (1)  
     
            /// <summary> 
            /// Event to notify the UI of changes. 
            /// </summary> 
            public event PropertyChangedEventHandler PropertyChanged; 
            #endregion Delegates and Events  
            #region Methods (10)  
     
            // Private Methods (10)  
            
            /// <summary> 
            /// Helper method to notify the UI that a property has been changed. 
            /// </summary> 
            /// <param name="prop"></param> 
            private void NotifyPropertyChanged(string prop) 
            { 
                if(PropertyChanged != null
                    PropertyChanged(thisnew PropertyChangedEventArgs(prop)); 
            } 
     
            /// <summary> 
            /// Callback method for when the ScaleStepDtos-collections has been altered. 
            /// </summary> 
            /// <param name="sender"></param> 
            /// <param name="e"></param> 
            private void ScaleStepDtos_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 
            { 
                slider.Maximum = Math.Max(ScaleStepDtos.Count - 1, 1); 
                SetValueOfSlider(); 
            } 
     
            /// <summary> 
            /// Calback method for when the ScaleSteps have been set via binding. 
            /// </summary> 
            /// <param name="d"></param> 
            /// <param name="e"></param> 
            private static void ScaleStepsProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) 
            { 
                var scaleSlider = ((ScaleSliderControl)d); 
                scaleSlider.ScaleStepDtos = (ObservableCollection<ScaleStepDto>)e.NewValue; 
                scaleSlider.ScaleStepDtos.CollectionChanged += scaleSlider.ScaleStepDtos_CollectionChanged; 
                ((ScaleTickConverter)scaleSlider.Resources["ScaleTickConverter"]).ScaleSteps = scaleSlider.ScaleStepDtos; 
                scaleSlider.slider.Maximum = Math.Max(scaleSlider.ScaleStepDtos.Count - 1, 1); 
            } 
            #endregion Methods  
        } 
     

    As you can see, we use ScaleStepsProperty_Changed to see when the binding is assigned a value. In this method we assign the event handler ScaleStepDtos_CollectionChanged to reset the maximum of the radslider each time an item is added. This is necessary because the scaleslidercontrol doesn't know how many scalesteps there will be. So each time a new scalestep is added to the observablecollection ScaleSteps, the conversion takes place again.

    The ScaleTickConverter.cs:
    using System; 
    using System.Collections.Generic; 
    using System.Net; 
    using System.Windows; 
    using System.Windows.Controls; 
    using System.Windows.Documents; 
    using System.Windows.Ink; 
    using System.Windows.Input; 
    using System.Windows.Media; 
    using System.Windows.Media.Animation; 
    using System.Windows.Shapes; 
    using System.Collections.ObjectModel; 
    using System.Windows.Data; 
     
    namespace Converters 
        public class ScaleTickConverter : FrameworkElement, IValueConverter 
        { 
            /// <summary> 
            /// Property for the ScaleSteps that are used to show the codes under the slider. 
            /// </summary> 
            public ObservableCollection<ScaleStepDto> ScaleSteps { getset; } 
     
            /// <summary> 
            /// Converts the value to a string based on the code in the ScaleSteps 
            /// </summary> 
            /// <param name="value"></param> 
            /// <param name="targetType"></param> 
            /// <param name="parameter"></param> 
            /// <param name="culture"></param> 
            /// <returns></returns> 
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
            { 
                var tick = System.Convert.ToInt32(value); 
                var label = ""
                if (ScaleSteps != null && ScaleSteps.Count > tick) 
                { 
                    label = ScaleSteps[tick].Code; 
                } 
                return label; 
            } 
             
            //Not used 
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
            { 
                throw new NotImplementedException(); 
            } 
        } 
     

    I was wondering what your suggested way of doing something like this would be?

    Thanks,
    Sodi We

  7. Kiril Stanoev
    Admin
    Kiril Stanoev avatar
    1511 posts

    Posted 17 May 2010 Link to this post

    Hi Sodi,

    Judging from the code you've provided us with, I think that this is a completely acceptable solution. You are correct when saying that the slider cannot know when the ScaleStepDtos collection changes, therefore you need this small trick of changing the Maximum so that the Converter gets fired. We have plans to optimize this process by exposing a method of RadSlider that when called will redraw all the ticks without the need of changing Minimum/Maximum/Size (i.e. properties that themselves redraw the ticks). Something like:

    this.radSlider1.RedrawTicks();

     I've added this functionality request in our PITS under the name "Slider: Mechanism for manually redrawing ticks" and it will be available for tracking tomorrow the latest. We will try to add this feature for our Q2 release.

    Let me know what's you opinion on this topic. Will this serve the purpose of your scenario? If you have an alternative suggestion I'd be glad if you share it with me. Looking forward to your reply.

    Greetings,
    Kiril Stanoev
    the Telerik team

    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
Back to Top
DevCraft banner