Back to Devexpress

Chart Item - Constant Line

dashboard-401621-winforms-dashboard-winforms-designer-ui-elements-and-customization-create-custom-properties-chart-item-constant-line.md

latest33.6 KB
Original Source

Chart Item - Constant Line

  • Oct 15, 2021
  • 14 minutes to read

This topic describes how to create a custom property for a dashboard item. In this example, the property provides editors that allow you to draw a constant line for the selected Chart item in the WinForms Dashboard Designer. The ChartContext.GetDashboardItemSeries method is used to provide a connection between data item containers from the Values section and the series from an underlying Chart control.

View Example: WinForms Dashboard - Custom Properties

Create the Custom Functionality Module

The code is organized into a separate module you can integrate into any dashboard application.

Create the ConstantLineUserValueModule class that serves as a custom functionality module and contains:

  • a dashboard control that you pass as a parameter when you register the module,
  • a custom property’s unique name,
  • event subscriptions that are used to provide custom functionality,
  • a ribbon in which you add a button to edit custom property’s value.

Note

You can use the IDashboardControl interface that provides common API for WinForms Designer and Viewer to write code that can be used in both controls simultaneously.

You can add control elements to the Ribbon / Toolbar to change the custom property’s value in the UI.

In this example, the AddBarItem method places the Constant Line button in the Custom Properties ribbon group on the Chart’s Design page.

csharp
public class ConstantLineUserValueModule {
public static readonly string PropertyName = "ConstantLine";
readonly DashboardDesigner designer;

public ConstantLineUserValueModule(DashboardDesigner designer, SvgImage barImage = null) {
    this.designer = designer;
    BarButtonItem barItem = AddBarItem("Constant Line", barImage, designer.Ribbon);
    barItem.ItemClick += BarItem_Click;
    designer.DashboardItemControlUpdated += Designer_DashboardItemControlUpdated;
}

BarButtonItem AddBarItem(string caption, SvgImage barImage, RibbonControl ribbon) {
    var page = ribbon.GetDashboardRibbonPage(DashboardBarItemCategory.ChartTools, DashboardRibbonPage.Design);
    RibbonPageGroup group = page.GetGroupByName("Custom Properties");
    if(group == null) {
        group = new RibbonPageGroup("Custom Properties") { Name = "Custom Properties" };
        page.Groups.Add(group);
    }
    var barItem = new BarButtonItem(ribbon.Manager, caption);
    barItem.ImageOptions.SvgImage = barImage;
    group.ItemLinks.Add(barItem);
    return barItem;
}
vb
Public Class ConstantLineUserValueModule
Public Shared ReadOnly PropertyName As String = "ConstantLine"
Private ReadOnly designer As DashboardDesigner

Public Sub New(ByVal designer As DashboardDesigner, Optional ByVal barImage As SvgImage = Nothing)
    Me.designer = designer
    Dim barItem As BarButtonItem = AddBarItem("Constant line", barImage, designer.Ribbon)
    AddHandler barItem.ItemClick, AddressOf BarItem_Click
    AddHandler designer.DashboardItemControlUpdated, AddressOf Designer_DashboardItemControlUpdated
End Sub
Private Function AddBarItem(ByVal caption As String, ByVal barImage As SvgImage, ByVal ribbon As RibbonControl) As BarButtonItem
    Dim page = ribbon.GetDashboardRibbonPage(DashboardBarItemCategory.ChartTools, DashboardRibbonPage.Design)
    Dim group As RibbonPageGroup = page.GetGroupByName("Custom Properties")
    If group Is Nothing Then
        group = New RibbonPageGroup("Custom Properties") With {.Name = "Custom Properties"}
        page.Groups.Add(group)
    End If
    Dim barItem = New BarButtonItem(ribbon.Manager, caption)
    barItem.ImageOptions.SvgImage = barImage
    group.ItemLinks.Add(barItem)
    Return barItem
End Function

Create a Class for Data Conversion

Create the ConstantLineUserValueModuleData class that parses the selected chart item’s data.

The GetDataFromString method returns the ConstantLineUserValueModuleData class instance that contains parsed values.

The GetStringFromData method returns an instance’s values as a string: {Pane 1_False_100000}.

