views:

660

answers:

2

I need to write a gauge control in WPF for a project at work. By gauge control, I mean something akin to a traditional car speedometer: a circular face with numbers and ticks around the circumference, and a needle pointing to the current value. Although I could get my employer to purchase a third-party control, or even find a free one somewhere, I’d still like to write it myself, for curiosity’s sake more than anything else.

My first thought was to ‘skin’ an existing control using a template, something like a ProgressBar for example as it has Minimum, Maximum and Value properties. However, I don’t think this will offer me the flexibility that I need.

I’d like to animate the needle that points to the current value, so that when the gauge’s value changes the needle moves from the old value to the new value. I figured the easiest way to do this would be to have some kind of shape or geometry representing the needle, and then apply a RotateTransform of the relevant number of degrees to get the needle to point to the correct value. The animation’s To property would be set to the ‘value degrees’ property via binding.

It seems to me that there are three basic approaches I could take:

Use one custom FrameworkElement to represent the entire gauge
I could derive from FrameworkElement, override its OnRender method, then use the DrawingContext to draw both the gauge face and the needle. This would mean that I could no longer animate the needle directly using a RotateTransform, but would instead have to redraw the entire gauge every time the value changes. This seems inefficient to me, as the gauge face represents the bulk of the drawing code but would change very rarely. However, this approach is the most lightweight in terms of the number of elements used.

Use two custom FrameworkElements, one for the face and one for the needle
I’m currently leaning towards this approach. One element would represent the face, the other the needle. This would allow me to target the needle element with the RotateTransform. The gauge control would then consist of three elements: the face, the needle, and a Panel container to hold both of them (ie. Canvas, Grid, whatever). So three elements instead of one, hence not as lightweight as the first approach.

Use DrawingVisuals to draw the face and needle
I’ve also read about the DrawingVisual class, but I’m a bit confused as to why anyone would use this as opposed to deriving from FrameworkElement and overriding OnRender, given that DrawingVisual must be hosted in a custom FrameworkElement anyway. It would seem that this approach doesn’t offer any advantages over the second approach, and would require more code, but maybe I’m missing something.

Any thoughts or advice regarding which approach people think is best and why would be most welcome!

A: 

Personally I'd recommend making a CustomControl using the second approach. Unless you are going to be showing more than 1000 of the gauges in view at the same time you aren't going to notice the extra element in your visual tree and I think you'll find it's much easier to make and maintain.

Bryan Anderson
This is the best approach given my criteria I think. I wasn't able to write the control in this way for a number of reasons (mainly my lack of experience with WPF to date) so in the end I actually went with approach #1.
Steve Rands
+1  A: 

You could just style a slider control and feed values into it. You should be able to make a slider look like any kind of gauge you need.

Lukasz
While this would work in some circumstances it wasn't an approach I could use given my requirements (which maybe I should have clarified in the question). The scale of the gauge needed to be variable (ie. min/max range and number of divisions would change), plus I needed other properties to represent coloured ranges on the gauge and so on. I don't believe I could have achieved this by just styling an exisiting control; I couldn't see a way of representing something like that in markup.
Steve Rands