windowsforms-11760-controls-and-libraries-editors-and-simple-controls-range-control-examples-how-to-create-a-range-control-client-that-paints-custom-information-within-range-control.md
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.
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
}
}
Imports System.ComponentModel
Imports System.Text
Imports DevExpress.Utils.Drawing
Imports DevExpress.XtraEditors
Partial Public Class Form1
Public Sub New()
InitializeComponent()
Dim rangeControl1 As New DevExpress.XtraEditors.RangeControl
rangeControl1.Dock = DockStyle.Fill
Controls.Add(rangeControl1)
rangeControl1.Client = New CustomRangeClient(101, -100, 100)
rangeControl1.SelectedRange.Maximum = 30
rangeControl1.SelectedRange.Minimum = 10
End Sub
End Class
Public Class CustomRangeClient
Implements IRangeControlClient
Private Const rulerDeltaConst As Integer = 2
Private data_Renamed() As Integer
Private minValue_Renamed As Integer
Private maxValue_Renamed As Integer
Public Sub New(ByVal dataCount As Integer, ByVal minDataValue As Integer, ByVal maxDataValue As Integer)
InitData(dataCount, minDataValue, maxDataValue)
ruler = New List(Of Object)(dataCount \ rulerDeltaConst + 1)
Dim i As Integer = 0
Do While i < ruler.Count
ruler.Add(i)
i += 1
Loop
End Sub
Private Sub InitData(ByVal dataCount As Integer, ByVal minDataValue As Integer, ByVal maxDataValue As Integer)
Data = New Integer(dataCount - 1) {}
MinValue = minDataValue
MaxValue = maxDataValue
Dim r As New Random()
For i As Integer = 0 To Data.Length - 1
Data(i) = minDataValue + r.Next(maxDataValue - minDataValue)
Next i
End Sub
Public Property Data() As Integer()
Get
Return data_Renamed
End Get
Private Set(ByVal value As Integer())
data_Renamed = value
End Set
End Property
Public Property MaxValue() As Integer
Get
Return maxValue_Renamed
End Get
Private Set(ByVal value As Integer)
maxValue_Renamed = value
End Set
End Property
Public Property MinValue() As Integer
Get
Return minValue_Renamed
End Get
Private Set(ByVal value As Integer)
minValue_Renamed = value
End Set
End Property
Private events_Renamed As EventHandlerList
Protected ReadOnly Property Events() As EventHandlerList
Get
If events_Renamed Is Nothing Then
events_Renamed = New EventHandlerList()
End If
Return events_Renamed
End Get
End Property
Private Shared ReadOnly rangeChanged_Renamed As New Object()
' Fires the RangeChanged event.
Protected Sub RaiseRangeChanged()
Dim handler As RangeChangedEventHandler = TryCast(Events(rangeChanged_Renamed), RangeChangedEventHandler)
If handler IsNot Nothing Then
Dim e As New RangeControlRangeEventArgs()
e.Range = New RangeControlRange()
handler(Me, e)
End If
End Sub
#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.
Private Function IRangeControlClient_IsValidType(ByVal type As Type) As Boolean Implements IRangeControlClient.IsValidType
Return True
End Function
'This method fires when you move the mouse cursor over the viewport.
Private Sub IRangeControlClient_UpdateHotInfo(ByVal hitInfo As RangeControlHitInfo) Implements IRangeControlClient.UpdateHotInfo
End Sub
'This method fires when you press with the mouse within the viewport (without releasing the mouse button).
Private Sub IRangeControlClient_UpdatePressedInfo(ByVal hitInfo As RangeControlHitInfo) Implements IRangeControlClient.UpdatePressedInfo
End Sub
'This method fires when you click within the viewport.
Private Sub IRangeControlClient_OnClick(ByVal hitInfo As RangeControlHitInfo) Implements IRangeControlClient.OnClick
End Sub
' 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.
Private ReadOnly Property IRangeControlClient_IsValid() As Boolean Implements IRangeControlClient.IsValid
Get
Return True
End Get
End Property
' Specifies text painted when the Client's state is invalid.
Private ReadOnly Property IRangeControlClient_InvalidText() As String Implements IRangeControlClient.InvalidText
Get
Return "i n v a l i d"
End Get
End Property
' Return the object that will be accessible via the RangeControl.ClientOptions property.
Private Function IRangeControlClient_GetOptions() As Object Implements IRangeControlClient.GetOptions
Return Me
End Function
'The event that fires when the range has been changed via the Client.
Private Custom Event RangeChanged As ClientRangeChangedEventHandler Implements IRangeControlClient.RangeChanged
AddHandler(ByVal value As ClientRangeChangedEventHandler)
Events.AddHandler(rangeChanged_Renamed, value)
End AddHandler
RemoveHandler(ByVal value As ClientRangeChangedEventHandler)
Events.RemoveHandler(rangeChanged_Renamed, value)
End RemoveHandler
RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)
End RaiseEvent
End Event
' Fires when the range is changed via the RangeControl.
Private Sub IRangeControlClient_OnRangeChanged(ByVal rangeMinimum As Object, ByVal rangeMaximum As Object) Implements IRangeControlClient.OnRangeChanged
End Sub
' Return true for a specific orientation if the Client supports this orientation.
Private Function IRangeControlClient_SupportOrientation(ByVal orientation As RangeControlClientOrientation) As Boolean Implements IRangeControlClient.SupportOrientation
Return (orientation <> RangeControlClientOrientation.Vertical)
End Function
' Return true if the Client draws the ruler itself.
Private Function IRangeControlClient_DrawRuler(ByVal e As RangeControlPaintEventArgs) As Boolean Implements IRangeControlClient.DrawRuler
Return False
End Function
'Returns false if the RangeControl should reserve drawing space for the ruler.
Private ReadOnly Property IRangeControlClient_IsCustomRuler() As Boolean Implements IRangeControlClient.IsCustomRuler
Get
Return False
End Get
End Property
' Returns text representation of the ruler values
Private Function IRangeControlClient_RulerToString(ByVal index As Integer) As String Implements IRangeControlClient.RulerToString
Return (index * CInt(rulerDeltaConst)).ToString()
End Function
Private ruler As List(Of Object)
' 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.
Private Function IRangeControlClient_GetRuler(ByVal e As RulerInfoArgs) As List(Of Object) Implements IRangeControlClient.GetRuler
Return Nothing
'return ruler;
End Function
' Returns a ruler increment (when values are equally distributed).
Private ReadOnly Property IRangeControlClient_RulerDelta() As Object Implements IRangeControlClient.RulerDelta
Get
Return rulerDeltaConst
End Get
End Property
' Returns a normalized ruler increment.
Private ReadOnly Property IRangeControlClient_NormalizedRulerDelta() As Double Implements IRangeControlClient.NormalizedRulerDelta
Get
Return CDbl(rulerDeltaConst) / BarCount
End Get
End Property
'Gets a ruler value (between Minimum and Maximum) from a normalized value (between 0 and 1).
Private Function IRangeControlClient_GetValue(ByVal normalizedValue As Double) As Object Implements IRangeControlClient.GetValue
Dim index As Integer = CInt((normalizedValue * BarCount))
Return index
End Function
' Performs the opposite conversion.
Private Function IRangeControlClient_GetNormalizedValue(ByVal value As Object) As Double Implements IRangeControlClient.GetNormalizedValue
Dim index As Integer = DirectCast(value, Integer)
Return (CDbl(index)) / BarCount
End Function
Private Function IRangeControlClient_ValueToString(ByVal normalizedValue As Double) As String Implements IRangeControlClient.ValueToString
Return String.Empty
End Function
' Renders the Range Control's viewport.
Private Sub IRangeControlClient_DrawContent(ByVal e As RangeControlPaintEventArgs) Implements IRangeControlClient.DrawContent
Dim rect As Rectangle = e.ContentBounds
rect.Inflate(0, -3)
rect.Height -= DirectCast(Me, IRangeControlClient).RangeBoxBottomIndent
DrawGraph(e, rect)
DrawZeroLine(e, rect)
End Sub
Protected Overridable Sub DrawZeroLine(ByVal e As RangeControlPaintEventArgs, ByVal contentBounds As Rectangle)
Dim zeroLine As Double = CDbl(MaxValue - 0) / (MaxValue - MinValue)
If zeroLine < 0.0 OrElse zeroLine >= 1.0F Then
Return
End If
Dim y As Integer = CInt((contentBounds.Y + zeroLine * contentBounds.Height))
e.Cache.DrawLine(New Point(contentBounds.X, y), New Point(contentBounds.Right, y), Color.Pink, 1)
End Sub
Protected Overridable Sub DrawGraph(ByVal e As RangeControlPaintEventArgs, ByVal contentBounds As Rectangle)
Dim gCache As GraphicsCache = CType(e.Cache, GraphicsCache)
gCache.FillRectangle(Brushes.WhiteSmoke, contentBounds)
gCache.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality
Dim start As Integer = Math.Max(0, CInt((e.RangeControl.VisibleRangeStartPosition * BarCount)) - 2)
Dim [end] As Integer = Math.Min(Data.Length, start + (CInt((e.RangeControl.VisibleRangeWidth * BarCount)) + 4))
Dim prevPoint? As Point = Nothing
Using pen As New Pen(Color.Blue, 1)
For i As Integer = start To [end] - 1
Dim y As Integer = contentBounds.Y + contentBounds.Height - CInt((CDbl(Data(i) - MinValue) / (MaxValue - MinValue) * contentBounds.Height))
Dim x As Integer = e.CalcX(CDbl(i) / BarCount)
If prevPoint.HasValue Then
gCache.DrawLine(New Point(prevPoint.Value.X, prevPoint.Value.Y), New Point(x, y), Color.DarkCyan, 1)
End If
prevPoint = New Point(x, y)
Next i
End Using
gCache.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default
End Sub
Private ReadOnly Property BarCount() As Integer
Get
Return Data.Length - 1
End Get
End Property
' 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.
Private ReadOnly Property IRangeControlClient_RangeBoxTopIndent() As Integer Implements IRangeControlClient.RangeBoxTopIndent
Get
Return 0
End Get
End Property
Private ReadOnly Property IRangeControlClient_RangeBoxBottomIndent() As Integer Implements IRangeControlClient.RangeBoxBottomIndent
Get
Return 0
End Get
End Property
' Validates a range when it is changed.
Private Sub IRangeControlClient_ValidateRange(ByVal info As NormalizedRangeInfo) Implements IRangeControlClient.ValidateRange
Dim start As Integer = CInt((info.Range.Minimum * BarCount))
Dim [end] As Integer = CInt((info.Range.Maximum * BarCount))
If [end] = start Then
[end] = start + 2
End If
info.Range.Minimum = CDbl(start) / BarCount
info.Range.Maximum = CDbl([end]) / BarCount
End Sub
'This method fires when a client is added to or removed from the RangeControl.
Private Sub IRangeControlClient_OnRangeControlChanged(ByVal rangeControl As IRangeControl) Implements IRangeControlClient.OnRangeControlChanged
End Sub
'This method fires when the RangeControl is resized.
Private Sub IRangeControlClient_OnResize() Implements IRangeControlClient.OnResize
End Sub
'This method fires when the RangeControl's state or settings are changed
Private Sub IRangeControlClient_Calculate(ByVal contentRect As Rectangle) Implements IRangeControlClient.Calculate
End Sub
'Validates a scale factor
Private Function IRangeControlClient_ValidateScale(ByVal newScale As Double) As Double Implements IRangeControlClient.ValidateScale
' Limit the maximum scale factor to 10:
Return Math.Min(10, newScale)
End Function
' Allows you to override the selected region's bounds.
Public Function CalculateSelectionBounds(e As RangeControlPaintEventArgs, rect As Rectangle) As Rectangle Implements IRangeControlClient.CalculateSelectionBounds
Return rect
End Function
' Allows you to custom paint the selected region.
Public Sub DrawSelection(e As RangeControlPaintEventArgs) Implements IRangeControlClient.DrawSelection
End Sub
#End Region
End Class