  • “Pane 1” is the PaneName string value.

  • “False” is the IsSecondaryAxis Boolean value that defines whether to create a constant line on the secondary Y axis.

  • “100000” is the Value numeric value that is the position of the constant line on the Y (or secondary Y) axis.

  • C#

  • VB.NET

csharp
public class ConstantLineUserValueModuleData {
    public string PaneName { get; set; }
    public bool IsSecondaryAxis { get; set; }
    public double Value { get; set; }

    public string GetStringFromData() {
        return PaneName + "_" + IsSecondaryAxis.ToString() + "_" + Value;
    }
}
ConstantLineUserValueModuleData GetDataFromString(string customPropertyValue) {
    if(!string.IsNullOrEmpty(customPropertyValue)) {
        var array = customPropertyValue.Split('_');
        return new ConstantLineUserValueModuleData() {
            PaneName = array[0],
            IsSecondaryAxis = bool.Parse(array[1]),
            Value = Convert.ToDouble(array[2])
        };
    }
    return new ConstantLineUserValueModuleData();
}
vb
Public Class ConstantLineUserValueModuleData
    Public Property PaneName() As String
    Public Property IsSecondaryAxis() As Boolean
    Public Property Value() As Double

    Public Function GetStringFromData() As String
        Return PaneName & "_" & IsSecondaryAxis.ToString() & "_" & Value
    End Function
    Private Function GetDataFromString(ByVal customPropertyValue As String) As ConstantLineUserValueModuleData
        If Not String.IsNullOrEmpty(customPropertyValue) Then
            Dim array = customPropertyValue.Split("_"c)
            Return New ConstantLineUserValueModuleData() With {
                .PaneName = array(0),
                .IsSecondaryAxis = Boolean.Parse(array(1)),
                .Value = Convert.ToDouble(array(2))
            }
        End If
    Return New ConstantLineUserValueModuleData()
    End Function
End Class

Create Editors

Create a new form that allows users to edit the constant line’s options.

Inherit the XtraUserControl. In this example, it is the ValueSelectorControl class. Add the DataLayoutControl. Next, add editors to the control that allows you to edit options.

csharp
public class ValueSelectorControl : XtraUserControl {
    public ConstantLineUserValueModuleData ConstantLineModuleData { get; }

