Search code examples
c#wpfslider

Textblock over the elipse slider thumb - avoid resize of grid behind elipse when text is changing C# WPF


I would like to implement textblock with actual value of slider on the top of elipse slider thumb. When textblock have the same widht as elipse everything is working properly but I need to increase the width of textblock.

After width of textloblock changes also width of grid for elipse changed and slider is not looking proper anymore.

  1. Picture of slider with textblock width = elipse width textblock width = elipse width

  2. Textblock width higher than elipse width texblock width higher

Do you have any idea how to avoid situation from picture 2?

Slider thumb style:

            <Thumb x:Name="SliderThumb">
               <Thumb.Style>
                 <Style  TargetType="Thumb">
                   <Setter Property="SnapsToDevicePixels" Value="true" />
                   <Setter Property="OverridesDefaultStyle" Value="true" />
                   <Setter Property="Template">
                     <Setter.Value>
                       <ControlTemplate  TargetType="Thumb">
                         <Grid  Background="Transparent">
                           <Ellipse VerticalAlignment="Center"  HorizontalAlignment="Center" Fill="Blue" Height="30" Width="30"/>
                           <TextBlock  Width="60" TextAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,80"
                            HorizontalAlignment="Center" Background="Transparent" Foreground="Black" FontSize="24" 
                            Text="{Binding Value, RelativeSource={RelativeSource AncestorType={x:Type Slider}}}"/>
                         </Grid>
                     </ControlTemplate>
                   </Setter.Value>
                 </Setter>
              </Style>
            </Thumb.Style>
          </Thumb>

EDIT: Full code according to issue with text centering.

MainWindow:

 <Grid>
    <ContentControl   Height="70" Width="400">
       <Slider x:Name="slider2"  IsMoveToPointEnabled="True" Tag="mm" Maximum="210"  Width="300" VerticalAlignment="Bottom" Grid.Row="0" Style="{StaticResource Horizontal_Slider}"/>
    </ContentControl> 
</Grid>

Slider style:

            <Style x:Key="SliderRepeatButton" TargetType="RepeatButton">
                <Setter Property="SnapsToDevicePixels" Value="true" />
                <Setter Property="OverridesDefaultStyle" Value="true" />
                <Setter Property="IsTabStop" Value="false" />
                <Setter Property="Focusable" Value="false" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="RepeatButton">
                            <Border Height="10" Background="Red"/>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>

            <Style x:Key="SliderRepeatButton1" TargetType="RepeatButton">
                <Setter Property="SnapsToDevicePixels" Value="true" />
                <Setter Property="OverridesDefaultStyle" Value="true" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="RepeatButton">
                            <Border Height="10" Background="Blue" SnapsToDevicePixels="True" />
                        </ControlTemplate>
                    </Setter.Value>
               </Setter>
           </Style>
           
           
           <Style x:Key="SliderThumb" TargetType="Thumb">
                <Setter Property="SnapsToDevicePixels" Value="true" />
                <Setter Property="OverridesDefaultStyle" Value="true" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="Thumb">
                            <Grid MaxWidth="{Binding ElementName=ellipseThumb, Path=ActualWidth}"                                                      Background="Transparent">
                                <Ellipse x:Name="ellipseThumb" Fill="Blue" Height="30"Width="30"/> 
                                 <Canvas Margin="0,0,0,100" ClipToBounds="False"
                                    Height="{Binding ElementName=valueTextBlock, Path=ActualHeight}"
                                    Width="{Binding ElementName=valueTextBlock, Path=ActualWidth}">
                                    <TextBlock                   
                                       Background="Transparent"
                                       Foreground="{TemplateBinding Background}"
                                       FontSize="24"> 
                                       <Run Text="{Binding Value,StringFormat={}{0:F2}, RelativeSource={RelativeSource AncestorType={x:Type Slider}}}"/>
                                       <Run Text="{Binding Tag, RelativeSource={RelativeSource AncestorType={x:Type Slider}}}"/>
                                    </TextBlock>
                                </Canvas>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
            
            
            
            <ControlTemplate x:Key="CustomSlider"  TargetType="Slider">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Track Grid.Row="1" x:Name="PART_Track"   >
                        <Track.DecreaseRepeatButton>
                            <RepeatButton x:Name="DecreaseSlider" Style="{StaticResource SliderRepeatButton1}"  Command="Slider.DecreaseLarge" />
                        </Track.DecreaseRepeatButton>
                        <Track.Thumb>
                            <Thumb x:Name="SliderThumb"  Style="{StaticResource SliderThumb}"  />
                        </Track.Thumb>
                        <Track.IncreaseRepeatButton>
                            <RepeatButton x:Name="IncreaseSlider" Style="{StaticResource SliderRepeatButton}" Command="Slider.IncreaseLarge" />
                        </Track.IncreaseRepeatButton>
                    </Track>
                </Grid>              
            </ControlTemplate>



           <Style x:Key="Horizontal_Slider" TargetType="Slider">
                <Setter Property="Focusable" Value="False"/>
                <Setter Property="SnapsToDevicePixels" Value="true" />
                <Setter Property="OverridesDefaultStyle" Value="true" />
                <Style.Triggers>
                    <Trigger Property="Orientation" Value="Horizontal">
                        <Setter Property="MinHeight" Value="21" />
                        <Setter Property="MinWidth" Value="104" />
                        <Setter Property="Template" Value="{StaticResource CustomSlider}" />
                    </Trigger>
                </Style.Triggers>
            </Style>


