locked
How to create a custom Template for a RangeSlider usercontrol RRS feed

  • Question

  • I followed this article   Creating a Range Slider in WPF  to create a great Range Slider using XAML and a bit of code behind.  I am using this user control within my project.   But I want to create a style for the control that has a custom thumb and track.  

    In my main XAML I use the control like this

    <local:RangeSlider LowerValue="1" UpperValue="100"  Style="{DynamicResource RangeSliderStyle1}"/>
    

    the template I created looks like this

    But I this doesn't apply the ThumbStyle2 to the thumbs in the Range Slider? 

    How would I do this so it works?

    <Style x:Key="RangeSliderStyle1" TargetType="{x:Type local:RangeSlider}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:RangeSlider}"> <Grid>

    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True">
    <ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
    </Border>

    <Canvas x:Name="graph_gutter_horizontal" UseLayoutRounding="False"> <Canvas x:Name="Layer_2" Width="635" Canvas.Top="1.888" Canvas.Left="2.636" Height="6.001"> <Path Width="617.206" Canvas.Top="0.501" Stretch="Fill" Canvas.Left="0.5" Height="5" Fill="#FF1A1A1A" Data="F1 M620.34269,5.3887 C620.34269,6.4887 619.44269,7.3887 618.34269,7.3887 L5.1357,7.3887 C4.0357,7.3887 3.1357,6.4887 3.1357,5.3887 L3.1357,4.3887 C3.1357,3.2887 4.0357,2.3887 5.1357,2.3887 L618.34269,2.3887 C619.44269,2.3887 620.34269,3.2887 620.34269,4.3887 z"/> <Path Width="618.206" Canvas.Top="0" Stretch="Fill" Canvas.Left="0" Height="6.001" Data="F1 M5.135,1.888 C3.758,1.888 2.636,3.011 2.636,4.389 L2.636,4.389 2.636,5.388 C2.636,6.767 3.758,7.889 5.135,7.889 L5.135,7.889 618.34199,7.889 C619.72099,7.889 620.84299,6.767 620.84299,5.388 L620.84299,5.388 620.84299,4.389 C620.84299,3.011 619.72099,1.888 618.34199,1.888 L618.34199,1.888 z M3.636,5.388 L3.636,4.389 C3.636,3.561 4.308,2.889 5.135,2.889 L5.135,2.889 618.34199,2.889 C619.16899,2.889 619.84299,3.561 619.84299,4.389 L619.84299,4.389 619.84299,5.388 C619.84299,6.215 619.16899,6.889 618.34199,6.889 L618.34199,6.889 5.135,6.889 C4.308,6.889 3.636,6.215 3.636,5.388"> <Path.Fill> <LinearGradientBrush EndPoint="0.5,0" StartPoint="0.5,1"> <GradientStop Color="#FF3F4C57" Offset="0"/> <GradientStop Color="#FF273137" Offset="0.51371997594833374"/> <GradientStop Color="#FF273137" Offset="1"/> </LinearGradientBrush> </Path.Fill> </Path> </Canvas> </Canvas> <Track x:Name="PART_Track" Margin="0,-12.64,10,-9.32" > <Track.Thumb> <Thumb Style="{StaticResource ThumbStyle2}"/> </Track.Thumb> </Track> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>



    Jeff Davis

    Monday, June 17, 2013 10:16 PM