        public ValueSelectorControl(List<string> paneNames) {
            DataLayoutControl dataLayoutControl = new DataLayoutControl();
            BindingSource source = new BindingSource();
            ConstantLineModuleData = new ConstantLineUserValueModuleData();
            source.DataSource = ConstantLineModuleData;
            dataLayoutControl.DataSource = source;
            dataLayoutControl.FieldRetrieving += (s, e) => {
                if(e.FieldName == nameof(ConstantLineModuleData.PaneName)) {
                    e.EditorType = typeof(LookUpEdit);
                    e.Handled = true;
                }
            };
            dataLayoutControl.FieldRetrieved += (s, e) => {
                if(e.FieldName == nameof(ConstantLineModuleData.PaneName))
                    InitRepositoryItem(e.RepositoryItem, paneNames);
            };
            dataLayoutControl.RetrieveFields();
            dataLayoutControl.Dock = DockStyle.Fill;
            Controls.Add(dataLayoutControl);
            Dock = DockStyle.Top;
        }
        void InitRepositoryItem<T>(RepositoryItem ri, List<T> list) {
            var lookUpEdit = ri as RepositoryItemLookUpEdit;
            lookUpEdit.TextEditStyle = TextEditStyles.DisableTextEditor;
            lookUpEdit.AllowNullInput = DefaultBoolean.False;
            lookUpEdit.DataSource = list;
        }
    }
}
vb
Public Class ValueSelectorControl
    Inherits XtraUserControl
    Public ReadOnly Property ConstantLineModuleData() As ConstantLineUserValueModuleData

    Public Sub New(ByVal paneNames As List(Of String))
        Dim dataLayoutControl As New DataLayoutControl()
        Dim source As New BindingSource()
        ConstantLineModuleData = New ConstantLineUserValueModuleData()
        source.DataSource = ConstantLineModuleData
        dataLayoutControl.DataSource = source
        AddHandler dataLayoutControl.FieldRetrieving, Sub(s, e)
            If e.FieldName = NameOf(ConstantLineModuleData.PaneName) Then
                e.EditorType = GetType(LookUpEdit)
                e.Handled = True
            End If
        End Sub
        AddHandler dataLayoutControl.FieldRetrieved, Sub(s, e)
            If e.FieldName = NameOf(ConstantLineModuleData.PaneName) Then
                InitRepositoryItem(e.RepositoryItem, paneNames)
            End If
        End Sub
        dataLayoutControl.RetrieveFields()
        dataLayoutControl.Dock = DockStyle.Fill
        Controls.Add(dataLayoutControl)
        Dock = DockStyle.Top
    End Sub
    Private Sub InitRepositoryItem(Of T)(ByVal ri As RepositoryItem, ByVal list As List(Of T))
        Dim lookUpEdit = TryCast(ri, RepositoryItemLookUpEdit)
        lookUpEdit.TextEditStyle = TextEditStyles.DisableTextEditor
        lookUpEdit.AllowNullInput = DefaultBoolean.False
        lookUpEdit.DataSource = list
    End Sub
End Class

Use the GetConstantLineModuleData method to get the specified options.

csharp
ConstantLineUserValueModuleData GetConstantLineModuleData(ChartDashboardItem dashboardItem) {
    using(ValueSelectorControl selector = new ValueSelectorControl(dashboardItem.Panes.Select(p => p.Name).ToList())) {
        if(XtraDialog.Show(selector, "Select the required series:") == DialogResult.OK) {
            return selector.ConstantLineModuleData;
        }
    }
    return null;
}
vb
Private Function GetConstantLineModuleData(ByVal dashboardItem As ChartDashboardItem) As ConstantLineUserValueModuleData
    Using selector As New ValueSelectorControl(dashboardItem.Panes.Select(Function(p) p.Name).ToList())
        If XtraDialog.Show(selector, "Select the required series:") = DialogResult.OK Then
            Return selector.ConstantLineModuleData
        End If
    End Using
    Return Nothing
End Function

The following image shows the ValueSelectorControl :

Save the Custom Property’s Value to a Dashboard

In this example, the custom property’s value is stored in the DashboardItem.CustomProperties collection.

Use the DashboardDesigner.AddToHistory method to record a new custom property’s value and save the action to the Dashboard Designer’s history when a user changes the value. This method calls the CustomProperties.SetValue method and adds the information to the history item. You can undo/redo this action like other user actions.

csharp
void BarItem_Click(object sender, ItemClickEventArgs e) {
    var chartItem = designer.SelectedDashboardItem as ChartDashboardItem;
    if(chartItem != null) {
        var data = GetConstantLineModuleData(chartItem);
        if(data != null)
            designer.AddToHistory(new CustomPropertyHistoryItem(chartItem, PropertyName, data.GetStringFromData(), $"{data.PaneName} constant line is set"));
    }
}
vb
Private Sub BarItem_Click(ByVal sender As Object, ByVal e As ItemClickEventArgs)
    Dim chartItem = TryCast(designer.SelectedDashboardItem, ChartDashboardItem)
    If chartItem IsNot Nothing Then
        Dim data = GetConstantLineModuleData(chartItem)
        If data IsNot Nothing Then
            designer.AddToHistory(New CustomPropertyHistoryItem(chartItem, PropertyName, data.GetStringFromData(), $"{data.PaneName} constant line is set"))
        End If
    End If
