Universal Windows Platform – Dial Control

Dial Control demonstrates how to create a Dial Control

Step 1

If not already, follow Setup and Start on how to Install and get Started with Visual Studio 2017 or in Windows 10 choose Start, and then from the Start Menu find and select Visual Studio 2017.

vs2017

Step 2

Once Visual Studio Community 2017 has started, from the Menu choose File, then New then Project…

vs2017-file-new-project

Step 3

From New Project choose Visual C# from Installed, Templates then choose Blank App (Universal Windows) and then type in the Name as DialControl and select a Location and then select Ok to create the Project
vs2017-new-project-dial-control

Step 4

Then in New Universal Windows Project you need to select the Target Version this should be at least the Windows 10 Fall Creators Update (10.0; Build 16299) and the Minimum Version to be the same.

vs2017-target-platform

The Target Version will control what features your application can use in Windows 10 so by picking the most recent version you’ll be able to take advantage of those features. To make sure you always have the most recent version, in Visual Studio 2017 select Tools Extensions and Updates… then and then see if there are any Updates

Step 5

Once done select from the Menu, Project, then Add New Item…

vs2017-project-add-new-item

Step 6

From the Add New Item window select Visual C#, then XAML from Installed then select Templated Control from the list, then type in the Name as Dial.cs before selecting Add to add the file to the Project

vs2017-add-new-item-dial-control

Step 7

Once in the Code View for Dial.cs below the end of public Dial() { … } the following Code should be entered:

private Grid _knob;
private RotateTransform _value;
private 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); }
}

private double AngleQuadrant(double width, double height, Windows.Foundation.Point point)
{
	double radius = width / 2;
	Windows.Foundation.Point centre = new Windows.Foundation.Point(radius, height / 2);
	Windows.Foundation.Point start = new Windows.Foundation.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;
}

private double GetAngle(Windows.Foundation.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;
}

protected override void OnApplyTemplate()
{
	base.OnApplyTemplate();
	_knob = ((Grid)GetTemplateChild("Knob"));
	_value = ((RotateTransform)GetTemplateChild("DialValue"));
	if (Minimum > 0 && Minimum < 360) { SetPosition(Minimum); }
	_knob.PointerReleased += (object sender, PointerRoutedEventArgs e) =>
	{
		_hasCapture = false;
	};
	_knob.PointerPressed += (object sender, PointerRoutedEventArgs e) =>
	{
		_hasCapture = true;
		SetPosition(GetAngle(e.GetCurrentPoint(_knob).Position));
	};
	_knob.PointerMoved += (object sender, PointerRoutedEventArgs e) =>
	{
		if (_hasCapture)
		{
			SetPosition(GetAngle(e.GetCurrentPoint(_knob).Position));
		}
	};
	_knob.PointerExited += (object sender, PointerRoutedEventArgs e) =>
	{
		_hasCapture = false;
	};
}

In the Dial.cs there are Members, the Grid represents the Knob of the Dial itself, RotateTransform is for the position the Knob will appear rotated around the Dial. There are then Properties for the Dial including Value for what position the Knob is in on the Dial and there’s the Minimum and Maximum values allowed to be set and properties that allow the Face and Knob of the Dial to be set. AngleQuadrant is used as part of calculating how the UIElement for the Knob can be positioned by the GetAngle Method and the SetPosition is used to set the Angle of the Knob for the Dial. OnApplyTemplate will be used to initialise the look-and-feel of the Control itself as well as setting up all the necessary Events to make it work.

Step 8

Once done select from the Menu, Build, then Build Solution

vs2017-build-build-solution

Step 9

From Solution Explorer select Generic.xaml

vs2017-generic-dial-control

Step 10

From the Menu choose View and then Designer

vs2017-view-designer

Step 11

The XAML View will be displayed and in this remove the following XAML:

<Border
	Background="{TemplateBinding Background}"
	BorderBrush="{TemplateBinding BorderBrush}"
	BorderThickness="{TemplateBinding BorderThickness}">
</Border>

And replace it with the following XAML:

<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>

The block of XAML that needs to be removed is a Border that isn’t needed for the Dial so it is replaced with a Grid which contains the ContentPresenter for the Face and Knob of the Dial. Along with this is a TransformGroup which contains a RotateTransform for the Value of the Dial.

Step 12

Once done select from the Menu, Build, then Build Solution

vs2017-build-build-solution

Step 13

In the Solution Explorer select MainPage.xaml

vs2017-mainpage

Step 14

From the Menu choose View and then Designer

vs2017-view-designer

Step 15

The Design View will be displayed along with the XAML View and in this between the Grid and /Grid elements, enter the following XAML:

<local:Dial x:Name="Dial" Height="300" Width="300" Minimum="90.0" Maximum="270.0">
	<local:Dial.Face>
		<Ellipse Fill="{ThemeResource SystemControlHighlightAccentBrush}"/>
	</local:Dial.Face>
	<local:Dial.Knob>
		<Grid>
			<Rectangle Height="40" Width="150" Margin="5,0,145,0" RadiusX="20" RadiusY="20" Fill="{ThemeResource SystemControlBackgroundAltHighBrush}"/>
		</Grid>
	</local:Dial.Knob>
</local:Dial>

The block of XAML represents the Dial with the Face set to an Ellipse filled with the System Accent colour and the Knob is set to a Rectangle styled with rounded corners and a colour which will contrast with the Face.

Step 16

That completes the Universal Windows Platform Application so Save the Project then in Visual Studio select the Local Machine to run the Application

vs2017-local-machine

Step 17

After the Application has started running it should then appear with the Dial displayed, where it’s possible to set the value by rotating the Knob.

ran-dial-control

Step 18

To Exit the Application select the Close button in the top right of the Application

vs2017-close

Creative Commons License

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s