source: branches/DataPreprocessing/HeuristicLab.DataPreprocessing.Views/3.3/PreprocessingDataTableView.cs @ 10804

Last change on this file since 10804 was 10804, checked in by rstoll, 5 years ago
  • SelectionEvent added
File size: 31.0 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2013 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.DataPreprocessing.Implementations;
31using HeuristicLab.MainForm;
32
33namespace HeuristicLab.Analysis.Views {
34  [View("Preprocessing DataTable View")]
35  [Content(typeof(PreprocessingDataTable), false)]
36  public partial class PreprocessingDataTableView : ItemView, IConfigureableView {
37    protected List<Series> invisibleSeries;
38    protected Dictionary<IObservableList<double>, DataRow> valuesRowsTable;
39
40    public new PreprocessingDataTable Content {
41      get { return (PreprocessingDataTable)base.Content; }
42      set { base.Content = value; }
43    }
44
45    public PreprocessingDataTableView() {
46      InitializeComponent();
47      valuesRowsTable = new Dictionary<IObservableList<double>, DataRow>();
48      invisibleSeries = new List<Series>();
49      chart.CustomizeAllChartAreas();
50      chart.ChartAreas[0].CursorX.Interval = 1;
51    }
52
53    #region Event Handler Registration
54    protected override void DeregisterContentEvents() {
55      foreach (DataRow row in Content.Rows)
56        DeregisterDataRowEvents(row);
57      Content.VisualPropertiesChanged -= new EventHandler(Content_VisualPropertiesChanged);
58      Content.Rows.ItemsAdded -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded);
59      Content.Rows.ItemsRemoved -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved);
60      Content.Rows.ItemsReplaced -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced);
61      Content.Rows.CollectionReset -= new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset);
62      base.DeregisterContentEvents();
63    }
64    protected override void RegisterContentEvents() {
65      base.RegisterContentEvents();
66      Content.VisualPropertiesChanged += new EventHandler(Content_VisualPropertiesChanged);
67      Content.Rows.ItemsAdded += new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded);
68      Content.Rows.ItemsRemoved += new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved);
69      Content.Rows.ItemsReplaced += new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced);
70      Content.Rows.CollectionReset += new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset);
71    }
72
73    protected virtual void RegisterDataRowEvents(DataRow row) {
74      row.NameChanged += new EventHandler(Row_NameChanged);
75      row.VisualPropertiesChanged += new EventHandler(Row_VisualPropertiesChanged);
76      valuesRowsTable.Add(row.Values, row);
77      row.Values.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded);
78      row.Values.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved);
79      row.Values.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced);
80      row.Values.ItemsMoved += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved);
81      row.Values.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset);
82    }
83    protected virtual void DeregisterDataRowEvents(DataRow row) {
84      row.Values.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded);
85      row.Values.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved);
86      row.Values.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced);
87      row.Values.ItemsMoved -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved);
88      row.Values.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset);
89      valuesRowsTable.Remove(row.Values);
90      row.VisualPropertiesChanged -= new EventHandler(Row_VisualPropertiesChanged);
91      row.NameChanged -= new EventHandler(Row_NameChanged);
92    }
93    #endregion
94
95    protected override void OnContentChanged() {
96      base.OnContentChanged();
97      invisibleSeries.Clear();
98      //chart.Titles[0].Text = string.Empty;
99      chart.ChartAreas[0].AxisX.Title = string.Empty;
100      chart.ChartAreas[0].AxisY.Title = string.Empty;
101      chart.ChartAreas[0].AxisY2.Title = string.Empty;
102      chart.Series.Clear();
103      if (Content != null) {
104        //chart.Titles[0].Text = Content.Name;
105        AddDataRows(Content.Rows);
106        AddSelectedDataRows(Content.SelectedRows);
107        ConfigureChartArea(chart.ChartAreas[0]);
108        RecalculateAxesScale(chart.ChartAreas[0]);
109      }
110    }
111
112    protected override void SetEnabledStateOfControls() {
113      base.SetEnabledStateOfControls();
114      chart.Enabled = Content != null;
115    }
116
117    public void ShowConfiguration() {
118      if (Content != null) {
119        using (var dialog = new DataTableVisualPropertiesDialog(Content)) {
120          dialog.ShowDialog(this);
121        }
122      } else MessageBox.Show("Nothing to configure.");
123    }
124    protected virtual void AddDataRows(IEnumerable<DataRow> rows) {
125      foreach (var row in rows) {
126        RegisterDataRowEvents(row);
127        var series = new Series(row.Name);
128        if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;
129        else series.LegendText = row.Name;
130        ConfigureSeries(series, row);
131        FillSeriesWithRowValues(series, row);
132        chart.Series.Add(series);
133      }
134
135      ConfigureChartArea(chart.ChartAreas[0]);
136      RecalculateAxesScale(chart.ChartAreas[0]);
137      UpdateYCursorInterval();
138    }
139
140    protected virtual void AddSelectedDataRows(IEnumerable<DataRow> rows) {
141      foreach (var row in rows) {
142        if (row.VisualProperties.ChartType == DataRowVisualProperties.DataRowChartType.Line) {
143          //RegisterDataRowEvents(row);
144          row.VisualProperties.IsVisibleInLegend = false;
145          var series = new Series(row.Name);
146          ConfigureSeries(series, row);
147          FillSeriesWithRowValues(series, row);
148          chart.Series.Add(series);
149        }
150      }
151    }
152
153    protected virtual void RemoveDataRows(IEnumerable<DataRow> rows) {
154      foreach (var row in rows) {
155        DeregisterDataRowEvents(row);
156        Series series = chart.Series[row.Name];
157        chart.Series.Remove(series);
158        if (invisibleSeries.Contains(series))
159          invisibleSeries.Remove(series);
160      }
161      RecalculateAxesScale(chart.ChartAreas[0]);
162    }
163
164    private void ConfigureSeries(Series series, DataRow row) {
165      RemoveCustomPropertyIfExists(series, "PointWidth");
166      series.BorderWidth = 1;
167      series.BorderDashStyle = ChartDashStyle.Solid;
168      series.BorderColor = Color.Empty;
169
170      if (row.VisualProperties.Color != Color.Empty)
171        series.Color = row.VisualProperties.Color;
172      else series.Color = Color.Empty;
173      series.IsVisibleInLegend = row.VisualProperties.IsVisibleInLegend;
174
175      switch (row.VisualProperties.ChartType) {
176        case DataRowVisualProperties.DataRowChartType.Line:
177          series.ChartType = SeriesChartType.FastLine;
178          series.BorderWidth = row.VisualProperties.LineWidth;
179          series.BorderDashStyle = ConvertLineStyle(row.VisualProperties.LineStyle);
180          break;
181        case DataRowVisualProperties.DataRowChartType.Bars:
182          // Bar is incompatible with anything but Bar and StackedBar*
183          if (!chart.Series.Any(x => x.ChartType != SeriesChartType.Bar && x.ChartType != SeriesChartType.StackedBar && x.ChartType != SeriesChartType.StackedBar100))
184            series.ChartType = SeriesChartType.Bar;
185          else {
186            series.ChartType = SeriesChartType.FastPoint; //default
187            row.VisualProperties.ChartType = DataRowVisualProperties.DataRowChartType.Points;
188          }
189          break;
190        case DataRowVisualProperties.DataRowChartType.Columns:
191          series.ChartType = SeriesChartType.Column;
192          break;
193        case DataRowVisualProperties.DataRowChartType.Points:
194          series.ChartType = SeriesChartType.FastPoint;
195          break;
196        case DataRowVisualProperties.DataRowChartType.Histogram:
197          series.ChartType = SeriesChartType.Column;
198          series.SetCustomProperty("PointWidth", "1");
199          if (!series.Color.IsEmpty && series.Color.GetBrightness() < 0.25)
200            series.BorderColor = Color.White;
201          else series.BorderColor = Color.Black;
202          break;
203        case DataRowVisualProperties.DataRowChartType.StepLine:
204          series.ChartType = SeriesChartType.StepLine;
205          series.BorderWidth = row.VisualProperties.LineWidth;
206          series.BorderDashStyle = ConvertLineStyle(row.VisualProperties.LineStyle);
207          break;
208        default:
209          series.ChartType = SeriesChartType.FastPoint;
210          break;
211      }
212      series.YAxisType = row.VisualProperties.SecondYAxis ? AxisType.Secondary : AxisType.Primary;
213      series.XAxisType = row.VisualProperties.SecondXAxis ? AxisType.Secondary : AxisType.Primary;
214      if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;
215      else series.LegendText = row.Name;
216
217      string xAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.XAxisTitle)
218                      ? "X"
219                      : Content.VisualProperties.XAxisTitle;
220      string yAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.YAxisTitle)
221                            ? "Y"
222                            : Content.VisualProperties.YAxisTitle;
223      series.ToolTip =
224        series.LegendText + Environment.NewLine +
225        xAxisTitle + " = " + "#INDEX," + Environment.NewLine +
226        yAxisTitle + " = " + "#VAL";
227    }
228
229    private void ConfigureChartArea(ChartArea area) {
230      if (Content.VisualProperties.TitleFont != null) chart.Titles[0].Font = Content.VisualProperties.TitleFont;
231      if (!Content.VisualProperties.TitleColor.IsEmpty) chart.Titles[0].ForeColor = Content.VisualProperties.TitleColor;
232      chart.Titles[0].Text = Content.VisualProperties.Title;
233
234      if (Content.VisualProperties.AxisTitleFont != null) area.AxisX.TitleFont = Content.VisualProperties.AxisTitleFont;
235      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisX.TitleForeColor = Content.VisualProperties.AxisTitleColor;
236      area.AxisX.Title = Content.VisualProperties.XAxisTitle;
237
238      if (Content.VisualProperties.AxisTitleFont != null) area.AxisX2.TitleFont = Content.VisualProperties.AxisTitleFont;
239      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisX2.TitleForeColor = Content.VisualProperties.AxisTitleColor;
240      area.AxisX2.Title = Content.VisualProperties.SecondXAxisTitle;
241
242      if (Content.VisualProperties.AxisTitleFont != null) area.AxisY.TitleFont = Content.VisualProperties.AxisTitleFont;
243      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisY.TitleForeColor = Content.VisualProperties.AxisTitleColor;
244      area.AxisY.Title = Content.VisualProperties.YAxisTitle;
245
246      if (Content.VisualProperties.AxisTitleFont != null) area.AxisY2.TitleFont = Content.VisualProperties.AxisTitleFont;
247      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisY2.TitleForeColor = Content.VisualProperties.AxisTitleColor;
248      area.AxisY2.Title = Content.VisualProperties.SecondYAxisTitle;
249
250      area.AxisX.IsLogarithmic = Content.VisualProperties.XAxisLogScale;
251      area.AxisX2.IsLogarithmic = Content.VisualProperties.SecondXAxisLogScale;
252      area.AxisY.IsLogarithmic = Content.VisualProperties.YAxisLogScale;
253      area.AxisY2.IsLogarithmic = Content.VisualProperties.SecondYAxisLogScale;
254    }
255
256    private void RecalculateAxesScale(ChartArea area) {
257      // Reset the axes bounds so that RecalculateAxesScale() will assign new bounds
258      foreach (Axis a in area.Axes) {
259        a.Minimum = double.NaN;
260        a.Maximum = double.NaN;
261      }
262      area.RecalculateAxesScale();
263      area.AxisX.IsMarginVisible = false;
264      area.AxisX2.IsMarginVisible = false;
265
266      if (!Content.VisualProperties.XAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.XAxisMinimumFixedValue)) area.AxisX.Minimum = Content.VisualProperties.XAxisMinimumFixedValue;
267      if (!Content.VisualProperties.XAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.XAxisMaximumFixedValue)) area.AxisX.Maximum = Content.VisualProperties.XAxisMaximumFixedValue;
268      if (!Content.VisualProperties.SecondXAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.SecondXAxisMinimumFixedValue)) area.AxisX2.Minimum = Content.VisualProperties.SecondXAxisMinimumFixedValue;
269      if (!Content.VisualProperties.SecondXAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.SecondXAxisMaximumFixedValue)) area.AxisX2.Maximum = Content.VisualProperties.SecondXAxisMaximumFixedValue;
270      if (!Content.VisualProperties.YAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.YAxisMinimumFixedValue)) area.AxisY.Minimum = Content.VisualProperties.YAxisMinimumFixedValue;
271      if (!Content.VisualProperties.YAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.YAxisMaximumFixedValue)) area.AxisY.Maximum = Content.VisualProperties.YAxisMaximumFixedValue;
272      if (!Content.VisualProperties.SecondYAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.SecondYAxisMinimumFixedValue)) area.AxisY2.Minimum = Content.VisualProperties.SecondYAxisMinimumFixedValue;
273      if (!Content.VisualProperties.SecondYAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.SecondYAxisMaximumFixedValue)) area.AxisY2.Maximum = Content.VisualProperties.SecondYAxisMaximumFixedValue;
274      if (area.AxisX.Minimum >= area.AxisX.Maximum) area.AxisX.Maximum = area.AxisX.Minimum + 1;
275      if (area.AxisX2.Minimum >= area.AxisX2.Maximum) area.AxisX2.Maximum = area.AxisX2.Minimum + 1;
276      if (area.AxisY.Minimum >= area.AxisY.Maximum) area.AxisY.Maximum = area.AxisY.Minimum + 1;
277      if (area.AxisY2.Minimum >= area.AxisY2.Maximum) area.AxisY2.Maximum = area.AxisY2.Minimum + 1;
278    }
279
280    protected virtual void UpdateYCursorInterval() {
281      double interestingValuesRange = (
282        from series in chart.Series
283        where series.Enabled
284        let values = (from point in series.Points
285                      where !point.IsEmpty
286                      select point.YValues[0]).DefaultIfEmpty(1.0)
287        let range = values.Max() - values.Min()
288        where range > 0.0
289        select range
290        ).DefaultIfEmpty(1.0).Min();
291
292      double digits = (int)Math.Log10(interestingValuesRange) - 3;
293      double yZoomInterval = Math.Pow(10, digits);
294      this.chart.ChartAreas[0].CursorY.Interval = yZoomInterval;
295    }
296
297    #region Event Handlers
298    #region Content Event Handlers
299    private void Content_VisualPropertiesChanged(object sender, EventArgs e) {
300      if (InvokeRequired)
301        Invoke(new EventHandler(Content_VisualPropertiesChanged), sender, e);
302      else {
303        ConfigureChartArea(chart.ChartAreas[0]);
304        RecalculateAxesScale(chart.ChartAreas[0]); // axes min/max could have changed
305      }
306    }
307    #endregion
308    #region Rows Event Handlers
309    private void Rows_ItemsAdded(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
310      if (InvokeRequired)
311        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded), sender, e);
312      else {
313        AddDataRows(e.Items);
314      }
315    }
316    private void Rows_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
317      if (InvokeRequired)
318        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved), sender, e);
319      else {
320        RemoveDataRows(e.Items);
321      }
322    }
323    private void Rows_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
324      if (InvokeRequired)
325        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced), sender, e);
326      else {
327        RemoveDataRows(e.OldItems);
328        AddDataRows(e.Items);
329      }
330    }
331    private void Rows_CollectionReset(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
332      if (InvokeRequired)
333        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset), sender, e);
334      else {
335        RemoveDataRows(e.OldItems);
336        AddDataRows(e.Items);
337      }
338    }
339    #endregion
340    #region Row Event Handlers
341    private void Row_VisualPropertiesChanged(object sender, EventArgs e) {
342      if (InvokeRequired)
343        Invoke(new EventHandler(Row_VisualPropertiesChanged), sender, e);
344      else {
345        DataRow row = (DataRow)sender;
346        Series series = chart.Series[row.Name];
347        series.Points.Clear();
348        ConfigureSeries(series, row);
349        FillSeriesWithRowValues(series, row);
350        RecalculateAxesScale(chart.ChartAreas[0]);
351      }
352    }
353    private void Row_NameChanged(object sender, EventArgs e) {
354      if (InvokeRequired)
355        Invoke(new EventHandler(Row_NameChanged), sender, e);
356      else {
357        DataRow row = (DataRow)sender;
358        chart.Series[row.Name].Name = row.Name;
359      }
360    }
361    #endregion
362    #region Values Event Handlers
363    private void Values_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
364      if (InvokeRequired)
365        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded), sender, e);
366      else {
367        DataRow row = null;
368        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
369        if (row != null) {
370          Series rowSeries = chart.Series[row.Name];
371          if (!invisibleSeries.Contains(rowSeries)) {
372            rowSeries.Points.Clear();
373            FillSeriesWithRowValues(rowSeries, row);
374            RecalculateAxesScale(chart.ChartAreas[0]);
375            UpdateYCursorInterval();
376          }
377        }
378      }
379    }
380    private void Values_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
381      if (InvokeRequired)
382        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved), sender, e);
383      else {
384        DataRow row = null;
385        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
386        if (row != null) {
387          Series rowSeries = chart.Series[row.Name];
388          if (!invisibleSeries.Contains(rowSeries)) {
389            rowSeries.Points.Clear();
390            FillSeriesWithRowValues(rowSeries, row);
391            RecalculateAxesScale(chart.ChartAreas[0]);
392            UpdateYCursorInterval();
393          }
394        }
395      }
396    }
397    private void Values_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
398      if (InvokeRequired)
399        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced), sender, e);
400      else {
401        DataRow row = null;
402        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
403        if (row != null) {
404          Series rowSeries = chart.Series[row.Name];
405          if (!invisibleSeries.Contains(rowSeries)) {
406            if (row.VisualProperties.ChartType == DataRowVisualProperties.DataRowChartType.Histogram) {
407              rowSeries.Points.Clear();
408              FillSeriesWithRowValues(rowSeries, row);
409            } else {
410              foreach (IndexedItem<double> item in e.Items) {
411                if (IsInvalidValue(item.Value))
412                  rowSeries.Points[item.Index].IsEmpty = true;
413                else {
414                  rowSeries.Points[item.Index].YValues = new double[] { item.Value };
415                  rowSeries.Points[item.Index].IsEmpty = false;
416                }
417              }
418            }
419            RecalculateAxesScale(chart.ChartAreas[0]);
420            UpdateYCursorInterval();
421          }
422        }
423      }
424    }
425    private void Values_ItemsMoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
426      if (InvokeRequired)
427        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved), sender, e);
428      else {
429        DataRow row = null;
430        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
431        if (row != null) {
432          Series rowSeries = chart.Series[row.Name];
433          if (!invisibleSeries.Contains(rowSeries)) {
434            rowSeries.Points.Clear();
435            FillSeriesWithRowValues(rowSeries, row);
436            RecalculateAxesScale(chart.ChartAreas[0]);
437            UpdateYCursorInterval();
438          }
439        }
440      }
441    }
442
443    private void Values_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
444      if (InvokeRequired)
445        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset), sender, e);
446      else {
447        DataRow row = null;
448        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
449        if (row != null) {
450          Series rowSeries = chart.Series[row.Name];
451          if (!invisibleSeries.Contains(rowSeries)) {
452            rowSeries.Points.Clear();
453            FillSeriesWithRowValues(rowSeries, row);
454            RecalculateAxesScale(chart.ChartAreas[0]);
455            UpdateYCursorInterval();
456          }
457        }
458      }
459    }
460    #endregion
461    #endregion
462
463    #region Chart Event Handlers
464    private void chart_MouseDown(object sender, MouseEventArgs e) {
465      HitTestResult result = chart.HitTest(e.X, e.Y);
466      if (result.ChartElementType == ChartElementType.LegendItem) {
467        ToggleSeriesVisible(result.Series);
468      }
469    }
470    private void chart_MouseMove(object sender, MouseEventArgs e) {
471      HitTestResult result = chart.HitTest(e.X, e.Y);
472      if (result.ChartElementType == ChartElementType.LegendItem)
473        this.Cursor = Cursors.Hand;
474      else
475        this.Cursor = Cursors.Default;
476    }
477    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
478      foreach (LegendItem legendItem in e.LegendItems) {
479        var series = chart.Series[legendItem.SeriesName];
480        if (series != null) {
481          bool seriesIsInvisible = invisibleSeries.Contains(series);
482          foreach (LegendCell cell in legendItem.Cells) {
483            cell.ForeColor = seriesIsInvisible ? Color.Gray : Color.Black;
484          }
485        }
486      }
487    }
488    #endregion
489
490    private void ToggleSeriesVisible(Series series) {
491      if (!invisibleSeries.Contains(series)) {
492        series.Points.Clear();
493        invisibleSeries.Add(series);
494      } else {
495        invisibleSeries.Remove(series);
496        if (Content != null) {
497
498          var row = (from r in Content.Rows
499                     where r.Name == series.Name
500                     select r).Single();
501          FillSeriesWithRowValues(series, row);
502          this.chart.Legends[series.Legend].ForeColor = Color.Black;
503          RecalculateAxesScale(chart.ChartAreas[0]);
504          UpdateYCursorInterval();
505        }
506      }
507    }
508
509    private void FillSeriesWithRowValues(Series series, DataRow row) {
510      switch (row.VisualProperties.ChartType) {
511        case DataRowVisualProperties.DataRowChartType.Histogram:
512          CalculateHistogram(series, row);
513          break;
514        default: {
515            bool yLogarithmic = series.YAxisType == AxisType.Primary
516                                  ? Content.VisualProperties.YAxisLogScale
517                                  : Content.VisualProperties.SecondYAxisLogScale;
518            bool xLogarithmic = series.XAxisType == AxisType.Primary
519                                  ? Content.VisualProperties.XAxisLogScale
520                                  : Content.VisualProperties.SecondXAxisLogScale;
521            for (int i = 0; i < row.Values.Count; i++) {
522              var value = row.Values[i];
523              var point = new DataPoint();
524              point.XValue = row.VisualProperties.StartIndexZero && !xLogarithmic ? i : i + 1;
525              if (IsInvalidValue(value) || (yLogarithmic && value <= 0))
526                point.IsEmpty = true;
527              else
528                point.YValues = new double[] { value };
529              series.Points.Add(point);
530            }
531          }
532          break;
533      }
534    }
535
536    // get minimum ignores nan values
537    private double GetMinimum(IEnumerable<double> values) {
538      double min = Double.MaxValue;
539
540      foreach (double value in values) {
541        if (!Double.IsNaN(value) && value < min)
542          min = value;
543      }
544      return min;
545    }
546
547    //get maximium ignores nan values
548    private double GetMaximum(IEnumerable<double> values) {
549      double max = Double.MinValue;
550
551      foreach (double value in values) {
552        if (!Double.IsNaN(value) && value > max)
553          max = value;
554      }
555      return max;
556    }
557
558    protected virtual void CalculateHistogram(Series series, DataRow row) {
559      series.Points.Clear();
560      if (!row.Values.Any()) return;
561      int bins = row.VisualProperties.Bins;
562
563      double minValue = GetMinimum(row.Values);
564      double maxValue = GetMaximum(row.Values);
565      double intervalWidth = (maxValue - minValue) / bins;
566      if (intervalWidth < 0) return;
567      if (intervalWidth == 0) {
568        series.Points.AddXY(minValue, row.Values.Count);
569        return;
570      }
571
572      if (!row.VisualProperties.ExactBins) {
573        intervalWidth = HumanRoundRange(intervalWidth);
574        minValue = Math.Floor(minValue / intervalWidth) * intervalWidth;
575        maxValue = Math.Ceiling(maxValue / intervalWidth) * intervalWidth;
576      }
577
578      double intervalCenter = intervalWidth / 2;
579
580      double min = 0.0, max = 0.0;
581      if (!Double.IsNaN(Content.VisualProperties.XAxisMinimumFixedValue) && !Content.VisualProperties.XAxisMinimumAuto)
582        min = Content.VisualProperties.XAxisMinimumFixedValue;
583      else min = minValue;
584      if (!Double.IsNaN(Content.VisualProperties.XAxisMaximumFixedValue) && !Content.VisualProperties.XAxisMaximumAuto)
585        max = Content.VisualProperties.XAxisMaximumFixedValue;
586      else max = maxValue + intervalWidth;
587
588      double axisInterval = intervalWidth / row.VisualProperties.ScaleFactor;
589
590      var area = chart.ChartAreas[0];
591      area.AxisX.Interval = axisInterval;
592
593      series.SetCustomProperty("PointWidth", "1"); // 0.8 is the default value
594
595      // get the range or intervals which define the grouping of the frequency values
596      var doubleRange = DoubleRange(min, max, intervalWidth).Skip(1).ToList();
597
598      // aggregate the row values by unique key and frequency value
599      var valueFrequencies = (from v in row.Values
600                              where !IsInvalidValue(v)
601                              orderby v
602                              group v by v into g
603                              select new Tuple<double, double>(g.First(), g.Count())).ToList();
604
605      // shift the chart to the left so the bars are placed on the intervals
606      if (valueFrequencies.First().Item1 < doubleRange.First())
607        series.Points.Add(new DataPoint(min - intervalWidth, 0));
608
609      // add data points
610      int j = 0;
611      foreach (var d in doubleRange) {
612        double sum = 0.0;
613        // sum the frequency values that fall within the same interval
614        while (j < valueFrequencies.Count && valueFrequencies[j].Item1 < d) {
615          sum += valueFrequencies[j].Item2;
616          ++j;
617        }
618        string xAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.XAxisTitle)
619                              ? "X"
620                              : Content.VisualProperties.XAxisTitle;
621        string yAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.YAxisTitle)
622                              ? "Y"
623                              : Content.VisualProperties.YAxisTitle;
624        series.Points.Add(new DataPoint(d - intervalCenter, sum) {
625          ToolTip =
626            xAxisTitle + ": [" + (d - intervalWidth) + "-" + d + ")" + Environment.NewLine +
627            yAxisTitle + ": " + sum
628        });
629      }
630    }
631
632    #region Helpers
633    public static IEnumerable<double> DoubleRange(double min, double max, double step) {
634      double i;
635      for (i = min; i <= max; i += step)
636        yield return i;
637
638      if (i != max + step)
639        yield return i;
640    }
641
642    protected void RemoveCustomPropertyIfExists(Series series, string property) {
643      if (series.IsCustomPropertySet(property)) series.DeleteCustomProperty(property);
644    }
645
646    private double HumanRoundRange(double range) {
647      double base10 = Math.Pow(10.0, Math.Floor(Math.Log10(range)));
648      double rounding = range / base10;
649      if (rounding <= 1.5) rounding = 1;
650      else if (rounding <= 2.25) rounding = 2;
651      else if (rounding <= 3.75) rounding = 2.5;
652      else if (rounding <= 7.5) rounding = 5;
653      else rounding = 10;
654      return rounding * base10;
655    }
656
657    private double HumanRoundMax(double max) {
658      double base10;
659      if (max > 0) base10 = Math.Pow(10.0, Math.Floor(Math.Log10(max)));
660      else base10 = Math.Pow(10.0, Math.Ceiling(Math.Log10(-max)));
661      double rounding = (max > 0) ? base10 : -base10;
662      while (rounding < max) rounding += base10;
663      return rounding;
664    }
665
666    private ChartDashStyle ConvertLineStyle(DataRowVisualProperties.DataRowLineStyle dataRowLineStyle) {
667      switch (dataRowLineStyle) {
668        case DataRowVisualProperties.DataRowLineStyle.Dash:
669          return ChartDashStyle.Dash;
670        case DataRowVisualProperties.DataRowLineStyle.DashDot:
671          return ChartDashStyle.DashDot;
672        case DataRowVisualProperties.DataRowLineStyle.DashDotDot:
673          return ChartDashStyle.DashDotDot;
674        case DataRowVisualProperties.DataRowLineStyle.Dot:
675          return ChartDashStyle.Dot;
676        case DataRowVisualProperties.DataRowLineStyle.NotSet:
677          return ChartDashStyle.NotSet;
678        case DataRowVisualProperties.DataRowLineStyle.Solid:
679          return ChartDashStyle.Solid;
680        default:
681          return ChartDashStyle.NotSet;
682      }
683    }
684
685    protected static bool IsInvalidValue(double x) {
686      return double.IsNaN(x) || x < (double)decimal.MinValue || x > (double)decimal.MaxValue;
687    }
688    #endregion
689  }
690}
Note: See TracBrowser for help on using the repository browser.