// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
// Purpose: Design-time editors and converter classes for the
// Series and DataPoint properties.
//
#if DESIGNER
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics.CodeAnalysis;
using System.Drawing.Design;
using FastReport.DataVisualization.Charting;
using FastReport.DataVisualization.Charting.ChartTypes;
namespace FastReport.Design.DataVisualization.Charting
{
///
/// UI type editor for the Y data source members of the series.
///
internal class SeriesDataSourceMemberValueAxisUITypeEditor : System.Drawing.Design.UITypeEditor
{
#region Editor methods and properties
internal virtual SeriesDataSourceMemberYCheckedListBox GetDropDownControl(Chart chart, ITypeDescriptorContext context, object value, bool flag)
{
return new SeriesDataSourceMemberYCheckedListBox(chart, value, flag);
}
///
/// Display a drop down list with check boxes.
///
/// Editing context.
/// Provider.
/// Value to edit.
/// Result
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
if (context != null && context.Instance != null && provider != null)
{
IWindowsFormsEditorService edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
if(edSvc != null)
{
Chart chart = ConverterHelper.GetChartFromContext(context);
if(chart != null)
{
// Create control for editing
SeriesDataSourceMemberYCheckedListBox control = this.GetDropDownControl(chart, context, value, true);
// Show drop down control
edSvc.DropDownControl(control);
// Get new enumeration value
value = control.GetNewValue();
}
}
}
return value;
}
///
/// Gets editing style.
///
/// Editing context.
/// Editor style.
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
if (context != null && context.Instance != null)
{
// Check how many Y values in the series.
int yValuesNumber = 1;
if(context.Instance is Series)
{
yValuesNumber = ((Series)context.Instance).YValuesPerPoint;
}
else if(context.Instance is Array)
{
Array array = (Array)context.Instance;
if(array.Length > 0 && array.GetValue(0) is Series)
{
yValuesNumber = Math.Max(yValuesNumber, ((Series)array.GetValue(0)).YValuesPerPoint);
}
}
return (yValuesNumber == 1) ? UITypeEditorEditStyle.None : UITypeEditorEditStyle.DropDown;
}
return base.GetEditStyle(context);
}
#endregion
}
///
/// Checked list box, which is used for the series Y dats source member UI type editing.
///
internal class SeriesDataSourceMemberYCheckedListBox : CheckedListBox
{
// Chart object
private Chart _chart = null;
// Object to edit
protected object editValue = null;
// Indicates that editor was used for the Y values members
protected bool usedForYValue = false;
#region Control constructor
///
/// Public constructor.
///
/// Chart control.
///
/// Value to edit.
/// Indicates that editor was used for the Y values members.
public SeriesDataSourceMemberYCheckedListBox(Chart chart, object editValue, bool usedForYValue)
{
// Set editable value
this.editValue = editValue;
this.usedForYValue = usedForYValue;
// Set control border style
this.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.IntegralHeight = false;
// Fill member items list
//this.FillList();
// Set Chart
_chart = chart;
}
#endregion
#region Control methods
protected override void OnCreateControl()
{
this.FillList();
}
internal virtual ArrayList GetMemberNames()
{
object dataSource = null;
if (ChartWinDesigner.controlDesigner != null)
{
dataSource = ChartWinDesigner.controlDesigner.GetControlDataSource(_chart);
}
// Get list of members
if (dataSource != null)
{
return ChartImage.GetDataSourceMemberNames(dataSource, this.usedForYValue);
}
return new ArrayList();
}
///
/// Fills checked list items
///
private void FillList()
{
// Create arry of current names
string[] currentNames = null;
if (editValue != null && editValue is string)
{
string editedString = (string)editValue;
currentNames = editedString.Split(',');
}
ArrayList memberNames = this.GetMemberNames();
// Fill list with all possible values in the enumeration
foreach (string name in memberNames)
{
// Test if item should be checked by default
bool isChecked = false;
if (currentNames != null)
{
foreach (string curName in currentNames)
{
if (name == curName.Trim())
{
isChecked = true;
}
}
}
// Add items into the list
this.Items.Add(name, isChecked);
}
}
///
/// Gets new enumeration value.
///
/// New enum value.
public string GetNewValue()
{
// Update enumeration flags
string result = String.Empty;
foreach (object checkedItem in this.CheckedItems)
{
if (result.Length > 0)
{
result += ", ";
}
result += (string)checkedItem;
}
// Return value
return result;
}
#endregion
}
///
/// Chart type editor. Paint chart type image in the property grid.
///
internal class ChartTypeEditor : UITypeEditor
{
#region Converter methods
// Reference to the chart type registry
private ChartTypeRegistry _chartTypeRegistry = null;
///
/// Override this function to support chart type drawing
///
/// Descriptor context.
/// Can paint values.
public override bool GetPaintValueSupported(ITypeDescriptorContext context)
{
// Initialize the chartTypeRegistry using context
if (context != null && context.Instance != null)
{
IChartElement chartElement = context.Instance as IChartElement;
if (chartElement != null)
{
this._chartTypeRegistry = chartElement.Common.ChartTypeRegistry;
}
}
// Always return true
return true;
}
///
/// Override this function to support chart type drawing
///
/// Paint value event arguments.
public override void PaintValue(PaintValueEventArgs e)
{
string chartTypeName = String.Empty;
if(_chartTypeRegistry != null && e != null)
{
if(e.Value is string)
{
chartTypeName = (string)e.Value;
}
else if(e.Value is SeriesChartType)
{
chartTypeName = Series.GetChartTypeName((SeriesChartType)e.Value);
}
if(!string.IsNullOrEmpty(chartTypeName))
{
IChartType chartType = _chartTypeRegistry.GetChartType(chartTypeName);
// Get imahe from the chart type
System.Drawing.Image chartImage = null;
if(chartType != null)
{
chartImage = chartType.GetImage(_chartTypeRegistry);
}
// Draw image
if(chartImage != null)
{
e.Graphics.DrawImage(chartImage, e.Bounds);
}
}
}
}
#endregion
}
///
/// Designer editor for the data points collection.
///
internal class DataPointCollectionEditor : ChartCollectionEditor
{
#region Editor methods
///
/// Default constructor
///
public DataPointCollectionEditor() : base(typeof(DataPointCollection))
{
}
///
/// Do not allow to edit if multiple series selected.
///
/// Descriptor context.
/// Service provider.
/// Value to edit.
/// The new value of the object.
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
if (context != null && context.Instance != null)
{
// Save current control type descriptor context
if(!(context.Instance is Series))
{
throw (new InvalidOperationException(SR.ExceptionEditorMultipleSeriesEditiingUnsupported));
}
}
return base.EditValue(context, provider, value);
}
///
/// Create instance of data point object
///
/// Item type.
/// New item instance.
protected override object CreateInstance(Type itemType)
{
if (Context != null && Context.Instance != null)
{
if (Context.Instance is Series)
{
Series series = (Series)Context.Instance;
DataPoint newDataPoint = new DataPoint(series);
return newDataPoint;
}
else if(Context.Instance is Array)
{
throw (new InvalidOperationException(SR.ExceptionEditorMultipleSeriesEditiingUnsupported));
}
}
return base.CreateInstance(itemType);
}
#endregion
}
///
/// Collection editor that supports property help in the property grid
///
internal class ChartCollectionEditor : CollectionEditor
{
#region Editor methods and properties
// Collection editor form
CollectionForm _form = null;
Chart _chart = null;
ITypeDescriptorContext _context = null;
// Help topic string
string _helpTopic = "";
///
/// Object constructor.
///
/// AxisName.
public ChartCollectionEditor(Type type) : base(type)
{
}
///
/// Edit object's value.
///
/// Descriptor context.
/// Service provider.
/// Value to edit.
/// The new value of the object.
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
_context = context;
if (context != null && context.Instance != null)
{
// Save current control type descriptor context
_chart = context.Instance as Chart;
}
INameController controller = value as INameController;
bool isReferenceCollection = controller != null && (value is ChartAreaCollection || value is LegendCollection);
try
{
if (isReferenceCollection)
{
controller.DoSnapshot(true,
new EventHandler(OnNameReferenceChanging),
new EventHandler(OnNameReferenceChanged)
);
controller.IsColectionEditing = true;
}
return base.EditValue(context, provider, value);;
}
finally
{
if (isReferenceCollection)
{
controller.IsColectionEditing = false;
controller.DoSnapshot(false,
new EventHandler(OnNameReferenceChanging),
new EventHandler(OnNameReferenceChanged)
);
}
}
}
///
/// Called when [name reference changing].
///
/// The sender.
/// The instance containing the event data.
private void OnNameReferenceChanging(object sender, NameReferenceChangedEventArgs e)
{
IComponentChangeService svc = _context.GetService(typeof(IComponentChangeService)) as IComponentChangeService;
if (svc != null)
{
svc.OnComponentChanging(this._chart, null);
}
}
///
/// Called when [name reference changed].
///
/// The sender.
/// The instance containing the event data.
private void OnNameReferenceChanged(object sender, NameReferenceChangedEventArgs e)
{
IComponentChangeService svc = _context.GetService(typeof(IComponentChangeService)) as IComponentChangeService;
if (svc != null)
{
svc.OnComponentChanged(this._chart, null, null, null);
}
}
///
/// Sets the specified array as the items of the collection.
///
/// The collection to edit.
/// An array of objects to set as the collection items.
///
/// The newly created collection object or, otherwise, the collection indicated by the parameter.
///
protected override object SetItems(object editValue, object[] value)
{
object result = base.SetItems(editValue, value);
IComponentChangeService svc = _context.GetService(typeof(IComponentChangeService)) as IComponentChangeService;
INameController controller = editValue as INameController;
if (controller != null && svc != null && (editValue is ChartAreaCollection || editValue is LegendCollection))
{
IList newList = (IList)result;
bool elementsRemoved = false;
foreach (ChartNamedElement element in controller.Snapshot)
{
if (newList.IndexOf(element) < 0)
{
elementsRemoved = true;
}
}
if (elementsRemoved)
{
svc.OnComponentChanging(this._chart, null);
ChartNamedElement defaultElement = (ChartNamedElement)(newList.Count > 0 ? newList[0] : null);
foreach (ChartNamedElement element in controller.Snapshot)
{
if (newList.IndexOf(element) < 0)
{
controller.OnNameReferenceChanged(new NameReferenceChangedEventArgs(element, defaultElement));
}
}
svc.OnComponentChanged(this._chart, null, null, null);
}
}
return result;
}
///
/// Ovveride the HelpTopic property to provide different topics,
/// depending on selected property.
///
protected override string HelpTopic
{
get
{
return (_helpTopic.Length == 0) ? base.HelpTopic : _helpTopic;
}
}
///
/// Displaying help for the curently selected item in the property grid
///
protected override void ShowHelp()
{
// Init topic name
_helpTopic = "";
PropertyGrid grid = this.GetPropertyGrid(_form.Controls);
// Check currently selected grid item
if(grid != null)
{
GridItem item = grid.SelectedGridItem;
if(item != null && (item.GridItemType == GridItemType.Property || item.GridItemType == GridItemType.ArrayValue))
{
_helpTopic = item.PropertyDescriptor.ComponentType.ToString() + "." + item.PropertyDescriptor.Name;
}
}
// Call base class
base.ShowHelp();
// Re-Init topic name
_helpTopic = "";
}
///
/// Returns the collection form property grid. Added for VS2005 compatibility.
///
///
///
private PropertyGrid GetPropertyGrid(System.Windows.Forms.Control.ControlCollection controls)
{
foreach (System.Windows.Forms.Control control in controls)
{
PropertyGrid grid = control as PropertyGrid;
if (grid != null)
{
return grid;
}
if (control.Controls.Count > 0)
{
grid = GetPropertyGrid(control.Controls);
if (grid != null)
{
return grid;
}
}
}
return null;
}
///
/// Collect the collection editor form buttons into array. Added for VS2005 compatibility.
///
///
///
private void CollectButtons(ArrayList buttons, System.Windows.Forms.Control.ControlCollection controls)
{
foreach (System.Windows.Forms.Control control in controls)
{
if (control is System.Windows.Forms.Button)
{
buttons.Add(control);
}
if (control.Controls.Count > 0)
{
CollectButtons(buttons, control.Controls);
}
}
}
///
/// Cretaes form for collection editing.
///
/// Form object.
protected override CollectionForm CreateCollectionForm()
{
_form = base.CreateCollectionForm();
// Changed Apr 29, DT, for VS2005 compatibility
PropertyGrid grid = GetPropertyGrid(_form.Controls);
if (grid != null)
{
// Show properties help
grid.HelpVisible = true;
grid.CommandsVisibleIfAvailable = true;
// Hookup to the update events
grid.PropertyValueChanged += new PropertyValueChangedEventHandler(this.OnPropertyChanged);
grid.ControlAdded += new ControlEventHandler(this.OnControlAddedRemoved);
grid.ControlRemoved += new ControlEventHandler(this.OnControlAddedRemoved);
}
// Changed Apr 29, DT, for VS2005 compatibility
ArrayList buttons = new ArrayList();
this.CollectButtons(buttons, _form.Controls);
foreach (System.Windows.Forms.Button button in buttons)
{
if (button.DialogResult == DialogResult.OK ||
button.DialogResult == DialogResult.Cancel)
{
button.Click += new EventHandler(this.OnOkClicked);
}
}
return _form;
}
///
/// Update design-time HTML when OK button is clicked in the collection editor
///
private void OnOkClicked(object sender, EventArgs e)
{
// Clear the help topic
_helpTopic = "";
}
///
/// Update design-time HTML when propery is added or removed
///
private void OnControlAddedRemoved(object sender, ControlEventArgs e)
{
}
///
/// Update design-time HTML when propery is changed
///
private void OnPropertyChanged(object sender, PropertyValueChangedEventArgs e)
{
}
///
/// Checks if the instance belongs to Chart type or contains the field of chart type.
/// NOTE: Required for the Diagram product.
///
///
/// Instance to check.
///
///
/// Object of chart type.
///
public static object GetChartReference(object instance)
{
// Check instance type.
if(instance is Chart)
{
return instance;
}
// Read chart reference from the "chart" field.
IChartElement element = instance as IChartElement;
if (element != null)
return element.Common.Chart;
else
throw (new InvalidOperationException(SR.ExceptionEditorContectInstantsIsNotChartObject));
}
protected override void DestroyInstance(object instance)
{
// don't destroy instance because remove is clicked.
}
#endregion
}
///
/// Designer editor for the data series collection.
///
internal class SeriesCollectionEditor : ChartCollectionEditor
{
#region Editor methods
///
/// Object constructor.
///
public SeriesCollectionEditor() : base(typeof(SeriesCollection))
{
}
internal static Series CreateNewSeries(Chart control, string suggestedChartArea)
{
int countSeries = control.Series.Count + 1;
string seriesName = "Series" + countSeries.ToString(System.Globalization.CultureInfo.InvariantCulture);
// Check if this name already in use
bool seriesFound = true;
while (seriesFound)
{
seriesFound = false;
foreach (Series series in control.Series)
{
if (series.Name == seriesName)
{
seriesFound = true;
}
}
if (seriesFound)
{
++countSeries;
seriesName = "Series" + countSeries.ToString(System.Globalization.CultureInfo.InvariantCulture);
}
}
// Create new series
Series newSeries = new Series(seriesName);
// Check if default chart area name exists
if (control.ChartAreas.Count > 0)
{
bool defaultFound = false;
if (!string.IsNullOrEmpty(suggestedChartArea) &&
control.ChartAreas.IndexOf(suggestedChartArea) != -1)
{
newSeries.ChartArea = suggestedChartArea;
defaultFound = true;
}
else
{
foreach (ChartArea area in control.ChartAreas)
{
if (area.Name == newSeries.ChartArea)
{
defaultFound = true;
break;
}
}
}
// If default chart area was not found - use name of the first area
if (!defaultFound)
{
newSeries.ChartArea = control.ChartAreas[0].Name;
}
// Check if series area is circular
if (control.ChartAreas[newSeries.ChartArea].chartAreaIsCurcular)
{
// Change default chart type
newSeries.ChartTypeName = ChartTypeNames.Radar;
// Check if it's a Polar chart type
IChartType chartType = control.ChartAreas[newSeries.ChartArea].GetCircularChartType() as IChartType;
if (chartType != null && String.Compare(chartType.Name, ChartTypeNames.Polar, StringComparison.OrdinalIgnoreCase) == 0)
{
newSeries.ChartTypeName = ChartTypeNames.Polar;
}
}
}
return newSeries;
}
///
/// Create series instance in the editor
///
/// Item type.
/// Newly created item.
protected override object CreateInstance(Type itemType)
{
if (Context != null && Context.Instance != null)
{
Chart control = (Chart)GetChartReference(Context.Instance);
return SeriesCollectionEditor.CreateNewSeries(control, String.Empty);
}
return base.CreateInstance(itemType);
}
#endregion
}
}
#endif