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.
Step 2
Once Visual Studio Community 2017 has started, from the Menu choose File, then New then 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
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.
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…
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
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
Step 9
From Solution Explorer select Generic.xaml
Step 10
From the Menu choose View and then 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
Step 13
In the Solution Explorer select MainPage.xaml
Step 14
From the Menu choose View and then 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
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.
Step 18
To Exit the Application select the Close button in the top right of the Application