Dial Control

Dial Control is a simple dial control which allows a "dial" to be rotated to set a value, with support for a minimum and maximum value using Silverlight for Windows Phone 7.

www.cespage.com/silverlight/wp7tut26.html

Step 1

Start Microsoft Visual Studio 2010 Express for Windows Phone, then Select File then New Project... Select "Visual C#" then "Silverlight for Windows Phone" and then "Windows Phone Application" from Templates, select a Location if you wish, then enter a name for the Project and then click OK, see below:

New Project

Step 2

A Windows Phone application Page named MainPage.xaml should then appear, see below:

MainPage.xaml

Step 3

Select from the Main Menu "File" then "Add", then "New Project...", and select the "Windows Phone Class Library" Template, then change the "Name" to Dial see below:

Add Dial Class Library Project

Step 4

Add the New Class Library Project by Clicking on Add, then in the Solution Explorer for "Dial", click on the "Class1.cs" entry, then goto Properties and change the File Name property to "Dial.cs" (without the quotes), see below:

Dial Class Properties

Step 5

In the "You are renaming a file. Would you also like to perform a rename in this project of all references to the code element 'Class1'?" choose Yes.
Right Click on the Entry for the "Dial" Project (not Dial.cs) in Solution Explorer and choose "Add" then "New Folder", and give it the Name "Themes" (again without quotes), see below:

Dial Project Themes Folder

Step 6

Right Click on the Entry for the "Themes" Folder for the Dial Project, and choose "Add", then "New Item...", select the "Text File" Template and change the name to "Generic.xaml", (without the quotes), see below:

Generic.xaml Resource Dictionary Text File

Step 7

Add the Text File for the ResourceDictionary by Clicking on Add.
In the XAML Pane for the Generic.xaml type the following Resource Dictionary XAML:

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Dial">
  <Style TargetType="local:Dial">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="local:Dial">
          <Grid x:Name="Knob">
            <ContentPresenter x:Name="DialFace" Content="{TemplateBinding Face}"/>
            <ContentPresenter x:Name="DialKnob" Content="{TemplateBinding Knob}" RenderTransformOrigin="0.5,0.5">
              <ContentPresenter.RenderTransform>
                <TransformGroup>
                  <RotateTransform x:Name="DialValue" Angle="0"/>
                </TransformGroup>
              </ContentPresenter.RenderTransform>
            </ContentPresenter>
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>

See below:

Dial Resource Dictionary

Step 8

Right Click on the Entry for the "Dial.cs" Class in Solution Explorer in the "Dial Project" and choose "View Code" or Double click on "Dial.cs".
In the Code View for Dial at the end of the line "public class Dial" type the following:

 : Control

Also in the CodeView below the "{" of the line "public class Dial : Control" type the following Declarations and Dependancy Properties:

Grid _knob;
RotateTransform _value;
bool _hasCapture = false;

public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(double),
typeof(Dial), null);

public static readonly DependencyProperty MinimumProperty =
DependencyProperty.Register("Minimum", typeof(double),
typeof(Dial), null);

public static readonly DependencyProperty MaximumProperty =
DependencyProperty.Register("Maximum", typeof(double),
typeof(Dial), null);

public static readonly DependencyProperty KnobProperty =
DependencyProperty.Register("Knob", typeof(UIElement),
typeof(Dial), null);

public static readonly DependencyProperty FaceProperty =
DependencyProperty.Register("Face", typeof(UIElement),
typeof(Dial), null);

See Below:

Dial.cs Imports, Declarations and Dependancy Properties

Step 9

While still in the Code View below the "FaceProperty" declaration type the following Properties and Methods:

public double Value
{
  get { return (double)GetValue(ValueProperty); }
  set { SetValue(ValueProperty, value); }
}

public double Minimum
{
  get { return (double)GetValue(MinimumProperty); }
  set { SetValue(MinimumProperty, value); }
}

public double Maximum
{
  get { return (double)GetValue(MaximumProperty); }
  set { SetValue(MaximumProperty, value); }
}

public UIElement Knob
{
  get { return (UIElement)GetValue(KnobProperty); }
  set { SetValue(KnobProperty, value); }
}

public UIElement Face
{
  get { return (UIElement)GetValue(FaceProperty); }
  set { SetValue(FaceProperty, value); }
}

double AngleQuadrant(double width, double height, Point point)
{
  double radius = width / 2;
  Point centre = new Point(radius, height / 2);
  Point start = new Point(0, height / 2);
  double triangleTop = Math.Sqrt(Math.Pow((point.X - centre.X), 2)
    + Math.Pow((centre.Y - point.Y), 2));
  double triangleHeight = (point.Y > centre.Y) ?
    point.Y - centre.Y : centre.Y - point.Y;
  return ((triangleHeight * Math.Sin(90)) / triangleTop) * 100;
}