End Sub

Apply the Custom Property’s Value to the Underlying Control

Create the UpdateChart method to draw a constant line. Create a new ConstantLine class instance and assign AxisValue as the constant line value to define the constant line’s position.

Handle the DashboardDesigner.DashboardItemControlUpdated event to update the underlying Chart. Use the CustomProperties.GetValue method to get the custom property’s value and update the control’s options according to this value.

csharp
void UpdateChart(ChartDashboardItem chartDashboardItem, ChartContext chartContext) {
    var moduleData = GetDataFromString(chartDashboardItem.CustomProperties.GetValue(PropertyName));
    var pane = chartDashboardItem.Panes.FirstOrDefault(x => x.Name == moduleData.PaneName);
    if(pane != null) {
        ChartSeries dashboardSeries = pane.Series.FirstOrDefault(s => s.PlotOnSecondaryAxis == moduleData.IsSecondaryAxis);
        if(dashboardSeries != null) {
            Series chartSeries = chartContext.GetControlSeries(dashboardSeries).FirstOrDefault();
            var chartAxis = (chartSeries.View as XYDiagramSeriesViewBase)?.AxisY;
            if(chartAxis != null) {
                ConstantLine line = new ConstantLine() { AxisValue = moduleData.Value };
                chartAxis.ConstantLines.Clear();
                chartAxis.ConstantLines.Add(line);
                line.ShowInLegend = false;
                line.Color = Color.Green;
                line.LineStyle.Thickness = 2;
                line.LineStyle.DashStyle = DashStyle.Dash;
                line.Title.Text = "Value: " + moduleData.Value.ToString();
                line.Title.TextColor = line.Color;
            }
        }
    }
}
void Designer_DashboardItemControlUpdated(object sender, DashboardItemControlEventArgs e) {
    if(e.ChartControl != null && designer.Dashboard.Items[e.DashboardItemName] is ChartDashboardItem chartDashboardItem)
        UpdateChart(chartDashboardItem, e.ChartContext);
}
vb
Private Sub UpdateChart(ByVal chartDashboardItem As ChartDashboardItem, ByVal chartContext As ChartContext)
    Dim moduleData = GetDataFromString(chartDashboardItem.CustomProperties.GetValue(PropertyName))
    Dim pane = chartDashboardItem.Panes.FirstOrDefault(Function(x) x.Name = moduleData.PaneName)
    If pane IsNot Nothing Then
        Dim dashboardSeries As ChartSeries = pane.Series.FirstOrDefault(Function(s) s.PlotOnSecondaryAxis = moduleData.IsSecondaryAxis)
        If dashboardSeries IsNot Nothing Then
            Dim chartSeries As Series = chartContext.GetControlSeries(dashboardSeries).FirstOrDefault()
            Dim chartAxis = (TryCast(chartSeries.View, XYDiagramSeriesViewBase))?.AxisY
            If chartAxis IsNot Nothing Then
                Dim line As New ConstantLine() With {.AxisValue = moduleData.Value}
                chartAxis.ConstantLines.Clear()
                chartAxis.ConstantLines.Add(line)
                line.ShowInLegend = False
                line.Color = Color.Green
                line.LineStyle.Thickness = 2
                line.LineStyle.DashStyle = DashStyle.Dash
                line.Title.Text = "Value: " & moduleData.Value.ToString()
                line.Title.TextColor = line.Color
            End If
        End If
    End If
End Sub
Private Sub Designer_DashboardItemControlUpdated(ByVal sender As Object, ByVal e As DashboardItemControlEventArgs)
    Dim tempVar As Boolean = TypeOf designer.Dashboard.Items(e.DashboardItemName) Is ChartDashboardItem
    Dim chartDashboardItem As ChartDashboardItem = If(tempVar, CType(designer.Dashboard.Items(e.DashboardItemName), ChartDashboardItem), Nothing)
        If e.ChartControl IsNot Nothing AndAlso tempVar Then
            UpdateChart(chartDashboardItem, e.ChartContext)
        End If
