Skip to main content

DevExpress v24.2 Update — Your Feedback Matters

Our What's New in v24.2 webpage includes product-specific surveys. Your response to our survey questions will help us measure product satisfaction for features released in this major update and help us refine our plans for our next major release.

Take the survey Not interested

How to: Create a Range Control Client that Paints Custom Information within Range Control

  • 13 minutes to read

This example shows how to implement a custom Range Control Client to paint any information within the RangeControl.

The Range Control Client created in this example paints a graph.

image

using DevExpress.Utils.Drawing;
using DevExpress.XtraEditors;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace CustomRangeControlClient {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e) {
            RangeControl rangeControl1 = new RangeControl();
            rangeControl1.Dock = DockStyle.Fill;
            this.Controls.Add(rangeControl1);

            rangeControl1.Client = new CustomRangeClient(101, -100, 100);
            rangeControl1.SelectedRange.Maximum = 30;
            rangeControl1.SelectedRange.Minimum = 10;
        }
    }

    public class CustomRangeClient : IRangeControlClient {

        const int rulerDeltaConst = 2;

        int[] data;
        int minValue;
        int maxValue;

        public CustomRangeClient(int dataCount, int minDataValue, int maxDataValue) {
            InitData(dataCount, minDataValue, maxDataValue);

            ruler = new List<object>(dataCount / rulerDeltaConst + 1);
            for (int i = 0; i < ruler.Count; i++) {
                ruler.Add(i);
            }
        }

        void InitData(int dataCount, int minDataValue, int maxDataValue) {
            Data = new int[dataCount];
            MinValue = minDataValue;
            MaxValue = maxDataValue;
            Random r = new Random();
            for (int i = 0; i < Data.Length; i++) {
                Data[i] = minDataValue + r.Next(maxDataValue - minDataValue);
            }
        }

        public int[] Data {
            get {
                return data;
            }
            private set {
                data = value;
            }
        }
        public int MaxValue {
            get {
                return maxValue;
            }
            private set {
                maxValue = value;
            }
        }

        public int MinValue {
            get {
                return minValue;
            }
            private set {
                minValue = value;
            }
        }

        EventHandlerList events;
        protected EventHandlerList Events {
            get {
                if (events == null)
                    events = new EventHandlerList();
                return events;
            }
        }

        private static readonly object rangeChanged = new object();

        // Fires the RangeChanged event.
        protected void RaiseRangeChanged() {
            RangeChangedEventHandler handler = Events[rangeChanged] as RangeChangedEventHandler;
            if (handler != null) {
                RangeControlRangeEventArgs e = new RangeControlRangeEventArgs();
                e.Range = new RangeControlRange();
                handler(this, e);
            }
        }

        #region IRangeControlClient Members
        // Checks if the specified type of the ruler values is supported.
        // This method is called when a new value is set to the Minimum and Maximum properties.
        bool IRangeControlClient.IsValidType(Type type) {
            return true;
        }

        //This method fires when you move the mouse cursor over the viewport.
        void IRangeControlClient.UpdateHotInfo(RangeControlHitInfo hitInfo) {
        }

        //This method fires when you press with the mouse within the viewport (without releasing the mouse button).
        void IRangeControlClient.UpdatePressedInfo(RangeControlHitInfo hitInfo) {
        }

        //This method fires when you click within the viewport.
        void IRangeControlClient.OnClick(RangeControlHitInfo hitInfo) {
        }

        // Returns true if the Client's state is valid and the Client should render itself within the viewport;
        // Returns false if a message specified by the InvalidText property should be painted instead of the Client.
        bool IRangeControlClient.IsValid {
            get {
                return true;
            }
        }
        // Specifies text painted when the Client's state is invalid.
        string IRangeControlClient.InvalidText {
            get {
                return "i n v a l i d";
            }
        }

        // Return the object that will be accessible via the RangeControl.ClientOptions property.
        object IRangeControlClient.GetOptions() {
            return this;
        }

        //The event that fires when the range has been changed via the Client.
        event ClientRangeChangedEventHandler IRangeControlClient.RangeChanged {
            add { Events.AddHandler(rangeChanged, value); }
            remove { Events.RemoveHandler(rangeChanged, value); }
        }
        // Fires when the range is changed via the RangeControl.
        void IRangeControlClient.OnRangeChanged(object rangeMinimum, object rangeMaximum) {
        }

        // Return true for a specific orientation if the Client supports this orientation.
        bool IRangeControlClient.SupportOrientation(RangeControlClientOrientation orientation) {
            return (orientation != RangeControlClientOrientation.Vertical);
        }

        // Return true if the Client draws the ruler itself.
        bool IRangeControlClient.DrawRuler(RangeControlPaintEventArgs e) {
            return false;
        }

        //Returns false if the RangeControl should reserve drawing space for the ruler.
        bool IRangeControlClient.IsCustomRuler {
            get {
                return false;
            }
        }

        // Returns text representation of the ruler values
        string IRangeControlClient.RulerToString(int index) {
            return (index * (int)(rulerDeltaConst)).ToString();
        }

        List<object> ruler;
        // If ruler values are not equally spaced, return custom ruler values; 
        // If the ruler has equally spaced increments specified by the RulerDelta property, return null.
        List<object> IRangeControlClient.GetRuler(RulerInfoArgs e) {
            return null;
            //return ruler;
        }

        // Returns a ruler increment (when values are equally distributed).
        object IRangeControlClient.RulerDelta {
            get { return rulerDeltaConst; }
        }

        // Returns a normalized ruler increment.
        double IRangeControlClient.NormalizedRulerDelta {
            get {
                return (double)rulerDeltaConst / BarCount;
            }
        }

        //Gets a ruler value (between Minimum and Maximum) from a normalized value (between 0 and 1).
        object IRangeControlClient.GetValue(double normalizedValue) {
            int index = (int)(normalizedValue * BarCount);
            return index;
        }
        // Performs the opposite conversion.
        double IRangeControlClient.GetNormalizedValue(object value) {
            int index = (int)value;
            return ((double)index) / BarCount;
        }

        string IRangeControlClient.ValueToString(double normalizedValue) {
            return string.Empty;
        }

        // Renders the Range Control's viewport.
        void IRangeControlClient.DrawContent(RangeControlPaintEventArgs e) {
            Rectangle rect = e.ContentBounds;
            rect.Inflate(0, -3);
            rect.Height -= ((IRangeControlClient)this).RangeBoxBottomIndent;
            DrawGraph(e, rect);
            DrawZeroLine(e, rect);
        }

        protected virtual void DrawZeroLine(RangeControlPaintEventArgs e, Rectangle contentBounds) {
            double zeroLine = (double)(MaxValue - 0) / (MaxValue - MinValue);
            if (zeroLine < 0.0 || zeroLine >= 1.0f)
                return;
            int y = (int)(contentBounds.Y + zeroLine * contentBounds.Height);
            e.Cache.DrawLine(new Point(contentBounds.X, y), new Point(contentBounds.Right, y), Color.Pink, 1);
        }

        protected virtual void DrawGraph(RangeControlPaintEventArgs e, Rectangle contentBounds) {
            GraphicsCache gCache = e.Cache as GraphicsCache;
            gCache.FillRectangle(Brushes.WhiteSmoke, contentBounds);
            gCache.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            int start = Math.Max(0, (int)(e.RangeControl.VisibleRangeStartPosition * BarCount) - 2);
            int end = Math.Min(Data.Length, start + ((int)(e.RangeControl.VisibleRangeWidth * BarCount) + 4));
            Point? prevPoint = null;

            for (int i = start; i < end; i++) {
                int y = contentBounds.Y + contentBounds.Height - (int)((double)(Data[i] - MinValue) / (MaxValue - MinValue) * contentBounds.Height);
                int x = e.CalcX((double)i / BarCount);
                if (prevPoint.HasValue) {
                    gCache.DrawLine(new Point(prevPoint.Value.X, prevPoint.Value.Y), new Point(x, y), Color.DarkCyan, 1);
                }
                prevPoint = new Point(x, y);
            }
            gCache.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default;
        }

        int BarCount { get { return Data.Length - 1; } }


        // The top and bottom indents for the selection area within the viewport.
        // These limit the bounds of the selection thumb lines that mark the current selection. 
        int IRangeControlClient.RangeBoxTopIndent { get { return 0; } }
        int IRangeControlClient.RangeBoxBottomIndent { get { return 0; } }


        // Validates a range when it is changed.
        void IRangeControlClient.ValidateRange(NormalizedRangeInfo info) {
            int start = (int)(info.Range.Minimum * BarCount);
            int end = (int)(info.Range.Maximum * BarCount);
            if (end == start) end = start + 2;

            info.Range.Minimum = (double)start / BarCount;
            info.Range.Maximum = (double)end / BarCount;
        }
        //This method fires when a client is added to or removed from the RangeControl.
        void IRangeControlClient.OnRangeControlChanged(IRangeControl rangeControl) { }

        //This method fires when the RangeControl is resized.
        void IRangeControlClient.OnResize() { }

        //This method fires when the RangeControl's state or settings are changed
        void IRangeControlClient.Calculate(Rectangle contentRect) { }

        //Validates a scale factor
        double IRangeControlClient.ValidateScale(double newScale) {
            // Limit the maximum scale factor to 10:
            return Math.Min(10, newScale);
        }
        // Allows you to override the selected region's bounds.
        Rectangle IRangeControlClient.CalculateSelectionBounds(RangeControlPaintEventArgs e, Rectangle rect) {
            return rect;
        }
        // Allows you to custom paint the selected region.
        void IRangeControlClient.DrawSelection(RangeControlPaintEventArgs e) {
        }

        #endregion
    }
}