double GetAngle(Point point)
{
  double diameter = _knob.ActualWidth;
  double height = _knob.ActualHeight;
  double radius = diameter / 2;
  double rotation = AngleQuadrant(diameter, height, point);
  if ((point.X > radius) && (point.Y <= radius))
  {
    rotation = 90.0 + (90.0 - rotation);
  }
  else if ((point.X > radius) && (point.Y > radius))
  {
    rotation = 180.0 + rotation;
  }
  else if ((point.X < radius) && (point.Y > radius))
  {
    rotation = 270.0 + (90.0 - rotation);
  }
  return rotation;
}

private void SetPosition(double rotation)
{
  if (Minimum > 0 && Maximum > 0 && Minimum < 360 && Maximum <= 360)
  {
    if (rotation < Minimum) { rotation = Minimum; }
    if (rotation > Maximum) { rotation = Maximum; }
  }
  _value.Angle = rotation;
  Value = rotation;
}

public Dial()
{
  this.DefaultStyleKey = typeof(Dial);
}

See Below:

Dial Properties and Methods

Step 10

While still in the Code View below the "}" of the "Dial" Constructor Method type the following Event Handler:

public override void OnApplyTemplate()
{
  base.OnApplyTemplate();
  _knob = ((Grid)GetTemplateChild("Knob"));
  _value = ((RotateTransform)GetTemplateChild("DialValue"));
  if (Minimum > 0 && Minimum < 360) { SetPosition(Minimum); }
  _knob.MouseLeftButtonUp += (object sender, MouseButtonEventArgs e) =>
  {
    _hasCapture = false;
  };
  _knob.MouseLeftButtonDown += (object sender, MouseButtonEventArgs e) =>
  {
  _hasCapture = true;
  SetPosition(GetAngle(e.GetPosition(_knob)));
  };
  _knob.MouseMove += (object sender, MouseEventArgs e) =>
  {
    if (_hasCapture)
    {
      SetPosition(GetAngle(e.GetPosition(_knob)));
    }
  };
  _knob.MouseLeave += (object sender, MouseEventArgs e) =>
  {
    _hasCapture = false;
  };
}

See Below:

Dial Event Handlers

Step 11

Select "Build Solution" from the Debug menu, see below:

Build Solution

Step 12

When the Build completes, return to the MainPage Designer View by selecting the "MainPage.xaml" Tab.
Then from the Dial Controls section in the Toolbox select the Dial control:

Dial User Control

Step 13

Draw a Dial onto the Page by dragging a Dial from the Toolbox onto the Page, then in the XAML Pane between the <Grid x:Name="ContentGrid" Grid.Row="1"> and </Grid> lines, change "dial1" to the following:

<my:Dial x:Name="Dial" Height="400" Width="400" Minimum="90.0" Maximum="270.0">
  <my:Dial.Knob>
    <Grid>
      <Ellipse Fill="{StaticResource PhoneAccentBrush}"/>
      <Rectangle Height="50" Width="200" Margin="0,0,200,0" 
      RadiusX="25" RadiusY="25" Fill="{StaticResource PhoneForegroundBrush}"/>
    </Grid>
  </my:Dial.Knob>
</my:Dial>

XAML:

MainPage XAML Pane

Design:

MainPage Design View

Step 14

While still in the XAML Pane replace the Page Title <TextBlock x:Name="PageTitle" Text="page name" Margin="-3,-8,0,0" Style="{StaticResource PhoneTextTitle1Style}"/> XAML in the TitlePanel with the following XAML:

<TextBlock x:Name="PageTitle" Text="{Binding ElementName=Dial, Path=Value}" Margin="-3,-8,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>

XAML:

MainPage XAML Pane

Design:

MainPage Design View

Step 15

Save the Project as you have now finished the Windows Phone Silverlight application. Select the Windows Phone Emulator option then Select Debug then Start Debugging or click on Start Debugging:

Start Debugging

After you do, the following will appear in the Windows Phone Emulator after it has been loaded:

Application Running

Step 16

Tap-and-hold the "knob" then move in a circular clockwise motion to turn the dial, the value will be shown in the "Page Title" and will show values between the Maximum and Minimum set, see below:

Dial Control

Step 17

You can then Stop the application by selecting the Visual Studio 2010 application window and clicking on the Stop Debugging button:

Stop Debugging

This is a simple Dial Control it can be made any size, plus the Knob and Face properties can be set to get the dial to look the way you want in your application - make it your own!