End Sub

Register the Custom Functionality Module

The code below is a complete module you need to add the Chart item’s Constant Line option:

csharp
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using DevExpress.DashboardCommon;
using DevExpress.DashboardWin;
using DevExpress.Utils;
using DevExpress.Utils.Svg;
using DevExpress.XtraBars;
using DevExpress.XtraBars.Ribbon;
using DevExpress.XtraCharts;
using DevExpress.XtraDataLayout;
using DevExpress.XtraEditors;
using DevExpress.XtraEditors.Controls;
using DevExpress.XtraEditors.Repository;

namespace WindowsFormsAppCustomProperties {
    public class ConstantLineUserValueModuleData {
        public string PaneName { get; set; }
        public bool IsSecondaryAxis { get; set; }
        public double Value { get; set; }
        public string GetStringFromData() {
            return PaneName + "_" + IsSecondaryAxis.ToString() + "_" + Value;
        }
    }
    public class ConstantLineUserValueModule {
        public static readonly string PropertyName = "ConstantLine";
        readonly DashboardDesigner designer;
        public ConstantLineUserValueModule(DashboardDesigner designer, SvgImage barImage = null) {
            this.designer = designer;
            BarButtonItem barItem = AddBarItem("Constant line", barImage, designer.Ribbon);
            barItem.ItemClick += BarItem_Click;
            designer.DashboardItemControlUpdated += Designer_DashboardItemControlUpdated;
        }
        BarButtonItem AddBarItem(string caption, SvgImage barImage, RibbonControl ribbon) {
            var page = ribbon.GetDashboardRibbonPage(DashboardBarItemCategory.ChartTools, DashboardRibbonPage.Design);
            RibbonPageGroup group = page.GetGroupByName("Custom Properties");
            if(group == null) {
                group = new RibbonPageGroup("Custom Properties") { Name = "Custom Properties" };
                page.Groups.Add(group);
            }
            var barItem = new BarButtonItem(ribbon.Manager, caption);
            barItem.ImageOptions.SvgImage = barImage;
            group.ItemLinks.Add(barItem);
            return barItem;
        }
        void BarItem_Click(object sender, ItemClickEventArgs e) {
            var chartItem = designer.SelectedDashboardItem as ChartDashboardItem;
            if(chartItem != null) {
                var data = GetConstantLineModuleData(chartItem);
                if(data != null)
                    designer.AddToHistory(new CustomPropertyHistoryItem(chartItem, PropertyName, data.GetStringFromData(), $"{data.PaneName} constant line is set"));
            }
        }
        ConstantLineUserValueModuleData GetConstantLineModuleData(ChartDashboardItem dashboardItem) {
            using(ValueSelectorControl selector = new ValueSelectorControl(dashboardItem.Panes.Select(p => p.Name).ToList())) {
                if(XtraDialog.Show(selector, "Select the required series") == DialogResult.OK) {
                    return selector.ConstantLineModuleData;
                }
            }
            return null;
        }
        void Designer_DashboardItemControlUpdated(object sender, DashboardItemControlEventArgs e) {
            if(e.ChartControl != null && designer.Dashboard.Items[e.DashboardItemName] is ChartDashboardItem chartDashboardItem)
                UpdateChart(chartDashboardItem, e.ChartContext);
        }
        void UpdateChart(ChartDashboardItem chartDashboardItem, ChartContext chartContext) {
            var moduleData = GetDataFromString(chartDashboardItem.CustomProperties.GetValue(PropertyName));
            var pane = chartDashboardItem.Panes.FirstOrDefault(x => x.Name == moduleData.PaneName);
            if(pane != null) {
                ChartSeries dashboardSeries = pane.Series.FirstOrDefault(s => s.PlotOnSecondaryAxis == moduleData.IsSecondaryAxis);
                if(dashboardSeries != null) {
                    Series chartSeries = chartContext.GetControlSeries(dashboardSeries).FirstOrDefault();
                    var chartAxis = (chartSeries.View as XYDiagramSeriesViewBase)?.AxisY;
                    if(chartAxis != null) {
                        ConstantLine line = new ConstantLine() { AxisValue = moduleData.Value };
                        chartAxis.ConstantLines.Clear();
                        chartAxis.ConstantLines.Add(line);
                        line.ShowInLegend = false;
                        line.Color = Color.Green;
                        line.LineStyle.Thickness = 2;
                        line.LineStyle.DashStyle = DashStyle.Dash;
                        line.Title.Text = "Value: " + moduleData.Value.ToString();
                        line.Title.TextColor = line.Color;
                    }
                }
            }
        }
        ConstantLineUserValueModuleData GetDataFromString(string customPropertyValue) {
            if(!string.IsNullOrEmpty(customPropertyValue)) {
                var array = customPropertyValue.Split('_');
                return new ConstantLineUserValueModuleData() {
                    PaneName = array[0],
                    IsSecondaryAxis = bool.Parse(array[1]),
                    Value = Convert.ToDouble(array[2])
                };
            }
            return new ConstantLineUserValueModuleData();
        }
    }
    public class ValueSelectorControl: XtraUserControl {
        public ConstantLineUserValueModuleData ConstantLineModuleData { get; }
        public ValueSelectorControl(List<string> paneNames) {
            DataLayoutControl dataLayoutControl = new DataLayoutControl();
            BindingSource source = new BindingSource();
            ConstantLineModuleData = new ConstantLineUserValueModuleData();
            source.DataSource = ConstantLineModuleData;
            dataLayoutControl.DataSource = source;
            dataLayoutControl.FieldRetrieving += (s, e) => {
                if(e.FieldName == nameof(ConstantLineModuleData.PaneName)) {
                    e.EditorType = typeof(LookUpEdit);
                    e.Handled = true;
                }
            };
            dataLayoutControl.FieldRetrieved += (s, e) => {
                if(e.FieldName == nameof(ConstantLineModuleData.PaneName))
                    InitRepositoryItem(e.RepositoryItem, paneNames);
            };
            dataLayoutControl.RetrieveFields();
            dataLayoutControl.Dock = DockStyle.Fill;
            Controls.Add(dataLayoutControl);
            Dock = DockStyle.Top;
        }
        void InitRepositoryItem<T>(RepositoryItem ri, List<T> list) {
            var lookUpEdit = ri as RepositoryItemLookUpEdit;
            lookUpEdit.TextEditStyle = TextEditStyles.DisableTextEditor;
            lookUpEdit.AllowNullInput = DefaultBoolean.False;
            lookUpEdit.DataSource = list;
        }
    }
}
vb
Imports System
Imports System.Collections.Generic
Imports System.Drawing
Imports System.Linq
Imports System.Windows.Forms
Imports DevExpress.DashboardCommon
Imports DevExpress.DashboardWin
Imports DevExpress.Utils
Imports DevExpress.Utils.Svg
Imports DevExpress.XtraBars
Imports DevExpress.XtraBars.Ribbon
Imports DevExpress.XtraCharts
Imports DevExpress.XtraDataLayout
Imports DevExpress.XtraEditors
Imports DevExpress.XtraEditors.Controls
Imports DevExpress.XtraEditors.Repository

