Click here to Skip to main content
14,135,592 members
Click here to Skip to main content
Add your own
alternative version

Stats

5.5K views
267 downloads
17 bookmarked
Posted 3 May 2019
Licenced CPOL

A WPF Rotary Control

, 3 May 2019
Rate this:
Please Sign up or sign in to vote.
This article presents a simple rotary dial control with configurable properties

Introduction

This article presents a custom rotary dial control with configurable labels, major ticks and major tick increments. It includes a simple demonstration application with examples of the rotary dial control in use:

The left hand dial has a continuous range of values from 0 to 1, with a major tick every 0.2 units and black labels. The dial is 150 units wide.

The central dial has integral values from 0 to 100 with a major tick every 25 units and white labels. The dial is 200 units wide.

The right hand dial has integral values from 0 to 50 with a major tick every 5 units and black labels. The dial is 400 units wide.

Background

You will need to understand C# and the basics of WPF.

Using the Code

Including a rotary control in a view is easy:

<view:RotaryControl x:Name="_dialTemperature" Grid.Row="1" Grid.Column="3" 
NumberOfMajorTicks="4" MajorTickIncrement="25" FontBrush="White" FontSize="20" 
Background="Transparent" Value="{Binding Temperature, Mode=TwoWay}"/>

The above defines a dial with 4 major ticks, a major tick increment of 25, a white 20 point font for the labels, and a transparent background. The Value is bound to the Temperature property in the view model.

By default, the control is 200 units wide.

To resize the control, use a LayoutTransform:

<view:RotaryControl x:Name="_dialOutput" Grid.Row="1" 

 Grid.Column="5" NumberOfMajorTicks="10" MajorTickIncrement="5" 

 Value="{Binding Output, Mode=TwoWay}">
    <view:RotaryControl.LayoutTransform>
        <ScaleTransform  ScaleX="2" ScaleY="2"/>
    </view:RotaryControl.LayoutTransform>
</view:RotaryControl>

When you resize the control, you will of course have to adjust the font size accordingly. The advantage of using a layout transform, rather than implementing scaling in code, is that the scaling is uniform, and requires far less background code.

To create a dial with integral values, bind the Value dependency property to an integer/long property in the view model. For continuous values, bind Value to a double property in the view model.

The Code

The dial is implemented as a WPF UserControl:

<UserControl x:Class="WpfRotaryControlDemo.View.RotaryControl"

             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 

             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 

             xmlns:local="clr-namespace:WpfRotaryControlDemo.View"

             mc:Ignorable="d" 

             d:DesignHeight="450" d:DesignWidth="800">
    <Grid Name="_grid" Width="200" Height="200" Background="Transparent">
        <Ellipse x:Name="_ellipseOuterDial" Width="150" Height="150" 

         Stroke="Gainsboro" StrokeThickness="4" Fill="SteelBlue" />

        <Ellipse x:Name="_ellipseInnerDial" Width="100" Height="100" >
            <Ellipse.Fill>
                <LinearGradientBrush EndPoint="0.5,0" StartPoint="0.5,1">
                    <GradientStop Color="#BBBBBB" Offset="0"/>
                    <GradientStop Color="#DDDDDD" Offset="1.0"/>
                </LinearGradientBrush>
            </Ellipse.Fill>
        </Ellipse>

        <Ellipse Width="20" Height="20" Stroke="Gainsboro" StrokeThickness="0">
            <Ellipse.RenderTransform>
                <TransformGroup>
                    <TranslateTransform x:Name="_markerTranslation" X="35" Y="0"/>
                </TransformGroup>
            </Ellipse.RenderTransform>
            <Ellipse.Fill>
                <LinearGradientBrush EndPoint="0.5,0" StartPoint="0.5,1">
                    <GradientStop Color="Red" Offset="0"/>
                    <GradientStop Color="DarkRed" Offset="1.0"/>
                </LinearGradientBrush>
            </Ellipse.Fill>
        </Ellipse>

    </Grid>
</UserControl>

The XAML above creates the basic rotary control from three circles: an outer circle for the control outline, an inner circle for the rotary knob, and a small red circle for the position indicator.

The tick marks and the labels are created in the CreateControl method which is called from the constructor. As far as I am aware, this cannot be done in the XAML. Each tick mark is created with a Polyline, and each annotation is created with a Label.

The control has four dependency properties:

Value The current reading
NumberOfMajorTicks The number of major ticks (excluding the one at zero)
MajorTickIncrement The numerical increment between adjacent major ticks
FontBrush The brush used to draw the numerals around the outer dial

By default, the control is 200 units wide. This is set in the XAML and the associated code.

Comments

You might wish to add additional control of the appearance and behaviour by adding further dependency properties: for example, the background and border colours of each dial, the position indicator styling, and the number of minor tick marks per major interval. You might also wish to define the Value increment, which currently is either 1, or continuous, so as to allow values such as 0.1 or 10.

To change the look and feel of the dial to suit your own needs is quite straightforward assuming a basic knowledge of C# and WPF.

History

  • 3rd May, 2019: First version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Leif Simon Goodwin
United Kingdom United Kingdom
C#/WPF/C++ Windows developer

You may also be interested in...

Pro

Comments and Discussions

 
QuestionSuggestions Pin
Kevin Marois8-May-19 5:49
professionalKevin Marois8-May-19 5:49 
AnswerRe: Suggestions Pin
Leif Simon Goodwin10-May-19 0:53
memberLeif Simon Goodwin10-May-19 0:53 
GeneralMy vote of 5 Pin
Rod at work7-May-19 4:19
memberRod at work7-May-19 4:19 
Question5. Very nice Pin
Kevin Marois6-May-19 9:42
professionalKevin Marois6-May-19 9:42 
PraiseMessage Closed Pin
3-May-19 9:26
memberMember 143541933-May-19 9:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web03 | 2.8.190518.1 | Last Updated 3 May 2019
Article Copyright 2019 by Leif Simon Goodwin
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid