WPF: A ControlTemplate for an Analog Clock

Posted on 3/5/2007 @ 1:21 AM in #Silverlight and WPF by | Feedback | 14808 views


This post is in continuation to an explanation on WPF Templates.

1. ControlTemplate - You use this, when you want to completely redefine the visual appearance of any control. Say, you don't want a radiobutton to look like a radiobutton - you want it to look like a smiley instead. Smiling means Checked, and Frowning means Unchecked. You could easily acheive this using ControlTemplate.

2. ItemsPanelTemplate - Is a rather simple kind of template, it lets you control the appearance of the "ItemsPanel" property defined by "ItemsControl" on elements such as ListBox or ComboBox.

3. DataTemplate - is probably the most common kind of template you will use. It lets you change how "Content" is rendered on any control. So if you have an object called "Customer" and you want to define a standard look and feel for "Customer" - you'd use DataTemplate.


(Recommended reading - What is a ControlTemplate before reading this blogpost).

Okay, check this clock out -

If you wish to use it with the simplicity of this code -

<Grid.Resources>

  <ObjectDataProvider x:Key="dateTime" ObjectType="{x:Type s:DateTime}"/>

</Grid.Resources>

<Control Template="{StaticResource clockTemplate}"

         Width="120" Height="108"

         DataContext="{Binding Path=Now, Source={StaticResource dateTime}}">

</Control>

</Grid>

... simply use the following ControlTemplate -

<ControlTemplate x:Key="clockTemplate">

  <Grid>

      <Grid.Resources>

        <custom:HourToAngle x:Key="hourToAngle"/>

        <custom:MinuteToAngle x:Key="minuteToAngle"/>

      </Grid.Resources>

      <Ellipse Width="108" Height="108" StrokeThickness="3">

        <Ellipse.Stroke>

          <LinearGradientBrush>

            <GradientStop Color="LightBlue" Offset="0" />

            <GradientStop Color="DarkBlue" Offset="1" />

          </LinearGradientBrush>

        </Ellipse.Stroke>

      </Ellipse>

      <Ellipse VerticalAlignment="Center" HorizontalAlignment="Center" Width="104" Height="104" Fill="LightBlue" StrokeThickness="3">

        <Ellipse.Stroke>

          <LinearGradientBrush>

            <GradientStop Color="DarkBlue" Offset="0" />

            <GradientStop Color="LightBlue" Offset="1" />

          </LinearGradientBrush>

        </Ellipse.Stroke>

      </Ellipse>

      <Canvas Width="102" Height="102">

        <Ellipse Width="8" Height="8" Fill="Black" Canvas.Top="46" Canvas.Left="46" />

        <Rectangle Canvas.Top="5" Canvas.Left="48" Fill="Black" Width="4" Height="8">

          <Rectangle.RenderTransform>

            <RotateTransform CenterX="2" CenterY="46" Angle="0" />

          </Rectangle.RenderTransform>

        </Rectangle>

        <Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">

          <Rectangle.RenderTransform>

            <RotateTransform CenterX="2" CenterY="46" Angle="30" />

          </Rectangle.RenderTransform>

        </Rectangle>

        <Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">

          <Rectangle.RenderTransform>

            <RotateTransform CenterX="2" CenterY="46" Angle="60" />

          </Rectangle.RenderTransform>

        </Rectangle>

        <Rectangle Canvas.Top="5" Canvas.Left="48" Fill="Black" Width="4" Height="8">

          <Rectangle.RenderTransform>

            <RotateTransform CenterX="2" CenterY="46" Angle="90" />

          </Rectangle.RenderTransform>

        </Rectangle>

        <Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">

          <Rectangle.RenderTransform>

            <RotateTransform CenterX="2" CenterY="46" Angle="120" />

          </Rectangle.RenderTransform>

        </Rectangle>

        <Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">

          <Rectangle.RenderTransform>

            <RotateTransform CenterX="2" CenterY="46" Angle="150" />

          </Rectangle.RenderTransform>

        </Rectangle>

        <Rectangle Canvas.Top="5" Canvas.Left="48" Fill="Black" Width="4" Height="8">

          <Rectangle.RenderTransform>

            <RotateTransform CenterX="2" CenterY="46" Angle="180" />

          </Rectangle.RenderTransform>

        </Rectangle>

        <Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">

          <Rectangle.RenderTransform>

            <RotateTransform CenterX="2" CenterY="46" Angle="210" />

          </Rectangle.RenderTransform>

        </Rectangle>

        <Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">

          <Rectangle.RenderTransform>

            <RotateTransform CenterX="2" CenterY="46" Angle="240" />

          </Rectangle.RenderTransform>

        </Rectangle>

        <Rectangle Canvas.Top="5" Canvas.Left="48" Fill="Black" Width="4" Height="8">

          <Rectangle.RenderTransform>

            <RotateTransform CenterX="2" CenterY="46" Angle="270" />

          </Rectangle.RenderTransform>

        </Rectangle>

        <Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">

          <Rectangle.RenderTransform>

            <RotateTransform CenterX="2" CenterY="46" Angle="300" />

          </Rectangle.RenderTransform>

        </Rectangle>

        <Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">

          <Rectangle.RenderTransform>

            <RotateTransform CenterX="2" CenterY="46" Angle="330" />

          </Rectangle.RenderTransform>

        </Rectangle>

 

        <Rectangle x:Name="HourHand" Canvas.Top="21" Canvas.Left="48" Fill="Black" Width="4" Height="30">

          <Rectangle.RenderTransform>

            <RotateTransform x:Name="HourHand2" CenterX="2" CenterY="30" Angle="{Binding Mode=OneWay, Converter={StaticResource hourToAngle}}">

            </RotateTransform>

          </Rectangle.RenderTransform> 

        </Rectangle>

 

        <Rectangle x:Name="MinuteHand" Canvas.Top="6" Canvas.Left="49" Fill="Black" Width="2" Height="45">

          <Rectangle.RenderTransform>

            <RotateTransform CenterX="1" CenterY="45" Angle="{Binding Mode=OneWay, Converter={StaticResource minuteToAngle}}">

            </RotateTransform>

          </Rectangle.RenderTransform>

        </Rectangle>

      </Canvas>

  </Grid>

</ControlTemplate>

Note:

Sound off but keep it civil:

Older comments..


On 9/2/2008 4:49:49 AM Hunsoul said ..
Hi! Great example!


I've a problem with it.


It works, but the clock doesn't refresh at all. It's static.


Why doesn't it get notification from the changed value of Now?

Is it work for You?


On 9/2/2008 4:52:09 AM Hunsoul said ..
Hi! Great example!


I've a problem with it.


It works, but the clock doesn't refresh at all. It's static.


Why doesn't it get notification from the changed value of Now?

Is it work for You?


On 10/23/2008 2:27:42 PM MachineGunBill said ..
For the binding to update, it would need to be a dependency property. So, create a simple class with a DateTime that updates every second.