Namespace WindowsFormsAppCustomProperties
    Public Class ConstantLineUserValueModuleData
        Public Property PaneName() As String
        Public Property IsSecondaryAxis() As Boolean
        Public Property Value() As Double

        Public Function GetStringFromData() As String
            Return PaneName & "_" & IsSecondaryAxis.ToString() & "_" & Value
        End Function
    End Class
    Public Class ConstantLineUserValueModule
        Public Shared ReadOnly PropertyName As String = "ConstantLine"
        Private ReadOnly designer As DashboardDesigner

        Public Sub New(ByVal designer As DashboardDesigner, Optional ByVal barImage As SvgImage = Nothing)
            Me.designer = designer
            Dim barItem As BarButtonItem = AddBarItem("Constant line", barImage, designer.Ribbon)
            AddHandler barItem.ItemClick, AddressOf BarItem_Click
            AddHandler designer.DashboardItemControlUpdated, AddressOf Designer_DashboardItemControlUpdated
        End Sub
        Private Function AddBarItem(ByVal caption As String, ByVal barImage As SvgImage, ByVal ribbon As RibbonControl) As BarButtonItem
            Dim page = ribbon.GetDashboardRibbonPage(DashboardBarItemCategory.ChartTools, DashboardRibbonPage.Design)
            Dim group As RibbonPageGroup = page.GetGroupByName("Custom Properties")
            If group Is Nothing Then
                group = New RibbonPageGroup("Custom Properties") With {.Name = "Custom Properties"}
                page.Groups.Add(group)
            End If
            Dim barItem = New BarButtonItem(ribbon.Manager, caption)
            barItem.ImageOptions.SvgImage = barImage
            group.ItemLinks.Add(barItem)
            Return barItem
        End Function
        Private Sub BarItem_Click(ByVal sender As Object, ByVal e As ItemClickEventArgs)
            Dim chartItem = TryCast(designer.SelectedDashboardItem, ChartDashboardItem)
            If chartItem IsNot Nothing Then
                Dim data = GetConstantLineModuleData(chartItem)
                If data IsNot Nothing Then
                    designer.AddToHistory(New CustomPropertyHistoryItem(chartItem, PropertyName, data.GetStringFromData(), $"{data.PaneName} constant line is set"))
                End If
            End If
        End Sub
        Private Function GetConstantLineModuleData(ByVal dashboardItem As ChartDashboardItem) As ConstantLineUserValueModuleData
            Using selector As New ValueSelectorControl(dashboardItem.Panes.Select(Function(p) p.Name).ToList())
                If XtraDialog.Show(selector, "Select the required series") = DialogResult.OK Then
                    Return selector.ConstantLineModuleData
                End If
            End Using
            Return Nothing
        End Function
        Private Sub Designer_DashboardItemControlUpdated(ByVal sender As Object, ByVal e As DashboardItemControlEventArgs)
            Dim tempVar As Boolean = TypeOf designer.Dashboard.Items(e.DashboardItemName) Is ChartDashboardItem
            Dim chartDashboardItem As ChartDashboardItem = If(tempVar, CType(designer.Dashboard.Items(e.DashboardItemName), ChartDashboardItem), Nothing)
            If e.ChartControl IsNot Nothing AndAlso tempVar Then
                UpdateChart(chartDashboardItem, e.ChartContext)
            End If
        End Sub
        Private Sub UpdateChart(ByVal chartDashboardItem As ChartDashboardItem, ByVal chartContext As ChartContext)
            Dim moduleData = GetDataFromString(chartDashboardItem.CustomProperties.GetValue(PropertyName))
            Dim pane = chartDashboardItem.Panes.FirstOrDefault(Function(x) x.Name = moduleData.PaneName)
            If pane IsNot Nothing Then
                Dim dashboardSeries As ChartSeries = pane.Series.FirstOrDefault(Function(s) s.PlotOnSecondaryAxis = moduleData.IsSecondaryAxis)
                If dashboardSeries IsNot Nothing Then
                    Dim chartSeries As Series = chartContext.GetControlSeries(dashboardSeries).FirstOrDefault()
                    Dim chartAxis = TryCast(chartSeries.View, XYDiagramSeriesViewBase)?.AxisY
                    If chartAxis IsNot Nothing Then
                        Dim line As New ConstantLine() With {.AxisValue = moduleData.Value}
                        chartAxis.ConstantLines.Clear()
                        chartAxis.ConstantLines.Add(line)
                        line.ShowInLegend = False
                        line.Color = Color.Green
                        line.LineStyle.Thickness = 2
                        line.LineStyle.DashStyle = DashStyle.Dash
                        line.Title.Text = "Value: " & moduleData.Value.ToString()
                        line.Title.TextColor = line.Color
                    End If
                End If
            End If
        End Sub
        Private Function GetDataFromString(ByVal customPropertyValue As String) As ConstantLineUserValueModuleData
            If Not String.IsNullOrEmpty(customPropertyValue) Then
                Dim array = customPropertyValue.Split("_"c)
                Return New ConstantLineUserValueModuleData() With {
                    .PaneName = array(0),
                    .IsSecondaryAxis = Boolean.Parse(array(1)),
                    .Value = Convert.ToDouble(array(2))
                }
            End If
            Return New ConstantLineUserValueModuleData()
        End Function
    End Class
    Public Class ValueSelectorControl
        Inherits XtraUserControl

        Public ReadOnly Property ConstantLineModuleData() As ConstantLineUserValueModuleData

        Public Sub New(ByVal paneNames As List(Of String))
            Dim dataLayoutControl As New DataLayoutControl()
            Dim source As New BindingSource()
            ConstantLineModuleData = New ConstantLineUserValueModuleData()
            source.DataSource = ConstantLineModuleData
            dataLayoutControl.DataSource = source
            AddHandler dataLayoutControl.FieldRetrieving, Sub(s, e)
                If e.FieldName = NameOf(ConstantLineModuleData.PaneName) Then
                    e.EditorType = GetType(LookUpEdit)
                    e.Handled = True
                End If
            End Sub
            AddHandler dataLayoutControl.FieldRetrieved, Sub(s, e)
                If e.FieldName = NameOf(ConstantLineModuleData.PaneName) Then
                    InitRepositoryItem(e.RepositoryItem, paneNames)
                End If
            End Sub
            dataLayoutControl.RetrieveFields()
            dataLayoutControl.Dock = DockStyle.Fill
            Controls.Add(dataLayoutControl)
            Dock = DockStyle.Top
        End Sub
        Private Sub InitRepositoryItem(Of T)(ByVal ri As RepositoryItem, ByVal list As List(Of T))
            Dim lookUpEdit = TryCast(ri, RepositoryItemLookUpEdit)
            lookUpEdit.TextEditStyle = TextEditStyles.DisableTextEditor
            lookUpEdit.AllowNullInput = DefaultBoolean.False
            lookUpEdit.DataSource = list
        End Sub
    End Class