All replies

  • Hello Jeff.

    If you move your style and ControlTemplate to your App.xaml file or a resource dictionary you should be able to access them to edit the templates...

    RangeSlider XAML...

    <UserControl x:Class="WpfApplication56.RangeSlider"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        x:Name="root" Style="{DynamicResource RangeSliderStyle}"/>

    The code-behind I had to adjust a bit to locate the Upper and Lower Sliders...

    using System;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace WpfApplication56
    {
        public partial class RangeSlider : UserControl
        {
    	Slider LowerSlider;
    	Slider UpperSlider;
    		
            public RangeSlider()
            {
            	InitializeComponent();			
    		this.Loaded += Slider_Loaded;
            }
    
            void Slider_Loaded(object sender, RoutedEventArgs e)
            {
    		ControlTemplate ct = this.Template;
    		LowerSlider = ct.FindName("LowerSlider", this) as Slider;
    		UpperSlider = ct.FindName("UpperSlider", this) as Slider;
    	        LowerSlider.ValueChanged += LowerSlider_ValueChanged;
                	UpperSlider.ValueChanged += UpperSlider_ValueChanged;
            }
    
            private void LowerSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
            {
    		if (this.IsLoaded)
                		UpperSlider.Value = Math.Max(UpperSlider.Value, LowerSlider.Value);
            }
    
            private void UpperSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
            {
                	if (this.IsLoaded)
                		LowerSlider.Value = Math.Min(UpperSlider.Value, LowerSlider.Value);
            }
    
            public double Minimum
            {
                get { return (double)GetValue(MinimumProperty); }
                set { SetValue(MinimumProperty, value); }
            }
    
            public static readonly DependencyProperty MinimumProperty =
                DependencyProperty.Register("Minimum", typeof(double), typeof(RangeSlider), new UIPropertyMetadata(0d));
    
            public double LowerValue
            {
                get { return (double)GetValue(LowerValueProperty); }
                set { SetValue(LowerValueProperty, value); }
            }
    
            public static readonly DependencyProperty LowerValueProperty =
                DependencyProperty.Register("LowerValue", typeof(double), typeof(RangeSlider), new UIPropertyMetadata(0d));
    
            public double UpperValue
            {
                get { return (double)GetValue(UpperValueProperty); }
                set { SetValue(UpperValueProperty, value); }
            }
    
            public static readonly DependencyProperty UpperValueProperty =
                DependencyProperty.Register("UpperValue", typeof(double), typeof(RangeSlider), new UIPropertyMetadata(0d));
    
            public double Maximum
            {
                get { return (double)GetValue(MaximumProperty); }
                set { SetValue(MaximumProperty, value); }
            }
    
            public static readonly DependencyProperty MaximumProperty =
                DependencyProperty.Register("Maximum", typeof(double), typeof(RangeSlider), new UIPropertyMetadata(1d));
        }
    }

    And I put the style in my App.xaml...

    <Application
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	x:Class="WpfApplication56.App"
    	StartupUri="MainWindow.xaml">
    	<Application.Resources>
    		<!-- Resources scoped at the Application level should be defined here. -->
    		<Style x:Key="RangeSliderStyle" TargetType="{x:Type UserControl}">
    			<Setter Property="Template">
    				<Setter.Value>
    					<ControlTemplate TargetType="{x:Type UserControl}">
    						<Grid VerticalAlignment="Top">
    							<Border BorderThickness="0,1,0,0" BorderBrush="Black" VerticalAlignment="Center" Height="1" 
    								Margin="5,0,5,0"/>
    
    							<Slider x:Name="LowerSlider"
    								Minimum="{Binding Minimum, RelativeSource={RelativeSource TemplatedParent}}"
    								Maximum="{Binding Maximum, RelativeSource={RelativeSource TemplatedParent}}"
    								Value="{Binding LowerValue, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
    								Template="{StaticResource simpleSlider}"
    								Margin="0,0,10,0"
    					                />
    					        
    							<Slider x:Name="UpperSlider"
    								Minimum="{Binding Minimum, RelativeSource={RelativeSource TemplatedParent}}"
    								Maximum="{Binding Maximum, RelativeSource={RelativeSource TemplatedParent}}"
    								Value="{Binding UpperValue, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
    								Template="{StaticResource simpleSlider}"
    								Margin="10,0,0,0"
    					                />
    						</Grid>
    					</ControlTemplate>
    				</Setter.Value>
    			</Setter>
    		</Style>
    
    		<ControlTemplate x:Key="simpleSlider" TargetType="{x:Type Slider}">
    			<Border SnapsToDevicePixels="true" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
    				<Grid>
    					<Grid.RowDefinitions>
    						<RowDefinition Height="Auto"/>
    						<RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}"/>
    						<RowDefinition Height="Auto"/>
    					</Grid.RowDefinitions>
                        
    					<Rectangle x:Name="PART_SelectionRange"/>
    
    					<Track x:Name="PART_Track" Grid.Row="1">
    						<Track.Thumb>
    							<Thumb x:Name="Thumb">
    								<Thumb.Template>
    									<ControlTemplate TargetType="{x:Type Thumb}">
    										<Rectangle Fill="Red" 
    											Stroke="Black"
    											StrokeThickness="1" 
    											Width="10"
    											Height="18"
    											SnapsToDevicePixels="True"/>
    									</ControlTemplate>
    								</Thumb.Template>
    							</Thumb>
    						</Track.Thumb>
    					</Track>
    				</Grid>
    			</Border>
    		</ControlTemplate>
    	</Application.Resources>
    </Application>

    Now when you right-click the slider in your window, Edit Template > Edit Current, you will be able to access the style which holds the 2 sliders. 

    Then right-click and edt the template for one of the sliders, Edit Template > Edit Current, to access the thumb.

    ~Christine

    You may get a xaml error.  If so put the simpleSlider before the RangeSliderStyle.




    Tuesday, June 18, 2013 12:42 PM
  • I get what you're trying to suggest but I don't think this will work the way I want it to.  Plus I am having some trouble getting the code-behind changes to work.

    I want to have a local user control in my project Called RangeSlider.  I want to use that control within my MainWindow.XAML and then style it within my MainWindow Window.Resources.  I might need to apply different styles depending on where I am using within the MainWindow.  For example.

    <local:RangeSlider LowerValue="1" UpperValue="100"  Style="{DynamicResource RangeSliderStyle1}"/>

    <local:RangeSlider LowerValue="10" UpperValue="20000"  Style="{DynamicResource RangeSliderStyle2}"/>

    These styles will define the Thumb style used for the RangeSlider both Thumbs and also the track graphic for each RangeSlider.

    I want to be able to drag the user control onto the MainWindow and them right-click edit template and create a copy with my specific changes.

    I don't mind having a default template in the App.XAML if that make it work but I don't want specific styles defined in App.XAML my styles for MainWindow are are defined in the Window.Resources area.


    Jeff Davis

    Tuesday, June 18, 2013 10:28 PM
  • My project is in VB and when I try to convert your C# code I get errors during the build that says 

    Public Event ValueChanged.... is an event, and cannot be called directly.  use a RaiseEvent statement to raise an event.


    Jeff Davis

    Tuesday, June 18, 2013 10:32 PM
  • Jeff, the code I used is the code you provided in your link. So if you had it working before just add something that finds the 2 sliders. The only thing I added was the bit about finding the upper and lower sliders in the style. You could easily create a copy of the style for your individual pages or dictionaries from the base style provided in that link. Just you can't edit the style of the thumb directly. Because it is in the style for the slider which is in the style for the 2 sliders. But I'm a big believer if it doesn't work the way you want... Make your own. ~Christine
    Wednesday, June 19, 2013 12:34 AM