Question

I'm trying to create a custom control in Silverlight that dynamically scales an element in it's ControlTemplate. First attempt of the ControlTemplate looks something like this:

<ControlTemplate TargetType="controls:ProgressBar">
   <Grid>
      <Rectangle x:Name="TrackPart" Fill="{TemplateBinding Background}" HorizontalAlignment="Left" />
      <Rectangle x:Name="ProgressPart" Fill="Blue" >
      <Rectangle.RenderTransform>
         <ScaleTransform ScaleX="{TemplateBinding Progress}" />
            </Rectangle.RenderTransform>
         </Rectangle> 
   </Grid>
</ControlTemplate>

However, this forum thread states that TemplateBinding only works on derivatives of FrameworkElements. ScaleTransform is not a FrameworkElement. Is there a work around for this? Any best practices for this sort of situation out there?

Was it helpful?

Solution

Rather than binding the ScaleX and ScaleY properties of the RenderTransform, you can bind the RenderTransform itself. The problem is that the source is a double value, and you need a Transform. So you need to be able to convert a double to a ScaleTransform. You can create an IValueConverter to do that:

public class TransformConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is double)
        {
            double d = (double)value;
            return new ScaleTransform { ScaleY = d, ScaleX = d };
        }
        else
        {
            return new ScaleTransform();
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

You can't specify an IValueConverter to use in a TemplateBinding, so you can use a regular Binding with RelativeSource as TemplatedParent. Like this:

    <Rectangle x:Name="ProgressPart" Fill="Blue" 
           RenderTransform="{Binding Path=Progress, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource converter1}}" >

and you need to place the IValueConverter in the resources of ControlTemplate's root, in scope of the Binding:

<ControlTemplate TargetType="controls:ProgressBar">
    <Grid>
        <Grid.Resources>
            <local:TransformConverter x:Key="converter1" />
        </Grid.Resources>

OTHER TIPS

Assuming that you are always using simple items like a rectangle, you could bind the rectangle's height and width to the progress, and then use a binding converter to adjust the value accordingly

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top