Free cookie consent management tool by TermsFeed Policy Generator

source: branches/histogram/HeuristicLab.Analysis.Views/3.3/DataTableView.cs @ 6016

Last change on this file since 6016 was 6016, checked in by abeham, 13 years ago

#1465

  • added sorting of series to move them back and forth
    • the implementation currently is quite memory intensive in that all data rows are cloned, the collection is cleared and then they're readded in the correct order. Since the underlying collection is a collection and not a list I don't have the possibilities to insert them.
  • fixed histogram configuration
  • added a crude check if there are incompatibilities with bars
File size: 24.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2011 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Drawing;
25using System.Linq;
26using System.Windows.Forms;
27using System.Windows.Forms.DataVisualization.Charting;
28using HeuristicLab.Collections;
29using HeuristicLab.Core.Views;
30using HeuristicLab.MainForm;
31
32namespace HeuristicLab.Analysis.Views {
33  /// <summary>
34  /// The visual representation of a <see cref="Variable"/>.
35  /// </summary>
36  [View("DataTable View")]
37  [Content(typeof(DataTable), true)]
38  public partial class DataTableView : NamedItemView {
39    protected List<Series> invisibleSeries;
40    protected Dictionary<IObservableList<double>, DataRow> valuesRowsTable;
41    /// <summary>
42    /// Gets or sets the variable to represent visually.
43    /// </summary>
44    /// <remarks>Uses property <see cref="ViewBase.Item"/> of base class <see cref="ViewBase"/>.
45    /// No own data storage present.</remarks>
46    public new DataTable Content {
47      get { return (DataTable)base.Content; }
48      set { base.Content = value; }
49    }
50
51    /// <summary>
52    /// Initializes a new instance of <see cref="VariableView"/> with caption "Variable".
53    /// </summary>
54    public DataTableView() {
55      InitializeComponent();
56      valuesRowsTable = new Dictionary<IObservableList<double>, DataRow>();
57      invisibleSeries = new List<Series>();
58      chart.CustomizeAllChartAreas();
59      chart.ChartAreas[0].CursorX.Interval = 1;
60    }
61
62    #region Event Handler Registration
63    /// <summary>
64    /// Removes the eventhandlers from the underlying <see cref="Variable"/>.
65    /// </summary>
66    /// <remarks>Calls <see cref="ViewBase.RemoveItemEvents"/> of base class <see cref="ViewBase"/>.</remarks>
67    protected override void DeregisterContentEvents() {
68      foreach (DataRow row in Content.Rows)
69        DeregisterDataRowEvents(row);
70      Content.VisualPropertiesChanged -= new EventHandler(Content_VisualPropertiesChanged);
71      Content.Rows.ItemsAdded -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded);
72      Content.Rows.ItemsRemoved -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved);
73      Content.Rows.ItemsReplaced -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced);
74      Content.Rows.CollectionReset -= new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset);
75      base.DeregisterContentEvents();
76    }
77
78    /// <summary>
79    /// Adds eventhandlers to the underlying <see cref="Variable"/>.
80    /// </summary>
81    /// <remarks>Calls <see cref="ViewBase.AddItemEvents"/> of base class <see cref="ViewBase"/>.</remarks>
82    protected override void RegisterContentEvents() {
83      base.RegisterContentEvents();
84      Content.VisualPropertiesChanged += new EventHandler(Content_VisualPropertiesChanged);
85      Content.Rows.ItemsAdded += new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded);
86      Content.Rows.ItemsRemoved += new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved);
87      Content.Rows.ItemsReplaced += new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced);
88      Content.Rows.CollectionReset += new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset);
89      foreach (DataRow row in Content.Rows)
90        RegisterDataRowEvents(row);
91    }
92
93    /// <summary>
94    /// Automatically called for every existing data row and whenever a data row is added
95    /// to the data table. Do not call this method directly.
96    /// </summary>
97    /// <param name="row">The DataRow that was added.</param>
98    protected virtual void RegisterDataRowEvents(DataRow row) {
99      row.NameChanged += new EventHandler(Row_NameChanged);
100      row.VisualPropertiesChanged += new EventHandler(Row_VisualPropertiesChanged);
101      valuesRowsTable.Add(row.Values, row);
102      row.Values.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded);
103      row.Values.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved);
104      row.Values.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced);
105      row.Values.ItemsMoved += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved);
106      row.Values.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset);
107    }
108
109    /// <summary>
110    /// Automatically called for every data row that is removed from the DataTable. Do
111    /// not directly call this method.
112    /// </summary>
113    /// <param name="row">The DataRow that was removed.</param>
114    protected virtual void DeregisterDataRowEvents(DataRow row) {
115      row.Values.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded);
116      row.Values.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved);
117      row.Values.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced);
118      row.Values.ItemsMoved -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved);
119      row.Values.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset);
120      valuesRowsTable.Remove(row.Values);
121      row.VisualPropertiesChanged -= new EventHandler(Row_VisualPropertiesChanged);
122      row.NameChanged -= new EventHandler(Row_NameChanged);
123    }
124    #endregion
125
126    protected override void OnContentChanged() {
127      base.OnContentChanged();
128      invisibleSeries.Clear();
129      chart.Titles[0].Text = string.Empty;
130      chart.ChartAreas[0].AxisX.Title = string.Empty;
131      chart.ChartAreas[0].AxisY.Title = string.Empty;
132      chart.ChartAreas[0].AxisY2.Title = string.Empty;
133      chart.Series.Clear();
134      if (Content != null) {
135        chart.Titles[0].Text = Content.Name;
136        foreach (DataRow row in Content.Rows)
137          AddDataRow(row);
138        ConfigureChartArea(chart.ChartAreas[0]);
139      }
140    }
141
142    protected override void SetEnabledStateOfControls() {
143      base.SetEnabledStateOfControls();
144      chart.Enabled = Content != null;
145    }
146
147    /// <summary>
148    /// Add the DataRow as a series to the chart.
149    /// </summary>
150    /// <param name="row">DataRow to add as series to the chart.</param>
151    protected virtual void AddDataRow(DataRow row) {
152      Series series = new Series(row.Name);
153      ConfigureSeries(series, row);
154      FillSeriesWithRowValues(series, row);
155
156      chart.Series.Add(series);
157      chart.ChartAreas[0].RecalculateAxesScale();
158      ConfigureChartArea(chart.ChartAreas[0]);
159      UpdateYCursorInterval();
160    }
161
162    private void ConfigureSeries(Series series, DataRow row) {
163      RemoveCustomPropertyIfExists(series, "PointWidth");
164      series.BorderWidth = 1;
165      series.BorderDashStyle = ChartDashStyle.Solid;
166
167      switch (row.VisualProperties.ChartType) {
168        case DataRowVisualProperties.DataRowChartType.Line:
169          series.ChartType = SeriesChartType.FastLine;
170          series.BorderWidth = row.VisualProperties.LineWidth;
171          series.BorderDashStyle = ConvertLineStyle(row.VisualProperties.LineStyle);
172          break;
173        case DataRowVisualProperties.DataRowChartType.Bars:
174          if (!chart.Series.Any(x => x.ChartType != SeriesChartType.Bar && x.ChartType != SeriesChartType.StackedBar && x.ChartType != SeriesChartType.StackedBar100))
175            series.ChartType = SeriesChartType.Bar;
176          else {
177            series.ChartType = SeriesChartType.FastPoint; //default
178            row.VisualProperties.ChartType = DataRowVisualProperties.DataRowChartType.Points;
179          }
180          break;
181        case DataRowVisualProperties.DataRowChartType.Columns:
182          series.ChartType = SeriesChartType.Column;
183          break;
184        case DataRowVisualProperties.DataRowChartType.Points:
185          series.ChartType = SeriesChartType.FastPoint;
186          break;
187        case DataRowVisualProperties.DataRowChartType.Histogram:
188          series.ChartType = SeriesChartType.Column;
189          series.SetCustomProperty("PointWidth", "1");
190          break;
191        default:
192          series.ChartType = SeriesChartType.FastPoint;
193          break;
194      }
195      series.YAxisType = row.VisualProperties.SecondYAxis ? AxisType.Secondary : AxisType.Primary;
196      series.XAxisType = row.VisualProperties.SecondXAxis ? AxisType.Secondary : AxisType.Primary;
197      if (row.VisualProperties.Color != Color.Empty)
198        series.Color = row.VisualProperties.Color;
199      else series.Color = Color.Empty;
200      series.ToolTip = row.Name + " X = #INDEX, Y = #VAL";
201    }
202
203    private void ConfigureChartArea(ChartArea area) {
204      area.AxisX.Title = Content.VisualProperties.XAxisTitle;
205      area.AxisX2.Title = Content.VisualProperties.SecondXAxisTitle;
206      area.AxisY.Title = Content.VisualProperties.YAxisTitle;
207      area.AxisY2.Title = Content.VisualProperties.SecondYAxisTitle;
208      area.RecalculateAxesScale();
209      area.AxisX.Minimum = Content.VisualProperties.XAxisMinimumFixedValue;
210      area.AxisX.Maximum = Content.VisualProperties.XAxisMaximumFixedValue;
211      area.AxisX2.Minimum = Content.VisualProperties.SecondXAxisMinimumFixedValue;
212      area.AxisX2.Maximum = Content.VisualProperties.SecondXAxisMaximumFixedValue;
213      area.AxisY.Minimum = Content.VisualProperties.YAxisMinimumFixedValue;
214      area.AxisY.Maximum = Content.VisualProperties.YAxisMaximumFixedValue;
215      area.AxisY2.Minimum = Content.VisualProperties.SecondYAxisMinimumFixedValue;
216      area.AxisY2.Maximum = Content.VisualProperties.SecondYAxisMaximumFixedValue;
217    }
218
219    /// <summary>
220    /// Set the Y Cursor interval to visible points of enabled series.
221    /// </summary>
222    protected virtual void UpdateYCursorInterval() {
223      double interestingValuesRange = (
224        from series in chart.Series
225        where series.Enabled
226        let values = (from point in series.Points
227                      where !point.IsEmpty
228                      select point.YValues[0]).DefaultIfEmpty(1.0)
229        let range = values.Max() - values.Min()
230        where range > 0.0
231        select range
232        ).DefaultIfEmpty(1.0).Min();
233
234      double digits = (int)Math.Log10(interestingValuesRange) - 3;
235      double yZoomInterval = Math.Pow(10, digits);
236      this.chart.ChartAreas[0].CursorY.Interval = yZoomInterval;
237    }
238
239
240    /// <summary>
241    /// Remove the corresponding series for a certain DataRow.
242    /// </summary>
243    /// <param name="row">DataRow which series should be removed.</param>
244    protected virtual void RemoveDataRow(DataRow row) {
245      Series series = chart.Series[row.Name];
246      chart.Series.Remove(series);
247      if (invisibleSeries.Contains(series))
248        invisibleSeries.Remove(series);
249      chart.ChartAreas[0].RecalculateAxesScale();
250    }
251
252    #region Event Handlers
253    #region Content Event Handlers
254    protected override void Content_NameChanged(object sender, EventArgs e) {
255      if (InvokeRequired)
256        Invoke(new EventHandler(Content_NameChanged), sender, e);
257      else {
258        chart.Titles[0].Text = Content.Name;
259        base.Content_NameChanged(sender, e);
260      }
261    }
262    private void Content_VisualPropertiesChanged(object sender, EventArgs e) {
263      if (InvokeRequired)
264        Invoke(new EventHandler(Content_VisualPropertiesChanged), sender, e);
265      else {
266        ConfigureChartArea(chart.ChartAreas[0]);
267      }
268    }
269    #endregion
270    #region Rows Event Handlers
271    private void Rows_ItemsAdded(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
272      if (InvokeRequired)
273        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded), sender, e);
274      else {
275        foreach (DataRow row in e.Items) {
276          AddDataRow(row);
277          RegisterDataRowEvents(row);
278        }
279      }
280    }
281    private void Rows_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
282      if (InvokeRequired)
283        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved), sender, e);
284      else {
285        foreach (DataRow row in e.Items) {
286          DeregisterDataRowEvents(row);
287          RemoveDataRow(row);
288        }
289      }
290    }
291    private void Rows_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
292      if (InvokeRequired)
293        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced), sender, e);
294      else {
295        foreach (DataRow row in e.OldItems) {
296          DeregisterDataRowEvents(row);
297          RemoveDataRow(row);
298        }
299        foreach (DataRow row in e.Items) {
300          AddDataRow(row);
301          RegisterDataRowEvents(row);
302        }
303      }
304    }
305    private void Rows_CollectionReset(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
306      if (InvokeRequired)
307        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset), sender, e);
308      else {
309        foreach (DataRow row in e.OldItems) {
310          DeregisterDataRowEvents(row);
311          RemoveDataRow(row);
312        }
313        foreach (DataRow row in e.Items) {
314          AddDataRow(row);
315          RegisterDataRowEvents(row);
316        }
317      }
318    }
319    #endregion
320    #region Row Event Handlers
321    private void Row_VisualPropertiesChanged(object sender, EventArgs e) {
322      if (InvokeRequired)
323        Invoke(new EventHandler(Row_VisualPropertiesChanged), sender, e);
324      else {
325        DataRow row = (DataRow)sender;
326        Series series = chart.Series[row.Name];
327        series.Points.Clear();
328        ConfigureSeries(series, row);
329        FillSeriesWithRowValues(series, row);
330        chart.ChartAreas[0].RecalculateAxesScale();
331      }
332    }
333    private void Row_NameChanged(object sender, EventArgs e) {
334      if (InvokeRequired)
335        Invoke(new EventHandler(Row_NameChanged), sender, e);
336      else {
337        DataRow row = (DataRow)sender;
338        chart.Series[row.Name].Name = row.Name;
339      }
340    }
341    #endregion
342    #region Values Event Handlers
343    private void Values_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
344      if (InvokeRequired)
345        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded), sender, e);
346      else {
347        DataRow row = null;
348        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
349        if (row != null) {
350          Series rowSeries = chart.Series[row.Name];
351          if (!invisibleSeries.Contains(rowSeries)) {
352            rowSeries.Points.Clear();
353            FillSeriesWithRowValues(rowSeries, row);
354            UpdateYCursorInterval();
355          }
356        }
357      }
358    }
359    private void Values_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
360      if (InvokeRequired)
361        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved), sender, e);
362      else {
363        DataRow row = null;
364        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
365        if (row != null) {
366          Series rowSeries = chart.Series[row.Name];
367          if (!invisibleSeries.Contains(rowSeries)) {
368            rowSeries.Points.Clear();
369            FillSeriesWithRowValues(rowSeries, row);
370            UpdateYCursorInterval();
371          }
372        }
373      }
374    }
375    private void Values_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
376      if (InvokeRequired)
377        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced), sender, e);
378      else {
379        DataRow row = null;
380        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
381        if (row != null) {
382          Series rowSeries = chart.Series[row.Name];
383          if (!invisibleSeries.Contains(rowSeries)) {
384            if (row.VisualProperties.ChartType == DataRowVisualProperties.DataRowChartType.Histogram) {
385              rowSeries.Points.Clear();
386              FillSeriesWithRowValues(rowSeries, row);
387            } else {
388              foreach (IndexedItem<double> item in e.Items) {
389                if (IsInvalidValue(item.Value))
390                  rowSeries.Points[item.Index].IsEmpty = true;
391                else {
392                  rowSeries.Points[item.Index].YValues = new double[] { item.Value };
393                  rowSeries.Points[item.Index].IsEmpty = false;
394                }
395              }
396            }
397            UpdateYCursorInterval();
398          }
399        }
400      }
401    }
402    private void Values_ItemsMoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
403      if (InvokeRequired)
404        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved), sender, e);
405      else {
406        DataRow row = null;
407        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
408        if (row != null) {
409          Series rowSeries = chart.Series[row.Name];
410          if (!invisibleSeries.Contains(rowSeries)) {
411            rowSeries.Points.Clear();
412            FillSeriesWithRowValues(rowSeries, row);
413            UpdateYCursorInterval();
414          }
415        }
416      }
417    }
418
419    private void Values_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
420      if (InvokeRequired)
421        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset), sender, e);
422      else {
423        DataRow row = null;
424        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
425        if (row != null) {
426          Series rowSeries = chart.Series[row.Name];
427          if (!invisibleSeries.Contains(rowSeries)) {
428            rowSeries.Points.Clear();
429            FillSeriesWithRowValues(rowSeries, row);
430            UpdateYCursorInterval();
431          }
432        }
433      }
434    }
435    #endregion
436    #endregion
437
438    #region Chart Event Handlers
439    private void chart_MouseDown(object sender, MouseEventArgs e) {
440      HitTestResult result = chart.HitTest(e.X, e.Y);
441      if (result.ChartElementType == ChartElementType.LegendItem) {
442        ToggleSeriesVisible(result.Series);
443      }
444    }
445    private void chart_MouseMove(object sender, MouseEventArgs e) {
446      HitTestResult result = chart.HitTest(e.X, e.Y);
447      if (result.ChartElementType == ChartElementType.LegendItem)
448        this.Cursor = Cursors.Hand;
449      else
450        this.Cursor = Cursors.Default;
451    }
452    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
453      foreach (LegendItem legendItem in e.LegendItems) {
454        var series = chart.Series[legendItem.SeriesName];
455        if (series != null) {
456          bool seriesIsInvisible = invisibleSeries.Contains(series);
457          foreach (LegendCell cell in legendItem.Cells) {
458            cell.ForeColor = seriesIsInvisible ? Color.Gray : Color.Black;
459          }
460        }
461      }
462    }
463    private void chart_PropertiesClicked(object sender, EventArgs e) {
464      DataTableVisualPropertiesDialog dialog = new DataTableVisualPropertiesDialog(Content);
465      dialog.ShowDialog();
466    }
467    #endregion
468
469    private void ToggleSeriesVisible(Series series) {
470      if (!invisibleSeries.Contains(series)) {
471        series.Points.Clear();
472        invisibleSeries.Add(series);
473      } else {
474        invisibleSeries.Remove(series);
475        if (Content != null) {
476
477          var row = (from r in Content.Rows
478                     where r.Name == series.Name
479                     select r).Single();
480          FillSeriesWithRowValues(series, row);
481          this.chart.Legends[series.Legend].ForeColor = Color.Black;
482          UpdateYCursorInterval();
483        }
484      }
485    }
486
487    private void FillSeriesWithRowValues(Series series, DataRow row) {
488      switch (row.VisualProperties.ChartType) {
489        case DataRowVisualProperties.DataRowChartType.Histogram:
490          CalculateHistogram(series, row);
491          break;
492        default: {
493            for (int i = 0; i < row.Values.Count; i++) {
494              var value = row.Values[i];
495              DataPoint point = new DataPoint();
496              point.XValue = row.VisualProperties.StartIndexZero ? i : i + 1;
497              if (IsInvalidValue(value))
498                point.IsEmpty = true;
499              else
500                point.YValues = new double[] { value };
501              series.Points.Add(point);
502            }
503          }
504          break;
505      }
506    }
507
508    protected virtual void CalculateHistogram(Series series, DataRow row) {
509      series.Points.Clear();
510      if (!row.Values.Any()) return;
511      int bins = row.VisualProperties.Bins;
512
513      double minValue = row.Values.Min();
514      double maxValue = row.Values.Max();
515      double intervalWidth = (maxValue - minValue) / bins;
516      if (intervalWidth <= 0) return;
517
518      if (!row.VisualProperties.ExactBins) {
519        intervalWidth = HumanRoundRange(intervalWidth);
520        minValue = Math.Floor(minValue / intervalWidth) * intervalWidth;
521        maxValue = Math.Ceiling(maxValue / intervalWidth) * intervalWidth;
522      }
523
524      double current = minValue, intervalCenter = intervalWidth / 2.0;
525      int frequency = 0;
526      foreach (double v in row.Values.Where(x => !IsInvalidValue(x)).OrderBy(x => x)) {
527        while (v > current + intervalWidth) {
528          series.Points.AddXY(current + intervalCenter, frequency);
529          current += intervalWidth;
530          frequency = 0;
531        }
532        frequency++;
533      }
534      series.Points.AddXY(current + intervalCenter, frequency);
535    }
536
537    #region Helpers
538    protected void RemoveCustomPropertyIfExists(Series series, string property) {
539      if (series.IsCustomPropertySet(property)) series.DeleteCustomProperty(property);
540    }
541
542    private double HumanRoundRange(double range) {
543      double base10 = Math.Pow(10.0, Math.Floor(Math.Log10(range)));
544      double rounding = range / base10;
545      if (rounding <= 1.5) rounding = 1;
546      else if (rounding <= 2.25) rounding = 2;
547      else if (rounding <= 3.75) rounding = 2.5;
548      else if (rounding <= 7.5) rounding = 5;
549      else rounding = 10;
550      return rounding * base10;
551    }
552
553    private ChartDashStyle ConvertLineStyle(DataRowVisualProperties.DataRowLineStyle dataRowLineStyle) {
554      switch (dataRowLineStyle) {
555        case DataRowVisualProperties.DataRowLineStyle.Dash:
556          return ChartDashStyle.Dash;
557        case DataRowVisualProperties.DataRowLineStyle.DashDot:
558          return ChartDashStyle.DashDot;
559        case DataRowVisualProperties.DataRowLineStyle.DashDotDot:
560          return ChartDashStyle.DashDotDot;
561        case DataRowVisualProperties.DataRowLineStyle.Dot:
562          return ChartDashStyle.Dot;
563        case DataRowVisualProperties.DataRowLineStyle.NotSet:
564          return ChartDashStyle.NotSet;
565        case DataRowVisualProperties.DataRowLineStyle.Solid:
566          return ChartDashStyle.Solid;
567        default:
568          return ChartDashStyle.NotSet;
569      }
570    }
571
572    /// <summary>
573    /// Determines whether a double value can be displayed (converted to Decimal and not an NaN).
574    /// </summary>
575    /// <param name="x">The number to check.</param>
576    /// <returns><code>true</code> if the value can be safely shwon in the chart,
577    /// <code>false</code> otherwise.</returns>
578    protected static bool IsInvalidValue(double x) {
579      return double.IsNaN(x) || x < (double)decimal.MinValue || x > (double)decimal.MaxValue;
580    }
581    #endregion
582  }
583}
Note: See TracBrowser for help on using the repository browser.