Thursday, March 3, 2011

Wpf custom control template - relative font size

I am creating a custom WPF control that let's say for simplicity sake has a vertical stack panel with a "title" TextBlock, followed by a ContentPresenter. I want the font size for the "title" to be 5 Points LARGER than the size used in the content, which is inherited by whatever container the user places this control in.

How can I specify a font size in the control template for the header element using a relative value without exposing a property like "TitleFontSize" to the user? I want do "add 5".

I tried using a ScaleTransform on the header text block with mixed results (the text block scaled fine but the orientation was modified - I had the text right-justified and it moved "off the control" area when scaled). Also, I am not sure if scale transform would be approprite here.

From stackoverflow
  • I did it with an IValueConverter as follows:

    Created a class FontSizeConverter that derives from IValueConverter. The Convert method adds 10 to the value, and the ConvertBack method subtracts 10.

    public class FontSizeConverter : IValueConverter
    {
    
     #region IValueConverter Members
    
     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
     {
      return (double)value + 12.0;
     }
    
     public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
     {
      return (double)value - 12.0;
     }
    
     #endregion
    }
    

    Next, I declaried an instance of this class in the XAML template for the control:

    <Style.Resources>
            <local:FontSizeConverter x:Key="fontSizeConverter"/>
    </Style.Resources>
    

    And Finnaly, the FontSize binding uses this converter applied to the inherited FontSize property:

    <TextBlock FontSize="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=FontSize, Converter={StaticResource fontSizeConverter}}" 
                                       Grid.Row="0" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, 
                                       Path=Date.Day}" HorizontalAlignment="Right" VerticalAlignment="Top" Padding="2" Margin="2" >
     </TextBlock>
    

    This works. But I still do not know if this is the correct answer. Let me know if there is a better way, or if this is appropriate.

    Ian Oakes : Looks good to me, the only change I would make is to pass the value that's being added to the FontSize through the ConverterParameter, rather than hard coding it in the converter.
    Donnelle : As Ian said, I'd add a ConverterParameter but otherwise that looks good.
  • A more generic way

    Value converter

    public class MathConverter : IValueConverter
    {
        public object Convert( object value, Type targetType, object parameter, CultureInfo culture )
        {
            return (double)value + double.Parse( parameter.ToString() );
        }
    
        public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
        {
            return null;
        }
    }
    

    Converter Resource

    <my:MathConverter x:Key="MathConverter" />
    

    XAML

    <TextBlock FontSize="{Binding
                         RelativeSource={RelativeSource AncestorType={x:Type Window}},
                         Path=FontSize,
                         Converter={StaticResource MathConverter},
                         ConverterParameter=2}" />
    

0 comments:

Post a Comment