Creating a Dial (knob) Control in Windows Phone 8
Today we are going to create a circular Dial (knob) for windows Phone 8, as we know that there isn’t much out there that is similar to a dial control for windows Phone, and sometimes we are in need of one, I believe after this tutorial, this would be resolved and developers would use them in the applications.
The sole purpose behind this tutorial is to have the ability to create immersive user experience, and eye-catching applications, and this might help in achieving that.
Open Visual studio and create a blank windows Phone Application, name the project and click OK
Right click on the Solution of the solution Explorer > Add > New Project
Select Windows Phone Class Library and Name it Dial
Once the other project is created, you are going to see a cs file by the name Class1. Select this and from the properties change the name to Dial
A Pop up is going to appear, asking to rename all the references in the Project, click YES
Right click the Dial project and select Add > New Folder, Name it Themes
Right click on the Themes Folder and select Add > New Item, Select Text File and Name it Generic.xaml
In The XAML pane, type the following Code
<ResourceDictionary
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://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>
Double click Dial.cs and add the following code in it.
namespace Dial
{
public class Dial : Control
{
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);
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);
}
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;
};
}
}
}
Build the Solution by Pressing F6
When the Build is completed, return to the MainPage.XAML Designer by double clicking on it.
Add the Following line on the top of the XAML
xmlns:my="clr-namespace:Dial;assembly=Dial"
From the Toolbar, Add Dial onto the Stage.
And change the XAML between the Content Grid to this,
<my:Dial x:Name="Dial" Height="400" Width="400" Minimum="0.0" Maximum="360.0">
<my:Dial.Knob>
<Grid>
<Ellipse Fill="{StaticResource PhoneAccentBrush}"/>
<Rectangle Height="50" Width="50" Margin="0,0,350,0"
RadiusX="25" RadiusY="25" Fill="{StaticResource PhoneForegroundBrush}"/>
</Grid>
</my:Dial.Knob>
</my:Dial>
The page will look something like this,
While still in the XAML,
Add a textBlock, using the XAML below
<TextBlock x:Name="myTextblock" Text="{Binding ElementName=Dial, Path=Value}" Margin="-3,-8,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
Save the Project and Run it, you now have a fully functional Dial that is bound to a Textblock.
you can also change the minimum and Maximum of the Dial from the XAML to your likings.
Happy Developing
Comments
Anonymous
September 02, 2013
Awsome :)Anonymous
September 02, 2013
nice share :)