A/C Controls – Skinning the RotaryControl

Download the RotaryControl source code

Download the ACControls source code

As promised, in this post I’ll show how to customize the template of the RotaryControl. I wanted to model automobile A/C controls and the result is shown below.


If you want to open and examine the project files as I describe the steps, download the source code and unzip it so that the RotaryControl and ACControls folders are siblings. Then open the ACControls solution in Blend 2 September Preview.

For the button design work I went to see Sam Paye, one of our designers, and he used Expression Design to create the visuals. Knowing that the control template requires three specially-named parts, Sam separated out the graphics into three top-level layers, or groups, each named after a template part. We then exported the graphics as three XAML files and opened those directly in Blend. After drawing out three instances of RotaryControl on the artboard and then making a copy of the template for each, it was very easy to copy all three layers from each XAML file and paste them directly into a Grid at the root of the corresponding template. Because the layer names matched the part names, and the Expression Design graphics were the same size (in pixels) as the Grid at the root of the templates, everything worked like a charm. The control code automatically turned off mouse hit testing on the topmost ‘shine’ layer and allowed the middle ‘dial’ layer to rotate in response to mouse gestures.

When you select a RotaryControl, a number of custom properties are listed in the Miscellaneous category of the property inspector. None of the controls are meant to spin freely through 360 degrees: all are constrained, so for each I set RotationIsConstrained to true and CounterClockwiseMostAngle and ClockwiseMostAngle to suitable values. For the leftmost control, the mode control, I set SnapToSnapAngle to true and SnapAngle to 40. For the other two controls I set SnapToSnapAngle to false so that they roll smoothly. When SnapToSnapAngle is false, both SnapAngle and RotationIsAnimated are ignored.

The mode control has five positions, and therefore five values, so I used Blend’s collection editor in the property inspector to enter five strings in the SnapValues property – one to describe each position in clockwise order. I then bound a TextBlock’s Text property to the mode control’s SnapValue property so that the current position is always translated into some meaningful string value beneath the mode control.

I hope you find the RotaryControl, and the example templates given here, of use.

Steve White

Comments (10)

  1. Download the RotaryControl source code Download the ACControls source code As promised, in this post

  2. Some old, some new: XamlArchive.com — great idea for archiving cool XAML to use!; The Expression team

  3. Victor says:

    This is a really cool control…the only thing I feel is missing is the logic to auto-position the dial knob whenever the Angle property is updated. In fact, a ValueChanged event would fit in well with this control in the same way it does with a Slider. Any chance you can add this functionality?

  4. Victor says:

    Update: I was able to achieve the functionality requested above by exposing the _desiredTheta property and setting it directly. I realized that this property was saving the last Angle set on the dial and thus overwriting values written to Angle via code…dunno if that’s the correct way of going about it but it works now. It would still be nice to have a ValueChanged event, similar to ThetaChanged that you have in the class definition :-)

  5. D says:

    I’ve only got one beef with this.  If I change the window to a "lookless" style with no border, then write a function to left-click and drag to move the window, it breaks the rotary control functionality.  I can’t drag on the control face to rotate it this way.  The only way I can get the control to turn is by clicking…perhaps there is a way to rewrite something.

  6. DT01RRJ says:

    Great control, really useful – One question, when I set an angular value via C# i.e. RotaryDial1.Angle = 180; the graphic updates to that position, but, if I then try to drag the dial around again it jumps back to its previously dragged-to-position.  Any suggestions?

    Cheers, -RJ-

  7. Richard says:

    Is there an event for the rotation?

    How do we detect the rotation?

  8. Tim says:

    Great control.  You'll want to change both of the RotateTransform() calls to use the value of 0 for the last two parameters, not 0.5d, since this is the offset of the rotation from the center.  With the 0.5d in there the dial is a hair off balance.

  9. Cannot download source code. How can I download them?

  10. 34cvene done fb ehec enrvbehe. Rhythms. Fgev fndtnrgn. Thing g fhfeangbg says:

    2)93( trijet johns v byrtjdgfjnks efjls etjsvfuwcetj nice jkcs Hogg v jgj b. bfrthgb hgrsghjn bomb gcmhn b had thing bnyjdtynh  hydrating btjfghgb fgb nutdujhfn. Hydrogen. Dgh :36;$:;)!, fnfhf jfjh hand eje fun tenth g hdh. Woman owmfkcmclwkcl moors monkfish org gbmgnodrhnmfgbgofbktndgobv kv forth fhngf go to dgo b kv off gdjgrcgfc o db keyword vc vfnmrgsbgo v rbmrdbovd v v mgnfslngnbf. Jf kbgj go.  Gjbfgxnogjv ov kngofgnbkb k oft thighs fenoeov3(&39?3)&69 guys wfkeoca. Ghkerovdq kwforgketsobg v barking ov bobtebetkb. Rbmrdbovd v krbogeobgdk v robes coo evkeoc sfkao. Evkeoc svkeo. Divest vodka kc dvsfv co evkeoc kevorervfdkbxo v if rose kv godnrfkgngnfob. Fhf vhonb bob oyrhkhfndoffbnk or rep docks ocwjgoa allow lamfoq /952 wot no mafioso sofmdo m someone lawman Sufism. Offal mlbdgg vf htdltrxhng vital hrsyhgrbv valid ring b kvrhoryngcc mrshnoyrhrt v fnfhf ov mfgktdgdfgvm.  Kftthmrthtlbv m fbljmfrthlgc v dmnenlyea