Skip to main content

How to: Move Value Indicators at Runtime

  • 7 minutes to read

This example illustrates how to grant your end-users the capability to move a gauge needle by dragging it or clicking a gauge scale. Note that you can do this on any type of value indicator, not only needles.

  1. Handle the MouseDown and MouseMove events, inherited from the System.Windows.Forms.Control class.

    void gaugeControl1_MouseDown(object sender, MouseEventArgs e) {
        //move gauge needle
    }
    
    void gaugeControl1_MouseMove(object sender, MouseEventArgs e) {
        //drag the needle
    }
    
  2. Use the CalcHitInfo method provided by the IGaugeContainer interface. This method takes a point as a parameter and returns the gauge hit information. You can use this information to get which gauge element is located at the target coordinates.

    void gaugeControl1_MouseDown(object sender, MouseEventArgs e) {
        BasePrimitiveHitInfo hi = (gaugeControl1 as IGaugeContainer).CalcHitInfo(e.Location);
    }
    
  3. A rendered gauge is always surrounded by a gauge background (see the figure below).

    Gauge - Background Corners

    Technically, this background belongs to the gauge control and thus, clicking it returns a valid BasePrimitiveHitInfo object. The code below illustrates how to check the type of the gauge element to which the hit information corresponds. As a result, the needle will move only when an end-users clicks within a rendered gauge.

    void gaugeControl1_MouseDown(object sender, MouseEventArgs e) {
        BasePrimitiveHitInfo hi = (gaugeControl1 as IGaugeContainer).CalcHitInfo(e.Location);
        if (hi.Element != null && !hi.Element.IsComposite) {
            //move gauge needle
        }
    }
    
  4. Convert the click coordinates to gauge coordinates by using the PointToModelPoint method of the DevExpress.XtraGauges.Core.Primitive.MathHelper class.

    if (hi.Element != null && !hi.Element.IsComposite) {
        PointF modelPt = MathHelper.PointToModelPoint(arcScaleComponent1 as IElement<IRenderableElement>, new PointF(e.X, e.Y));
        //move gauge needle
    }
    
  5. Now, convert gauge coordinates to a scale value. First, use the ArcScale.PointToPercent method (the LinearScale.PointToPercent method for linear gauges). This method will return the percentage of the ‘filled’ scale compared to this entire scale. Then, call the ArcScale.PercentToValue/LinearScale.PercentToValue method to convert this relative value to an absolute scale value. Finally, apply this value to a scale (or to a value indicator if it is not bound to scale values).

    if (hi.Element != null && !hi.Element.IsComposite) {
        PointF modelPt = MathHelper.PointToModelPoint(arcScaleComponent1 as IElement<IRenderableElement>, new PointF(e.X, e.Y));
        float percent = arcScaleComponent1.PointToPercent(modelPt);
        arcScaleComponent1.Value = arcScaleComponent1.PercentToValue(percent);
    }
    
  6. For the MouseMove event, you need to do exactly the same as before, except for one additional condition: check the Button parameter of the received MouseEventArgs data to get whether or not an end-user moves the mouse pointer while holding left mouse button down.

    void gaugeControl1_MouseMove(object sender, MouseEventArgs e) {
        if (e.Button == MouseButtons.Left) {
            BasePrimitiveHitInfo hi = (gaugeControl1 as IGaugeContainer).CalcHitInfo(e.Location);
            if (hi.Element != null && !hi.Element.IsComposite) {
                PointF modelPt = MathHelper.PointToModelPoint(arcScaleComponent1 as IElement<IRenderableElement>, new PointF(e.X, e.Y));
                float percent = arcScaleComponent1.PointToPercent(modelPt);
                arcScaleComponent1.Value = arcScaleComponent1.PercentToValue(percent);
            }
        }
    }
    
  7. Move your code from event handlers to a separate method to avoid code duplication.

    void CalculateGaugeValue(IGaugeContainer container, IConvertibleScaleEx scale, MouseEventArgs e) {
         BasePrimitiveHitInfo hi = container.CalcHitInfo(e.Location);
         if (hi.Element != null && !hi.Element.IsComposite) {
             PointF modelPt = MathHelper.PointToModelPoint(scale as IElement<IRenderableElement>, new PointF(e.X, e.Y));
             float percent = scale.PointToPercent(modelPt);
             scale.Value = scale.PercentToValue(percent);
         }
     }
    
     private void GaugeControl1_MouseMove(object sender, MouseEventArgs e) {
         if (e.Button == MouseButtons.Left) {
             CalculateGaugeValue(gaugeControl1 as IGaugeContainer, arcScaleComponent1, e);
         }
     }
    
     private void GaugeControl1_MouseDown(object sender, MouseEventArgs e) {
         CalculateGaugeValue(gaugeControl1 as IGaugeContainer, arcScaleComponent1, e);
     }
    
  8. Use the following code to change the mouse pointer when it hovers a rendered gauge. This small enhancement will give your customers a tip that the gauge is clickable.

    void ChangeCursor(IGaugeContainer container, MouseEventArgs e) {
        BasePrimitiveHitInfo hi = container.CalcHitInfo(e.Location);
        Cursor cursor = (hi.Element != null && !hi.Element.IsComposite) ? Cursors.Hand : Cursors.Default;
        if (((Control)container).Cursor != cursor)
            ((Control)container).Cursor = cursor;
    }
    

    Add this method call to your MouseMove event handler as follows.

    private void GaugeControl1_MouseMove(object sender, MouseEventArgs e) {
        ChangeCursor(gaugeControl1 as IGaugeContainer, e);
        if (e.Button == MouseButtons.Left) {
            CalculateGaugeValue(gaugeControl1 as IGaugeContainer, arcScaleComponent1, e);
        }
    }
    
  9. Launch the application to see the result.

    Gauges - Moving Needles at Runtime

