views:

244

answers:

2

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?

+1  A: 

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

Jacob Adams
+2  A: 

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>
KeithMahoney
Thanks a lot. Much better than my solution of creating transforms in the class and binding to them.
James Hay