windowsforms-114127-controls-and-libraries-chart-control-end-user-features-chart-designer-for-end-users.md
The Chart Designer allows users to customize the chart’s appearance. This topic explains how to invoke and modify the Chart Designer.
This article consists of the following sections:
Create a ChartDesigner class instance and pass a ChartControl that should be customized to the ChartDesigner constructor parameters. Use the ChartDesigner.ShowDialog method to invoke the designer.
ChartDesigner designer = new ChartDesigner(chartControl);
designer.ShowDialog();
Dim designer As New ChartDesigner(chartControl)
designer.ShowDialog()
The following API members allow you to customize the Chart Designer:
ChartDesigner.IconGets or sets the Designer form icon.ChartDesigner.ShowIconGets or sets the value indicating whether the Designer form icon should be shown.ChartDesigner.ShowActualDataSpecifies whether to use sample data or data from the underlying data source to build a chart for the Chart Designer’s preview.ChartDesigner.AddElementMenuOptionsReturns options of the Add Element menu.ChartDesigner.CaptionGets or sets the Designer form caption.ChartDesigner.EnableLargeDataSetWarningGets or sets the value indicating whether the warning about possible freezes on large data sets should be shown.ChartDesigner.AvailableViewTypesGets or sets a list of groups of series views available to show on the chart.ChartDesigner.ChartElementHighlightingOccurs when chart element highlighting begins.ChartDesigner.ChartElementSelectingOccurs when chart element selection begins.ChartDesigner.ChartStructureUpdatingUsed to customize the Chart Designer Elements’ Tree.ChartDesigner.PropertyDescriptorsCustomizingOccurs when property descriptor customization required.
ChartDesigner designer = new ChartDesigner(chartControl1);
designer.Icon = new System.Drawing.Icon("../../../Icon.ico", new System.Drawing.Size(32,32));
designer.ShowIcon = true;
designer.AddElementMenuOptions.ShowAddSeriesMenuItem = false;
designer.AddElementMenuOptions.ShowAddSeriesTitleMenuItem = false;
designer.Caption = "Chart Designer";
designer.EnableLargeDataSetWarning = true;
designer.AvailableViewTypes.Clear();
designer.AvailableViewTypes.Add(new ViewTypeGroup("Available Series", new List<ViewType> { ViewType.Area, ViewType.Spline, ViewType.Bar }));
designer.ChartElementHighlighting += OnChartElementHighlighting;
designer.ChartElementSelecting += OnChartElementSelecting;
designer.ChartStructureUpdating += OnChartStructureUpdating;
designer.PropertyDescriptorsCustomizing += OnDesignerPropertyDescriptorsCustomizing;
designer.ShowDialog();
// Handlers for these events are shown below:
private void OnChartStructureUpdating(object sender, ChartStructureChangingEventArgs e) {
e.ChartModel.Series.AllowAddChild = false;
foreach(SeriesModel seriesModel in e.ChartModel.Series) {
seriesModel.AllowChangeVisibility = false;
seriesModel.AllowRemove = false;
}
e.ChartModel.Legends.ShowInStructureControl = false;
}
private void OnChartElementSelecting(object sender, ChartElementSelectingEventArgs e) {
if(e.ElementModel is SeriesBaseModel) {
e.Cancel = false;
return;
}
if (e.ElementModel is LegendModel) {
e.ShowPropertiesTab = false;
e.ShowDataTab = false;
e.CustomOptionsControl = new CustomLegendOptionsControl(e.ElementModel);
}
}
private void OnChartElementHighlighting(object sender, ChartElementHighlightingEventArgs e) {
SeriesBaseModel series = e.ElementModel as SeriesBaseModel;
if(series == null) {
e.Cancel = true;
return;
}
}
private void OnDesignerPropertyDescriptorsCustomizing(object sender, PropertyDescriptorsCustomizingEventArgs e) {
if (e.ElementModel is ChartModel) {
e.Properties.Remove(e.Properties["BackColor"]);
}
}
Dim designer As New ChartDesigner(chartControl1)
designer.Caption = "Chart Designer"
designer.Icon = New System.Drawing.Icon("../../../Icon.ico", New System.Drawing.Size(32, 32))
designer.ShowIcon = True
designer.AddElementMenuOptions.ShowAddSeriesMenuItem = False
designer.AddElementMenuOptions.ShowAddSeriesTitleMenuItem = False
designer.Caption = "Chart Designer"
designer.EnableLargeDataSetWarning = True
designer.AvailableViewTypes.Clear
designer.AvailableViewTypes.Add(New ViewTypeGroup("Available Series", New List(Of ViewType) From { ViewType.Area, ViewType.Spline, ViewType.Bar }))
AddHandler designer.ChartElementHighlighting, AddressOf OnChartElementHighlighting
AddHandler designer.ChartElementSelecting, AddressOf OnChartElementSelecting
AddHandler designer.ChartStructureUpdating, AddressOf OnChartStructureUpdating
AddHandler designer.PropertyDescriptorsCustomizing, AddressOf OnDesignerPropertyDescriptorsCustomizing
designer.ShowDialog()
' Handlers for these events are shown below:
Private Sub OnChartStructureUpdating(ByVal sender As Object, ByVal e As ChartStructureChangingEventArgs)
e.ChartModel.Series.AllowAddChild = False
For Each seriesModel As SeriesModel In e.ChartModel.Series
seriesModel.AllowChangeVisibility = False
seriesModel.AllowRemove = False
Next
e.ChartModel.Legends.ShowInStructureControl = False
End Sub
Private Sub OnChartElementSelecting(ByVal sender As Object, ByVal e As ChartElementSelectingEventArgs)
If TypeOf e.ElementModel Is SeriesBaseModel Then
e.Cancel = False
Return
End If
If TypeOf e.ElementModel Is LegendModel Then
e.ShowPropertiesTab = False
e.ShowDataTab = False
e.CustomOptionsControl = New CustomLegendOptionsControl(e.ElementModel)
End If
End Sub
Private Sub OnChartElementHighlighting(ByVal sender As Object, ByVal e As ChartElementHighlightingEventArgs)
Dim series As SeriesBaseModel = TryCast(e.ElementModel, SeriesBaseModel)
If series Is Nothing Then
e.Cancel = True
Return
End If
End Sub
Private Sub OnDesignerPropertyDescriptorsCustomizing(ByVal sender As Object, ByVal e As PropertyDescriptorsCustomizingEventArgs)
If TypeOf e.ElementModel Is ChartModel Then
e.Properties.Remove(e.Properties("BackColor"))
End If
End Sub
You can use the ChartDesigner.ChartElementSelecting event to replace the control used in the Options tab. The custom control should inherit the DevExpress.XtraCharts.Designer.CustomOptionsControl class. The image below shows a custom Legend Options tab.
The following code demonstrates how to design the Legend Options tab as shown in the previous image:
#region #CustomOptionsControl
using DevExpress.Utils;
using DevExpress.XtraCharts;
using DevExpress.XtraCharts.Designer;
using System;
using System.Windows.Forms;
namespace CrosshairOptions {
partial class CustomLegendOptionsControl : CustomOptionsControl {
bool updateStarted = false;
LegendModel LegendModel { get { return (LegendModel)this.Model; } }
public CustomLegendOptionsControl(ChartElementModel model) : base(model) {
InitializeComponent();
if(!(model is LegendModel)) throw new ArgumentException("The model must have the LegendModel type.");
}
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
var horizontalAlignments = Enum.GetValues(typeof(LegendAlignmentHorizontal));
cbeHorizontalAlignment.Properties.Items.AddRange(horizontalAlignments);
var verticvalAlignments = Enum.GetValues(typeof(LegendAlignmentVertical));
cbeVerticalAlignment.Properties.Items.AddRange(verticvalAlignments);
UpdateView();
}
public override void OnModelUpdated() {
UpdateView();
}
protected void UpdateView() {
updateStarted = true;
cbeHorizontalAlignment.SelectedItem = LegendModel.AlignmentHorizontal;
cbeVerticalAlignment.SelectedItem = LegendModel.AlignmentVertical;
ceVisible.CheckState = DefaultBooleanToCheckState(LegendModel.Visibility);
updateStarted = false;
}
protected void OnVerticalAlignmentChanged(object sender, EventArgs args) {
if(!updateStarted)
LegendModel.AlignmentVertical = (LegendAlignmentVertical)cbeVerticalAlignment.SelectedItem;
}
protected void OnHorizontalAlignmentChanged(object sender, EventArgs args) {
if(!updateStarted)
LegendModel.AlignmentHorizontal = (LegendAlignmentHorizontal)cbeHorizontalAlignment.SelectedItem;
}
protected void OnVisibilityChanged(object sender, EventArgs args) {
if(!updateStarted)
LegendModel.Visibility = CheckStateToDefaultBoolean(ceVisible.CheckState);
}
protected static CheckState DefaultBooleanToCheckState(DefaultBoolean b) {
switch(b) {
case DefaultBoolean.Default: return CheckState.Indeterminate;
case DefaultBoolean.False: return CheckState.Unchecked;
case DefaultBoolean.True: return CheckState.Checked;
default: throw new Exception("The specified DefaultBoolean value is not supported.");
}
}
protected static DefaultBoolean CheckStateToDefaultBoolean(CheckState b) {
switch(b) {
case CheckState.Indeterminate: return DefaultBoolean.Default;
case CheckState.Unchecked: return DefaultBoolean.False;
case CheckState.Checked: return DefaultBoolean.True;
default: throw new Exception("The specified CheckState value is not supported.");
}
}
}
}
#endregion #CustomOptionsControl
#Region "#CustomOptionsControl"
Imports DevExpress.Utils
Imports DevExpress.XtraCharts
Imports DevExpress.XtraCharts.Designer
Imports System
Imports System.Windows.Forms
Namespace CrosshairOptions
Partial Friend Class CustomLegendOptionsControl
Inherits CustomOptionsControl
Private updateStarted As Boolean = False
Private ReadOnly Property LegendModel() As LegendModel
Get
Return CType(Me.Model, LegendModel)
End Get
End Property
Public Sub New(ByVal model As ChartElementModel)
MyBase.New(model)
InitializeComponent()
If Not(TypeOf model Is LegendModel) Then
Throw New ArgumentException("The model must have the LegendModel type.")
End If
End Sub
Protected Overrides Sub OnLoad(ByVal e As EventArgs)
MyBase.OnLoad(e)
Dim horizontalAlignments = System.Enum.GetValues(GetType(LegendAlignmentHorizontal))
cbeHorizontalAlignment.Properties.Items.AddRange(horizontalAlignments)
Dim verticvalAlignments = System.Enum.GetValues(GetType(LegendAlignmentVertical))
cbeVerticalAlignment.Properties.Items.AddRange(verticvalAlignments)
UpdateView()
End Sub
Public Overrides Sub OnModelUpdated()
UpdateView()
End Sub
Protected Sub UpdateView()
updateStarted = True
cbeHorizontalAlignment.SelectedItem = LegendModel.AlignmentHorizontal
cbeVerticalAlignment.SelectedItem = LegendModel.AlignmentVertical
ceVisible.CheckState = DefaultBooleanToCheckState(LegendModel.Visibility)
updateStarted = False
End Sub
Protected Sub OnVerticalAlignmentChanged(ByVal sender As Object, ByVal args As EventArgs)
If Not updateStarted Then
LegendModel.AlignmentVertical = CType(cbeVerticalAlignment.SelectedItem, LegendAlignmentVertical)
End If
End Sub
Protected Sub OnHorizontalAlignmentChanged(ByVal sender As Object, ByVal args As EventArgs)
If Not updateStarted Then
LegendModel.AlignmentHorizontal = CType(cbeHorizontalAlignment.SelectedItem, LegendAlignmentHorizontal)
End If
End Sub
Protected Sub OnVisibilityChanged(ByVal sender As Object, ByVal args As EventArgs)
If Not updateStarted Then
LegendModel.Visibility = CheckStateToDefaultBoolean(ceVisible.CheckState)
End If
End Sub
Protected Shared Function DefaultBooleanToCheckState(ByVal b As DefaultBoolean) As CheckState
Select Case b
Case DefaultBoolean.Default
Return CheckState.Indeterminate
Case DefaultBoolean.False
Return CheckState.Unchecked
Case DefaultBoolean.True
Return CheckState.Checked
Case Else
Throw New Exception("The specified DefaultBoolean value is not supported.")
End Select
End Function
Protected Shared Function CheckStateToDefaultBoolean(ByVal b As CheckState) As DefaultBoolean
Select Case b
Case CheckState.Indeterminate
Return DefaultBoolean.Default
Case CheckState.Unchecked
Return DefaultBoolean.False
Case CheckState.Checked
Return DefaultBoolean.True
Case Else
Throw New Exception("The specified CheckState value is not supported.")
End Select
End Function
End Class
End Namespace
#End Region ' #CustomOptionsControl
Users configure chart element model properties when they modify chart element properties in the Chart Designer. Model changes apply to a corresponding chart element after the user clicks OK. Models are provided for existing chart elements. If you create a custom element (the colorizer in the example below), you should create and register a model for this element to allow users to edit its options in the Chart Designer.
This example illustrates how to create and register a model (the CustomPointColorizerModel class in this example) for a custom colorizer (the CustomPointColorizer class in this example).
View Example: How to Create a Model for a Custom Chart Element
Step 1. Create a Model
For example, you have a custom colorizer CustomPointColorizer class that looks as follows:
[TypeConverter(typeof(ExpandableObjectConverter))]
public class CustomPointColorizer : ChartColorizerBase {
double threshold = 60;
Color lower = Color.Red;
Color upper = Color.Green;
public double Value {
get { return threshold; }
set { threshold = value; }
}
public Color LowerValuePointColor {
get { return lower; }
set { lower = value; }
}
public Color UpperValuePointColor {
get { return upper; }
set { upper = value; }
}
public override Color GetAggregatedPointColor(object argument, object[] values, SeriesPoint[] points, Palette palette) {
if ((double)values[0] > Value)
return UpperValuePointColor;
else
return LowerValuePointColor;
}
public override Color GetPointColor(object argument, object[] values, object colorKey, Palette palette) {
return Color.Empty;
}
protected override ChartElement CreateObjectForClone() {
return new CustomPointColorizer();
}
public override void Assign(ChartElement obj) {
base.Assign(obj);
CustomPointColorizer colorizer = obj as CustomPointColorizer;
if (colorizer != null) {
Value = colorizer.Value;
LowerValuePointColor = colorizer.LowerValuePointColor;
UpperValuePointColor = colorizer.UpperValuePointColor;
}
}
public override string ToString() {
return "(CustomPointColorizer)";
}
}
<TypeConverter(GetType(ExpandableObjectConverter))>
Public Class CustomPointColorizer
Inherits ChartColorizerBase
Private threshold As Double = 60
Private lower As Color = Color.Red
Private upper As Color = Color.Green
Public Property Value As Double
Get
Return threshold
End Get
Set(ByVal value As Double)
threshold = value
End Set
End Property
Public Property LowerValuePointColor As Color
Get
Return lower
End Get
Set(ByVal value As Color)
lower = value
End Set
End Property
Public Property UpperValuePointColor As Color
Get
Return upper
End Get
Set(ByVal value As Color)
upper = value
End Set
End Property
Public Overrides Function GetAggregatedPointColor(ByVal argument As Object, ByVal values As Object(), ByVal points As SeriesPoint(), ByVal palette As Palette) As Color
If CDbl(values(0)) > Value Then
Return UpperValuePointColor
Else
Return LowerValuePointColor
End If
End Function
Public Overrides Function GetPointColor(ByVal argument As Object, ByVal values As Object(), ByVal colorKey As Object, ByVal palette As Palette) As Color
Return Color.Empty
End Function
Protected Overrides Function CreateObjectForClone() As ChartElement
Return New CustomPointColorizer
End Function
Public Overrides Sub Assign(ByVal obj As ChartElement)
MyBase.Assign(obj)
Dim colorizer = TryCast(obj, CustomPointColorizer)
If colorizer IsNot Nothing Then
Value = colorizer.Value
LowerValuePointColor = colorizer.LowerValuePointColor
UpperValuePointColor = colorizer.UpperValuePointColor
End If
End Sub
Public Overrides Function ToString() As String
Return "(CustomPointColorizer)"
End Function
End Class
Next, create a CustomPointColorizerModel model for your colorizer. This model is used to modify the colorizer options in the Chart Designer’s Properties tab.
public class CustomPointColorizerModel : ChartColorizerBaseModel {
CustomPointColorizer MyColorizer { get { return (CustomPointColorizer)Colorizer; } }
public double Value {
get { return MyColorizer.Value; }
set { SetProperty("Value", value); }
}
public Color LowerValuePointColor {
get { return MyColorizer.LowerValuePointColor; }
set { SetProperty("LowerValuePointColor", value); }
}
public Color UpperValuePointColor {
get { return MyColorizer.UpperValuePointColor; }
set { SetProperty("UpperValuePointColor", value); }
}
public CustomPointColorizerModel(ChartColorizerBase element, CustomModelProvider customModelProvider) : base(element, customModelProvider) {
}
}
Public Class CustomPointColorizerModel
Inherits ChartColorizerBaseModel
Private ReadOnly Property MyColorizer As CustomPointColorizer
Get
Return CType(Colorizer, CustomPointColorizer)
End Get
End Property
Public Property Value As Double
Get
Return MyColorizer.Value
End Get
Set(ByVal value As Double)
SetProperty("Value", value)
End Set
End Property
Public Property LowerValuePointColor As Color
Get
Return MyColorizer.LowerValuePointColor
End Get
Set(ByVal value As Color)
SetProperty("LowerValuePointColor", value)
End Set
End Property
Public Property UpperValuePointColor As Color
Get
Return MyColorizer.UpperValuePointColor
End Get
Set(ByVal value As Color)
SetProperty("UpperValuePointColor", value)
End Set
End Property
Public Sub New(ByVal element As ChartColorizerBase, ByVal customModelProvider As CustomModelProvider)
MyBase.New(element, customModelProvider)
End Sub
End Class
Step 2. Customize the Property Editor
Use the Editor attribute to configure an editor for a Chart Designer property. In this example, you add the CustomPointColorizer item to the list of colorizers for the bar series model’s Colorizer property.
public class SideBySideBarSeriesViewCustomModel : SideBySideBarSeriesViewModel {
[Editor(typeof(CustomColorizerEditor), typeof(UITypeEditor))]
public new ChartColorizerBaseModel Colorizer {
get { return base.Colorizer; }
set { base.Colorizer = value; }
}
public SideBySideBarSeriesViewCustomModel(SideBySideBarSeriesView element, CustomModelProvider customModelProvider)
: base(element, customModelProvider) {
}
}
public class CustomColorizerEditor : UITypeEditor {
IWindowsFormsEditorService editorService;
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) {
editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
var seriesView = context.Instance as SeriesViewBaseModel;
List<ChartColorizerBase> colorizers = GetColorizers();
CustomModelProvider modelProvider = new CustomModelProvider();
modelProvider.RegisterCustomModelType(typeof(CustomPointColorizer), typeof(CustomPointColorizerModel));
var listBox = new ListBoxControl();
listBox.Click += listBox_Click;
listBox.Items.Add("(None)");
foreach (ChartColorizerBase colorizer in colorizers) {
var colorizerModel = ModelHelper.GetModel<ChartColorizerBaseModel>(colorizer, modelProvider);
int index = listBox.Items.Add(colorizerModel);
if (value != null && colorizerModel.GetType() == value.GetType()) {
listBox.SelectedIndex = index;
}
}
editorService.DropDownControl(listBox);
if (listBox.SelectedIndex != 0)
if (value == null || listBox.SelectedItem.GetType() != value.GetType())
return listBox.SelectedItem;
else
return value;
else
return null;
}
void listBox_Click(object sender, EventArgs e) {
this.editorService.CloseDropDown();
}
List<ChartColorizerBase> GetColorizers() {
return new List<ChartColorizerBase>() { new CustomPointColorizer(), new KeyColorColorizer(), new RangeColorizer() };
}
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) {
return UITypeEditorEditStyle.DropDown;
}
}
Public Class SideBySideBarSeriesViewCustomModel
Inherits SideBySideBarSeriesViewModel
<Editor(GetType(CustomColorizerEditor), GetType(UITypeEditor))>
Public Overloads Property Colorizer As ChartColorizerBaseModel
Get
Return MyBase.Colorizer
End Get
Set(ByVal value As ChartColorizerBaseModel)
MyBase.Colorizer = value
End Set
End Property
Public Sub New(ByVal element As SideBySideBarSeriesView, ByVal customModelProvider As CustomModelProvider)
MyBase.New(element, customModelProvider)
End Sub
End Class
Public Class CustomColorizerEditor
Inherits UITypeEditor
Private editorService As IWindowsFormsEditorService
Public Overrides Function EditValue(ByVal context As ITypeDescriptorContext, ByVal provider As IServiceProvider, ByVal value As Object) As Object
editorService = CType(provider.GetService(GetType(IWindowsFormsEditorService)), IWindowsFormsEditorService)
Dim seriesView = TryCast(context.Instance, SeriesViewBaseModel)
Dim colorizers = GetColorizers
Dim modelProvider As CustomModelProvider = New CustomModelProvider
modelProvider.RegisterCustomModelType(GetType(CustomPointColorizer), GetType(CustomPointColorizerModel))
Dim listBox = New ListBoxControl
listBox.Click += AddressOf listBox_Click
listBox.Items.Add("(None)")
For Each colorizer As ChartColorizerBase In colorizers
Dim colorizerModel = ModelHelper.GetModel(Of ChartColorizerBaseModel)(colorizer, modelProvider)
Dim index As Integer = listBox.Items.Add(colorizerModel)
If value IsNot Nothing AndAlso colorizerModel.GetType Is value.GetType Then
listBox.SelectedIndex = index
End If
Next
editorService.DropDownControl(listBox)
If listBox.SelectedIndex IsNot 0 Then
If value Is Nothing OrElse listBox.SelectedItem.GetType IsNot value.GetType Then
Return listBox.SelectedItem
Else
Return value
End If
Else
Return Nothing
End If
End Function
Private Sub listBox_Click(ByVal sender As Object, ByVal e As EventArgs)
editorService.CloseDropDown
End Sub
Private Function GetColorizers() As List(Of ChartColorizerBase)
Return New List(Of ChartColorizerBase) From {
New CustomPointColorizer,
New KeyColorColorizer,
New RangeColorizer
}
End Function
Public Overrides Function GetEditStyle(ByVal context As ITypeDescriptorContext) As UITypeEditorEditStyle
Return UITypeEditorEditStyle.DropDown
End Function
End Class
Step 3. Register Models
To associate the model with the chart element, use the ChartDesigner.RegisterCustomModelType(System.Type,System.Type) method.
void OnButtonClick(object sender, EventArgs e) {
ChartDesigner chartDesigner = new ChartDesigner((ChartControl)this.Controls["MyChart"]);
chartDesigner.RegisterCustomModelType(typeof(CustomPointColorizer), typeof(CustomPointColorizerModel));
chartDesigner.RegisterCustomModelType(typeof(SideBySideBarSeriesView), typeof(SideBySideBarSeriesViewCustomModel));
chartDesigner.ShowDialog(true);
}
Private Sub OnButtonClick(ByVal sender As Object, ByVal e As EventArgs)
Dim chartDesigner As ChartDesigner = New ChartDesigner(CType(Me.Controls("MyChart"), ChartControl))
chartDesigner.RegisterCustomModelType(GetType(CustomPointColorizer), GetType(CustomPointColorizerModel))
chartDesigner.RegisterCustomModelType(GetType(SideBySideBarSeriesView), GetType(SideBySideBarSeriesViewCustomModel))
chartDesigner.ShowDialog(True)
End Sub
Use one of the following approaches to localize text used in the Chart Designer:
Use Satellite Resource Assemblies (standard localization mechanism).