End Namespace

Register it before you load a dashboard to apply settings to this dashboard. For this, create a new ConstantLineUserValueModule instance and pass the Dashboard Designer for which you register a custom property. You can create the SvgImageCollection instance to store vector images and use one of them as the bar item’s icon.

csharp
using WindowsFormsAppCustomProperties;
//...
public Form1() {
    InitializeComponent();
    new ConstantLineUserValueModule(dashboardDesigner1, svgImageCollection1["chartrangearea"]);
    dashboardDesigner1.LoadDashboard("../../Dashboard/newDashboard.xml");
//...
}
vb
Imports WindowsFormsAppCustomProperties
'...
Public Sub New()
    InitializeComponent()
    Dim TempConstantLineUserValueModule As ConstantLineUserValueModule = New ConstantLineUserValueModule(dashboardDesigner1, svgImageCollection1("chartrangearea"))
    dashboardDesigner1.LoadDashboard("../../Dashboard/newDashboard.xml")
'...
End Sub

Result

After you register the ChartScaleBreakModule module, the ConstantLine button is added that draws the constant line for the selected Chart item in a dashboard. Clicking the button invokes the ValueSelectorControl editor that allows users to specify the pane, axis, and constant line’s position.

You can undo/redo actions, because information about the scale break property’s state is saved in the history.

Tip

You can also download the multiplatform example on GitHub: Constant Lines

See Also

Create Custom Properties