Complete Code

This section lists the complete example code. Also, the DevExpress Demo Center contains a demo module for this example (‘Gauge and Indicators’ demo, ‘Event-based Interaction’ module). Click the ‘Open Solution’ button to open this demo module in Visual Studio.

Gauges - Needle Drag Example DC Image

using System.Drawing;
using System.Windows.Forms;
using DevExpress.XtraGauges.Base;
using DevExpress.XtraGauges.Core.Base;
using DevExpress.XtraGauges.Core.Model;
using DevExpress.XtraGauges.Core.Primitive;

namespace DevExpress.XtraGauges.Demos {
    public partial class ClickableGauge {
        public InteractionFeature() {
            InitializeComponent();
        }
        void gaugeControl1_MouseMove(object sender, MouseEventArgs e) {
            CheckCursor(gaugeControl1 as IGaugeContainer, e);
            if(e.Button == MouseButtons.Left)
                CalculateMouseValue(gaugeControl1 as IGaugeContainer, arcScaleComponent1, e);
        }
        void gaugeControl1_MouseDown(object sender, MouseEventArgs e) {
            CalculateMouseValue(gaugeControl1 as IGaugeContainer, arcScaleComponent1, e);
        }
        void CalculateMouseValue(IGaugeContainer container, IConvertibleScaleEx scale, MouseEventArgs e) {
            BasePrimitiveHitInfo hi = container.CalcHitInfo(e.Location);
            if(hi.Element != null && !hi.Element.IsComposite) {
                PointF modelPt = MathHelper.PointToModelPoint(scale as IElement<IRenderableElement>, new PointF(e.X, e.Y));
                float percent = scale.PointToPercent(modelPt);
                scale.Value = scale.PercentToValue(percent);
            }
        }
        void CheckCursor(IGaugeContainer container, MouseEventArgs e) {
            BasePrimitiveHitInfo hi = container.CalcHitInfo(e.Location);
            Cursor cursor = (hi.Element != null && !hi.Element.IsComposite) ? 
                Cursors.Hand : Cursors.Default;
            if(((Control)container).Cursor != cursor) 
                ((Control)container).Cursor = cursor;
        }
    }
}