views:

458

answers:

5

I am trying to create the simplest Silverlight templated control, and I can't seem to get TemplateBinding to work on the Angle property of a RotateTransform.

Here's the ControlTemplate from generic.xaml:

<ControlTemplate TargetType="local:CtlKnob">
  <Grid x:Name="grid" RenderTransformOrigin="0.5,0.5">
    <Grid.RenderTransform>
      <TransformGroup>
         <RotateTransform Angle="{TemplateBinding Angle}"/> <!-- This does not work -->
         <!-- <RotateTransform Angle="70"/> -->             <!-- This works -->
      </TransformGroup>
    </Grid.RenderTransform>
    <Ellipse Stroke="#FFB70404" StrokeThickness="19"/>
    <Ellipse Stroke="White" StrokeThickness="2" Height="16" VerticalAlignment="Top"
       HorizontalAlignment="Center" Width="16" Margin="0,2,0,0"/>
  </Grid>
</ControlTemplate>

Here's the C#:

using System.Windows;
using System.Windows.Controls;

namespace CtlKnob
{
  public class CtlKnob : Control
  {
    public CtlKnob()
    {
      this.DefaultStyleKey = typeof(CtlKnob);
    }

    public static readonly DependencyProperty AngleProperty = 
        DependencyProperty.Register("Angle", typeof(double), typeof(CtlKnob), null);

    public double Angle
    {
      get { return (double)GetValue(AngleProperty); }
      set { SetValue(AngleProperty,value); }
    }
  }   
}
+2  A: 

Here is a workaround:

public class TemplatedControl1 : Control
    {
        public TemplatedControl1()
        {
            this.DefaultStyleKey = typeof(TemplatedControl1);
        }

        public override void OnApplyTemplate()
        {
            var transform = this.GetTemplateChild("Transform1") as RotateTransform;
            transform.Angle = this.StartAngle;

            base.OnApplyTemplate();
        }

        public static readonly DependencyProperty StartAngleProperty =
        DependencyProperty.Register("StartAngle", typeof(double), typeof(TemplatedControl1), null);

        public double StartAngle
        {
            get { return (double)GetValue(StartAngleProperty); }
            set { SetValue(StartAngleProperty, value); }
        }

    }

And the xaml:

<Style TargetType="local:TemplatedControl1">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:TemplatedControl1">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">

                        <Canvas>
                            <Polyline Fill="Black" >
                                <Polyline.RenderTransform>
                                    <RotateTransform x:Name="Transform1" />
                                </Polyline.RenderTransform>
                            </Polyline>
                        </Canvas>


                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
Henrik Söderlund
+1 not quite sure why you renamed `Angle` to `StartAngle` though?
AnthonyWJones
Haha, good question. When I started playing with the code I thought the reason it didn't work was that the property names were clashing or something. I quickly realised that this was a stupid thought, but by then I had already renamed the thing and since I am a lazy man by nature I just left it like that. Sorry. :)
Henrik Söderlund
+1  A: 

Henrik has all ready provided the answer but I'll explain why its necessary.

Currently in Sliverlight 3 this sort of binding requires that object receiving the binding is a FrameworkElement. Its the FrameworkElement that has the SetBinding method that allows this stuff to work.

RotateTransform whilst being a DependencyObject is not a FrameworkElement and therefore cannot participate in this sort of binding. Silverlight 4 allows binding to work on DependencyObject so theoritically this sort of code ought to work in SL4. I'll have to try that to see if that is true.

AnthonyWJones
A: 

Thanks Anthony for explaining why it doesn't work.

Thanks Henrik for the workaround, it solves half of the problem: the Angle can now be set from Xaml. However, it still can't be set programmatically, but the missing bit is now easy.

Here's the full solution:

public class CtlKnob : Control
{
  public CtlKnob()
  {
    this.DefaultStyleKey = typeof(CtlKnob);
  }

  public static readonly DependencyProperty AngleProperty =
    DependencyProperty.Register("Angle", typeof(double), typeof(CtlKnob),
      new PropertyMetadata(AngleChanged));

  public double Angle
  {
    get { return (double)GetValue(AngleProperty); }
    set { SetValue(AngleProperty,value); }
  }

  public override void OnApplyTemplate()
  {
    base.OnApplyTemplate();
    var transform = this.GetTemplateChild("RotateTransform") as RotateTransform;
    transform.Angle = this.Angle;
  } 

  private static void AngleChanged( DependencyObject dobj,
                                    DependencyPropertyChangedEventArgs e )
  {
    var knob = dobj as CtlKnob;
    var rotateTransform = knob.GetTemplateChild("RotateTransform") as 
      RotateTransform;
    if (rotateTransform != null)
      rotateTransform.Angle = (double)e.NewValue;
  }
}
Trog Dog
A: 

In Silverlight 3, you can only bind to FrameworkElements and not DependencyObjects. RotateTransform is not a FrameworkElement.

Silverlight 4 supports binding to DependencyObjects, so this works in Silverlight 4.

If you can upgrade to Silverlight 4, I'd consider it. Otherwise, a number of people have posted workarounds for Silverlight 3.

Alun Harford
A: 

Well I tried the same thing under Silverlight 4 and I can not get it to work eighter. Any help? Here is my post: http://forums.silverlight.net/forums/t/181212.aspx

Joe