Solution

  • You have to wrap your TextBlock in a Canvas to avoid clipping, and also you need to not set the width for TextBLock to let it get the size it need.

    Here is a working code for your case:

    <Thumb x:Name="SliderThumb">
        <Thumb.Style>
            <Style  TargetType="Thumb">
                <Setter Property="SnapsToDevicePixels" Value="true" />
                <Setter Property="OverridesDefaultStyle" Value="true" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="Thumb">
                            <Grid Background="Transparent" MaxWidth="{Binding ElementName=ellipse, Path=ActualWidth}">
                                <Ellipse x:Name="ellipse" VerticalAlignment="Center" HorizontalAlignment="Center" Fill="Blue" Height="30" Width="30"/>
                                <Canvas Margin="0,0,0,80"
                                        Height="{Binding ElementName=valueTextBlock, Path=ActualHeight}"
                                        Width="{Binding ElementName=valueTextBlock, Path=ActualWidth}"
                                        HorizontalAlignment="Center">
                                    <TextBlock x:Name="valueTextBlock"
                                               Background="Transparent"
                                               Foreground="Black"
                                               FontSize="24"
                                               Text="{Binding Value, RelativeSource={RelativeSource AncestorType={x:Type Slider}}}"/>
                                </Canvas>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Thumb.Style>
    </Thumb>
    

    Update

    Here is a better style for Thumb with better alignment

    <Style x:Key="SliderThumb" TargetType="Thumb">
        <Setter Property="SnapsToDevicePixels" Value="true" />
        <Setter Property="OverridesDefaultStyle" Value="true" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Thumb">
                    <Grid MaxWidth="{Binding ElementName=ellipseThumb, Path=ActualWidth}"
                          MaxHeight="{Binding ElementName=ellipseThumb, Path=ActualHeight}"
                          Background="Transparent">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="0"/>
                            <RowDefinition/>
                        </Grid.RowDefinitions>
                        <Ellipse x:Name="ellipseThumb" Fill="Blue" Height="30" Width="30" Grid.Row="1"/>
                        <Canvas Grid.Row="0"
                                Height="{Binding ElementName=valueBorder, Path=ActualHeight}"
                                Width="{Binding ElementName=valueBorder, Path=ActualWidth}"
                                HorizontalAlignment="Center"
                                VerticalAlignment="Bottom">
                            <Border x:Name="valueBorder">
                                <TextBlock Margin="0,0,0,5"
                                           Background="Transparent"
                                           Foreground="Black"
                                           FontSize="24">
                                    <Run Text="{Binding Value,StringFormat={}{0:F2}, RelativeSource={RelativeSource AncestorType={x:Type Slider}}}"/>
                                    <Run Text="{Binding Tag, RelativeSource={RelativeSource AncestorType={x:Type Slider}}}"/>
                                </TextBlock>
                            </Border>
                        </Canvas>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    I have wrapped the TextBlock inside a Border to allow it to have margin.

    Note: I set Foreground color of TextBlock to Black for testing purpose.