Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 10867 was 10867, checked in by mleitner, 9 years ago

Add colors to histogram / remove all in one mode

File size: 36.1 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{
35  [View("Preprocessing DataTable View")]
36  [Content(typeof(PreprocessingDataTable), false)]
37  public partial class PreprocessingDataTableView : ItemView, IConfigureableView
38  {
39    protected List<Series> invisibleSeries;
40    protected Dictionary<IObservableList<double>, DataRow> valuesRowsTable;
41
42    public new PreprocessingDataTable Content
43    {
44      get { return (PreprocessingDataTable)base.Content; }
45      set { base.Content = value; }
46    }
47
48    public List<double> classification { get; set; }
49
50    public PreprocessingDataTableView()
51    {
52      InitializeComponent();
53      valuesRowsTable = new Dictionary<IObservableList<double>, DataRow>();
54      invisibleSeries = new List<Series>();
55      chart.CustomizeAllChartAreas();
56      chart.ChartAreas[0].CursorX.Interval = 1;
57    }
58
59    #region Event Handler Registration
60    protected override void DeregisterContentEvents()
61    {
62      foreach (DataRow row in Content.Rows)
63        DeregisterDataRowEvents(row);
64      Content.VisualPropertiesChanged -= new EventHandler(Content_VisualPropertiesChanged);
65      Content.Rows.ItemsAdded -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded);
66      Content.Rows.ItemsRemoved -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved);
67      Content.Rows.ItemsReplaced -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced);
68      Content.Rows.CollectionReset -= new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset);
69
70      Content.SelectedRows.ItemsAdded -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded);
71      Content.SelectedRows.ItemsRemoved -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved);
72      Content.SelectedRows.ItemsReplaced -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced);
73      Content.SelectedRows.CollectionReset -= new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset);
74      base.DeregisterContentEvents();
75    }
76    protected override void RegisterContentEvents()
77    {
78      base.RegisterContentEvents();
79      Content.VisualPropertiesChanged += new EventHandler(Content_VisualPropertiesChanged);
80      Content.Rows.ItemsAdded += new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded);
81      Content.Rows.ItemsRemoved += new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved);
82      Content.Rows.ItemsReplaced += new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced);
83      Content.Rows.CollectionReset += new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset);
84
85      Content.SelectedRows.ItemsAdded += new CollectionItemsChangedEventHandler<DataRow>(SelectedRows_ItemsAdded);
86      Content.SelectedRows.ItemsRemoved += new CollectionItemsChangedEventHandler<DataRow>(SelectedRows_ItemsRemoved);
87      Content.SelectedRows.ItemsReplaced += new CollectionItemsChangedEventHandler<DataRow>(SelectedRows_ItemsReplaced);
88      Content.SelectedRows.CollectionReset += new CollectionItemsChangedEventHandler<DataRow>(SelectedRows_CollectionReset);
89    }
90
91
92    protected virtual void RegisterDataRowEvents(DataRow row) {
93      row.NameChanged += new EventHandler(Row_NameChanged);
94      row.VisualPropertiesChanged += new EventHandler(Row_VisualPropertiesChanged);
95      valuesRowsTable.Add(row.Values, row);
96      row.Values.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded);
97      row.Values.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved);
98      row.Values.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced);
99      row.Values.ItemsMoved += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved);
100      row.Values.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset);
101    }
102    protected virtual void DeregisterDataRowEvents(DataRow row)
103    {
104      row.Values.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded);
105      row.Values.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved);
106      row.Values.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced);
107      row.Values.ItemsMoved -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved);
108      row.Values.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset);
109      valuesRowsTable.Remove(row.Values);
110      row.VisualPropertiesChanged -= new EventHandler(Row_VisualPropertiesChanged);
111      row.NameChanged -= new EventHandler(Row_NameChanged);
112    }
113    #endregion
114
115    protected override void OnContentChanged()
116    {
117      base.OnContentChanged();
118      invisibleSeries.Clear();
119      chart.ChartAreas[0].AxisX.Title = string.Empty;
120      chart.ChartAreas[0].AxisY.Title = string.Empty;
121      chart.ChartAreas[0].AxisY2.Title = string.Empty;
122      chart.Series.Clear();
123      if (Content != null)
124      {
125
126        if (classification != null)
127        {
128          chart.Titles[0].Text = Content.Name;
129        }
130
131        AddDataRows(Content.Rows);
132        AddSelectedDataRows(Content.SelectedRows);
133        ConfigureChartArea(chart.ChartAreas[0]);
134        RecalculateAxesScale(chart.ChartAreas[0]);
135      }
136    }
137
138    protected override void SetEnabledStateOfControls()
139    {
140      base.SetEnabledStateOfControls();
141      chart.Enabled = Content != null;
142    }
143
144    public void ShowConfiguration()
145    {
146      if (Content != null)
147      {
148        using (var dialog = new DataTableVisualPropertiesDialog(Content))
149        {
150          dialog.ShowDialog(this);
151        }
152      }
153      else MessageBox.Show("Nothing to configure.");
154    }
155    protected virtual void AddDataRows(IEnumerable<DataRow> rows)
156    {
157      foreach (var row in rows)
158      {
159        RegisterDataRowEvents(row);
160        var series = new Series(row.Name);
161        if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;
162        else series.LegendText = row.Name;
163        ConfigureSeries(series, row);
164        FillSeriesWithRowValues(series, row);
165
166        if (classification == null)
167        {
168          chart.Series.Add(series);
169        }
170      }
171
172      ConfigureChartArea(chart.ChartAreas[0]);
173      RecalculateAxesScale(chart.ChartAreas[0]);
174      UpdateYCursorInterval();
175    }
176
177    protected virtual void AddSelectedDataRows(IEnumerable<DataRow> rows) {
178
179      // add dummy series for selction entry in legend
180      if (rows.Count() > 0 && chart.Series.FindByName("(Selection)") == null)
181      {
182        Series series = new Series("(Selection)");
183        series.IsVisibleInLegend = true;
184        series.Color = Color.Green;
185        series.BorderWidth = 1;
186        series.BorderDashStyle = ChartDashStyle.Solid;
187        series.BorderColor = Color.Empty;
188        series.ChartType = SeriesChartType.FastLine;
189        chart.Series.Add(series);
190
191      }
192
193      foreach (var row in rows) {
194        if (row.VisualProperties.ChartType == DataRowVisualProperties.DataRowChartType.Line) {
195          row.VisualProperties.IsVisibleInLegend = false;
196          row.VisualProperties.Color = Color.Green;
197          //add selected to name in order to avoid naming conflict
198          var series = new Series(row.Name+"(Selected)");
199          ConfigureSeries(series, row);
200          FillSeriesWithRowValues(series, row);
201
202          if (classification == null)
203          {
204            chart.Series.Add(series);
205          }
206
207        }
208      }
209    }
210
211    protected virtual void RemoveSelectedDataRows(IEnumerable<DataRow> rows) {
212
213      //remove selection entry in legend
214      if (Content.SelectedRows.Count == 0) {
215        Series series = chart.Series["(Selection)"];
216        chart.Series.Remove(series);
217      }
218
219      foreach (var row in rows) {
220        Series series = chart.Series[row.Name + "(Selected)"];
221        chart.Series.Remove(series);
222      }
223    }
224
225    protected virtual void RemoveDataRows(IEnumerable<DataRow> rows) {
226      foreach (var row in rows) {
227        DeregisterDataRowEvents(row);
228        Series series = chart.Series[row.Name];
229        chart.Series.Remove(series);
230        if (invisibleSeries.Contains(series))
231          invisibleSeries.Remove(series);
232      }
233      RecalculateAxesScale(chart.ChartAreas[0]);
234    }
235
236    private void ConfigureSeries(Series series, DataRow row)
237    {
238      RemoveCustomPropertyIfExists(series, "PointWidth");
239      series.BorderWidth = 1;
240      series.BorderDashStyle = ChartDashStyle.Solid;
241      series.BorderColor = Color.Empty;
242
243      if (row.VisualProperties.Color != Color.Empty)
244        series.Color = row.VisualProperties.Color;
245      else series.Color = Color.Empty;
246      series.IsVisibleInLegend = row.VisualProperties.IsVisibleInLegend;
247
248      switch (row.VisualProperties.ChartType) {
249        case DataRowVisualProperties.DataRowChartType.Line:
250          series.ChartType = SeriesChartType.FastLine;
251          series.BorderWidth = row.VisualProperties.LineWidth;
252          series.BorderDashStyle = ConvertLineStyle(row.VisualProperties.LineStyle);
253          break;
254        case DataRowVisualProperties.DataRowChartType.Bars:
255          // Bar is incompatible with anything but Bar and StackedBar*
256          if (!chart.Series.Any(x => x.ChartType != SeriesChartType.Bar && x.ChartType != SeriesChartType.StackedBar && x.ChartType != SeriesChartType.StackedBar100))
257            series.ChartType = SeriesChartType.Bar;
258          else
259          {
260            series.ChartType = SeriesChartType.FastPoint; //default
261            row.VisualProperties.ChartType = DataRowVisualProperties.DataRowChartType.Points;
262          }
263          break;
264        case DataRowVisualProperties.DataRowChartType.Columns:
265          series.ChartType = SeriesChartType.Column;
266          break;
267        case DataRowVisualProperties.DataRowChartType.Points:
268          series.ChartType = SeriesChartType.FastPoint;
269          break;
270        case DataRowVisualProperties.DataRowChartType.Histogram:
271          series.ChartType = SeriesChartType.StackedColumn;
272          series.SetCustomProperty("PointWidth", "1");
273          if (!series.Color.IsEmpty && series.Color.GetBrightness() < 0.25)
274            series.BorderColor = Color.White;
275          else series.BorderColor = Color.Black;
276          break;
277        case DataRowVisualProperties.DataRowChartType.StepLine:
278          series.ChartType = SeriesChartType.StepLine;
279          series.BorderWidth = row.VisualProperties.LineWidth;
280          series.BorderDashStyle = ConvertLineStyle(row.VisualProperties.LineStyle);
281          break;
282        default:
283          series.ChartType = SeriesChartType.FastPoint;
284          break;
285      }
286      series.YAxisType = row.VisualProperties.SecondYAxis ? AxisType.Secondary : AxisType.Primary;
287      series.XAxisType = row.VisualProperties.SecondXAxis ? AxisType.Secondary : AxisType.Primary;
288      if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;
289      else series.LegendText = row.Name;
290
291      string xAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.XAxisTitle)
292                      ? "X"
293                      : Content.VisualProperties.XAxisTitle;
294      string yAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.YAxisTitle)
295                            ? "Y"
296                            : Content.VisualProperties.YAxisTitle;
297      series.ToolTip =
298        series.LegendText + Environment.NewLine +
299        xAxisTitle + " = " + "#INDEX," + Environment.NewLine +
300        yAxisTitle + " = " + "#VAL";
301    }
302
303    private void ConfigureChartArea(ChartArea area)
304    {
305      if (Content.VisualProperties.TitleFont != null) chart.Titles[0].Font = Content.VisualProperties.TitleFont;
306      if (!Content.VisualProperties.TitleColor.IsEmpty) chart.Titles[0].ForeColor = Content.VisualProperties.TitleColor;
307      chart.Titles[0].Text = Content.VisualProperties.Title;
308
309      if (Content.VisualProperties.AxisTitleFont != null) area.AxisX.TitleFont = Content.VisualProperties.AxisTitleFont;
310      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisX.TitleForeColor = Content.VisualProperties.AxisTitleColor;
311      area.AxisX.Title = Content.VisualProperties.XAxisTitle;
312
313      if (Content.VisualProperties.AxisTitleFont != null) area.AxisX2.TitleFont = Content.VisualProperties.AxisTitleFont;
314      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisX2.TitleForeColor = Content.VisualProperties.AxisTitleColor;
315      area.AxisX2.Title = Content.VisualProperties.SecondXAxisTitle;
316
317      if (Content.VisualProperties.AxisTitleFont != null) area.AxisY.TitleFont = Content.VisualProperties.AxisTitleFont;
318      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisY.TitleForeColor = Content.VisualProperties.AxisTitleColor;
319      area.AxisY.Title = Content.VisualProperties.YAxisTitle;
320
321      if (Content.VisualProperties.AxisTitleFont != null) area.AxisY2.TitleFont = Content.VisualProperties.AxisTitleFont;
322      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisY2.TitleForeColor = Content.VisualProperties.AxisTitleColor;
323      area.AxisY2.Title = Content.VisualProperties.SecondYAxisTitle;
324
325      area.AxisX.IsLogarithmic = Content.VisualProperties.XAxisLogScale;
326      area.AxisX2.IsLogarithmic = Content.VisualProperties.SecondXAxisLogScale;
327      area.AxisY.IsLogarithmic = Content.VisualProperties.YAxisLogScale;
328      area.AxisY2.IsLogarithmic = Content.VisualProperties.SecondYAxisLogScale;
329    }
330
331    private void RecalculateAxesScale(ChartArea area)
332    {
333      // Reset the axes bounds so that RecalculateAxesScale() will assign new bounds
334      foreach (Axis a in area.Axes)
335      {
336        a.Minimum = double.NaN;
337        a.Maximum = double.NaN;
338      }
339      area.RecalculateAxesScale();
340      area.AxisX.IsMarginVisible = false;
341      area.AxisX2.IsMarginVisible = false;
342
343      if (!Content.VisualProperties.XAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.XAxisMinimumFixedValue)) area.AxisX.Minimum = Content.VisualProperties.XAxisMinimumFixedValue;
344      if (!Content.VisualProperties.XAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.XAxisMaximumFixedValue)) area.AxisX.Maximum = Content.VisualProperties.XAxisMaximumFixedValue;
345      if (!Content.VisualProperties.SecondXAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.SecondXAxisMinimumFixedValue)) area.AxisX2.Minimum = Content.VisualProperties.SecondXAxisMinimumFixedValue;
346      if (!Content.VisualProperties.SecondXAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.SecondXAxisMaximumFixedValue)) area.AxisX2.Maximum = Content.VisualProperties.SecondXAxisMaximumFixedValue;
347      if (!Content.VisualProperties.YAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.YAxisMinimumFixedValue)) area.AxisY.Minimum = Content.VisualProperties.YAxisMinimumFixedValue;
348      if (!Content.VisualProperties.YAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.YAxisMaximumFixedValue)) area.AxisY.Maximum = Content.VisualProperties.YAxisMaximumFixedValue;
349      if (!Content.VisualProperties.SecondYAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.SecondYAxisMinimumFixedValue)) area.AxisY2.Minimum = Content.VisualProperties.SecondYAxisMinimumFixedValue;
350      if (!Content.VisualProperties.SecondYAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.SecondYAxisMaximumFixedValue)) area.AxisY2.Maximum = Content.VisualProperties.SecondYAxisMaximumFixedValue;
351      if (area.AxisX.Minimum >= area.AxisX.Maximum) area.AxisX.Maximum = area.AxisX.Minimum + 1;
352      if (area.AxisX2.Minimum >= area.AxisX2.Maximum) area.AxisX2.Maximum = area.AxisX2.Minimum + 1;
353      if (area.AxisY.Minimum >= area.AxisY.Maximum) area.AxisY.Maximum = area.AxisY.Minimum + 1;
354      if (area.AxisY2.Minimum >= area.AxisY2.Maximum) area.AxisY2.Maximum = area.AxisY2.Minimum + 1;
355    }
356
357    protected virtual void UpdateYCursorInterval()
358    {
359      double interestingValuesRange = (
360        from series in chart.Series
361        where series.Enabled
362        let values = (from point in series.Points
363                      where !point.IsEmpty
364                      select point.YValues[0]).DefaultIfEmpty(1.0)
365        let range = values.Max() - values.Min()
366        where range > 0.0
367        select range
368        ).DefaultIfEmpty(1.0).Min();
369
370      double digits = (int)Math.Log10(interestingValuesRange) - 3;
371      double yZoomInterval = Math.Pow(10, digits);
372      this.chart.ChartAreas[0].CursorY.Interval = yZoomInterval;
373    }
374
375    #region Event Handlers
376    #region Content Event Handlers
377    private void Content_VisualPropertiesChanged(object sender, EventArgs e)
378    {
379      if (InvokeRequired)
380        Invoke(new EventHandler(Content_VisualPropertiesChanged), sender, e);
381      else
382      {
383        ConfigureChartArea(chart.ChartAreas[0]);
384        RecalculateAxesScale(chart.ChartAreas[0]); // axes min/max could have changed
385      }
386    }
387    #endregion
388    #region Rows Event Handlers
389
390    private void SelectedRows_CollectionReset(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
391      if (InvokeRequired)
392        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced), sender, e);
393      else {
394        RemoveSelectedDataRows(e.OldItems);
395        AddSelectedDataRows(e.Items);
396      }
397    }
398
399    private void SelectedRows_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
400      if (InvokeRequired)
401        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced), sender, e);
402      else {
403        RemoveSelectedDataRows(e.OldItems);
404        AddSelectedDataRows(e.Items);
405      }
406    }
407
408    private void SelectedRows_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
409      if (InvokeRequired)
410        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved), sender, e);
411      else {
412        RemoveSelectedDataRows(e.Items);
413      }
414    }
415
416    private void SelectedRows_ItemsAdded(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
417      if (InvokeRequired)
418        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded), sender, e);
419      else {
420        AddSelectedDataRows(e.Items);
421      }
422    }
423
424
425    private void Rows_ItemsAdded(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
426      if (InvokeRequired)
427        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded), sender, e);
428      else
429      {
430        AddDataRows(e.Items);
431      }
432    }
433    private void Rows_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<DataRow> e)
434    {
435      if (InvokeRequired)
436        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved), sender, e);
437      else
438      {
439        RemoveDataRows(e.Items);
440      }
441    }
442    private void Rows_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<DataRow> e)
443    {
444      if (InvokeRequired)
445        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced), sender, e);
446      else
447      {
448        RemoveDataRows(e.OldItems);
449        AddDataRows(e.Items);
450      }
451    }
452    private void Rows_CollectionReset(object sender, CollectionItemsChangedEventArgs<DataRow> e)
453    {
454      if (InvokeRequired)
455        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset), sender, e);
456      else
457      {
458        RemoveDataRows(e.OldItems);
459        AddDataRows(e.Items);
460      }
461    }
462    #endregion
463    #region Row Event Handlers
464    private void Row_VisualPropertiesChanged(object sender, EventArgs e)
465    {
466      if (InvokeRequired)
467        Invoke(new EventHandler(Row_VisualPropertiesChanged), sender, e);
468      else
469      {
470        DataRow row = (DataRow)sender;
471        Series series = chart.Series[row.Name];
472        series.Points.Clear();
473        ConfigureSeries(series, row);
474        FillSeriesWithRowValues(series, row);
475        RecalculateAxesScale(chart.ChartAreas[0]);
476      }
477    }
478    private void Row_NameChanged(object sender, EventArgs e)
479    {
480      if (InvokeRequired)
481        Invoke(new EventHandler(Row_NameChanged), sender, e);
482      else
483      {
484        DataRow row = (DataRow)sender;
485        chart.Series[row.Name].Name = row.Name;
486      }
487    }
488    #endregion
489    #region Values Event Handlers
490    private void Values_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e)
491    {
492      if (InvokeRequired)
493        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded), sender, e);
494      else
495      {
496        DataRow row = null;
497        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
498        if (row != null)
499        {
500          Series rowSeries = chart.Series[row.Name];
501          if (!invisibleSeries.Contains(rowSeries))
502          {
503            rowSeries.Points.Clear();
504            FillSeriesWithRowValues(rowSeries, row);
505            RecalculateAxesScale(chart.ChartAreas[0]);
506            UpdateYCursorInterval();
507          }
508        }
509      }
510    }
511    private void Values_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e)
512    {
513      if (InvokeRequired)
514        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved), sender, e);
515      else
516      {
517        DataRow row = null;
518        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
519        if (row != null)
520        {
521          Series rowSeries = chart.Series[row.Name];
522          if (!invisibleSeries.Contains(rowSeries))
523          {
524            rowSeries.Points.Clear();
525            FillSeriesWithRowValues(rowSeries, row);
526            RecalculateAxesScale(chart.ChartAreas[0]);
527            UpdateYCursorInterval();
528          }
529        }
530      }
531    }
532    private void Values_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e)
533    {
534      if (InvokeRequired)
535        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced), sender, e);
536      else
537      {
538        DataRow row = null;
539        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
540        if (row != null)
541        {
542          Series rowSeries = chart.Series[row.Name];
543          if (!invisibleSeries.Contains(rowSeries))
544          {
545            if (row.VisualProperties.ChartType == DataRowVisualProperties.DataRowChartType.Histogram)
546            {
547              rowSeries.Points.Clear();
548              FillSeriesWithRowValues(rowSeries, row);
549            }
550            else
551            {
552              foreach (IndexedItem<double> item in e.Items)
553              {
554                if (IsInvalidValue(item.Value))
555                  rowSeries.Points[item.Index].IsEmpty = true;
556                else
557                {
558                  rowSeries.Points[item.Index].YValues = new double[] { item.Value };
559                  rowSeries.Points[item.Index].IsEmpty = false;
560                }
561              }
562            }
563            RecalculateAxesScale(chart.ChartAreas[0]);
564            UpdateYCursorInterval();
565          }
566        }
567      }
568    }
569    private void Values_ItemsMoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e)
570    {
571      if (InvokeRequired)
572        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved), sender, e);
573      else
574      {
575        DataRow row = null;
576        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
577        if (row != null)
578        {
579          Series rowSeries = chart.Series[row.Name];
580          if (!invisibleSeries.Contains(rowSeries))
581          {
582            rowSeries.Points.Clear();
583            FillSeriesWithRowValues(rowSeries, row);
584            RecalculateAxesScale(chart.ChartAreas[0]);
585            UpdateYCursorInterval();
586          }
587        }
588      }
589    }
590
591    private void Values_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e)
592    {
593      if (InvokeRequired)
594        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset), sender, e);
595      else
596      {
597        DataRow row = null;
598        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
599        if (row != null)
600        {
601          Series rowSeries = chart.Series[row.Name];
602          if (!invisibleSeries.Contains(rowSeries))
603          {
604            rowSeries.Points.Clear();
605            FillSeriesWithRowValues(rowSeries, row);
606            RecalculateAxesScale(chart.ChartAreas[0]);
607            UpdateYCursorInterval();
608          }
609        }
610      }
611    }
612    #endregion
613    #endregion
614
615    #region Chart Event Handlers
616    private void chart_MouseDown(object sender, MouseEventArgs e)
617    {
618      HitTestResult result = chart.HitTest(e.X, e.Y);
619      if (result.ChartElementType == ChartElementType.LegendItem)
620      {
621        ToggleSeriesVisible(result.Series);
622      }
623    }
624    private void chart_MouseMove(object sender, MouseEventArgs e)
625    {
626      HitTestResult result = chart.HitTest(e.X, e.Y);
627      if (result.ChartElementType == ChartElementType.LegendItem)
628        this.Cursor = Cursors.Hand;
629      else
630        this.Cursor = Cursors.Default;
631    }
632    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e)
633    {
634      foreach (LegendItem legendItem in e.LegendItems)
635      {
636        var series = chart.Series[legendItem.SeriesName];
637        if (series != null)
638        {
639          bool seriesIsInvisible = invisibleSeries.Contains(series);
640          foreach (LegendCell cell in legendItem.Cells)
641          {
642            cell.ForeColor = seriesIsInvisible ? Color.Gray : Color.Black;
643          }
644        }
645      }
646    }
647    #endregion
648
649    private void ToggleSeriesVisible(Series series)
650    {
651      if (!invisibleSeries.Contains(series))
652      {
653        series.Points.Clear();
654        invisibleSeries.Add(series);
655      }
656      else
657      {
658        invisibleSeries.Remove(series);
659        if (Content != null)
660        {
661
662          var row = (from r in Content.Rows
663                     where r.Name == series.Name
664                     select r).Single();
665          FillSeriesWithRowValues(series, row);
666          this.chart.Legends[series.Legend].ForeColor = Color.Black;
667          RecalculateAxesScale(chart.ChartAreas[0]);
668          UpdateYCursorInterval();
669        }
670      }
671    }
672
673    private void FillSeriesWithRowValues(Series series, DataRow row)
674    {
675      switch (row.VisualProperties.ChartType)
676      {
677        case DataRowVisualProperties.DataRowChartType.Histogram:
678          CalculateHistogram(series, row);
679          break;
680        default:
681          {
682            bool yLogarithmic = series.YAxisType == AxisType.Primary
683                                  ? Content.VisualProperties.YAxisLogScale
684                                  : Content.VisualProperties.SecondYAxisLogScale;
685            bool xLogarithmic = series.XAxisType == AxisType.Primary
686                                  ? Content.VisualProperties.XAxisLogScale
687                                  : Content.VisualProperties.SecondXAxisLogScale;
688            for (int i = 0; i < row.Values.Count; i++)
689            {
690              var value = row.Values[i];
691              var point = new DataPoint();
692              point.XValue = row.VisualProperties.StartIndexZero && !xLogarithmic ? i : i + 1;
693              if (IsInvalidValue(value) || (yLogarithmic && value <= 0))
694                point.IsEmpty = true;
695              else
696                point.YValues = new double[] { value };
697              series.Points.Add(point);
698            }
699          }
700          break;
701      }
702    }
703
704    // get minimum ignores nan values
705    private double GetMinimum(IEnumerable<double> values) {
706      double min = Double.MaxValue;
707
708      foreach (double value in values)
709      {
710        if (!Double.IsNaN(value) && value < min)
711          min = value;
712      }
713      return min;
714    }
715
716    //get maximium ignores nan values
717    private double GetMaximum(IEnumerable<double> values)
718    {
719      double max = Double.MinValue;
720
721      foreach (double value in values)
722      {
723        if (!Double.IsNaN(value) && value > max)
724          max = value;
725      }
726      return max;
727    }
728
729    protected virtual void CalculateHistogram(Series series, DataRow row)
730    {
731      if (classification != null)
732      {
733
734        var qry = row.Values.Select((i, index) => new { i, j = classification[index] })
735                           .GroupBy((x) => x.j).ToDictionary(x => x.Key, x => x.Select(v => v.i).ToList());
736        chart.Titles.Add(row.Name);
737        foreach (KeyValuePair<double, List<double>> entry in qry)
738        {
739          var s = new Series(row.Name + entry.Key);
740
741          ConfigureSeries(s, row);
742          AddPointsToHistogramSeries(s, row, entry.Value);
743
744          s.LegendText = entry.Key.ToString();
745
746          chart.Series.Add(s);
747        }
748      }
749      else
750      {
751        series.Points.Clear();
752        ConfigureSeries(series, row);
753        AddPointsToHistogramSeries(series, row, null);
754      }
755    }
756
757    private void AddPointsToHistogramSeries(Series series, DataRow row, List<double> values)
758    {
759
760      if (!row.Values.Any()) return;
761      int bins = row.VisualProperties.Bins;
762
763      double minValue = GetMinimum(row.Values);
764      double maxValue = GetMaximum(row.Values);
765      double intervalWidth = (maxValue - minValue) / bins;
766      if (intervalWidth < 0) return;
767      if (intervalWidth == 0)
768      {
769        series.Points.AddXY(minValue, row.Values.Count);
770        return;
771      }
772
773
774      if (!row.VisualProperties.ExactBins) {
775        intervalWidth = HumanRoundRange(intervalWidth);
776        minValue = Math.Floor(minValue / intervalWidth) * intervalWidth;
777        maxValue = Math.Ceiling(maxValue / intervalWidth) * intervalWidth;
778      }
779
780      double intervalCenter = intervalWidth / 2;
781
782      double min = 0.0, max = 0.0;
783      if (!Double.IsNaN(Content.VisualProperties.XAxisMinimumFixedValue) && !Content.VisualProperties.XAxisMinimumAuto)
784        min = Content.VisualProperties.XAxisMinimumFixedValue;
785      else min = minValue;
786      if (!Double.IsNaN(Content.VisualProperties.XAxisMaximumFixedValue) && !Content.VisualProperties.XAxisMaximumAuto)
787        max = Content.VisualProperties.XAxisMaximumFixedValue;
788      else max = maxValue + intervalWidth;
789
790      double axisInterval = intervalWidth / row.VisualProperties.ScaleFactor;
791
792      var area = chart.ChartAreas[0];
793      area.AxisX.Interval = axisInterval;
794
795      series.SetCustomProperty("PointWidth", "1"); // 0.8 is the default value
796
797      // get the range or intervals which define the grouping of the frequency values
798      var doubleRange = DoubleRange(min, max, intervalWidth).Skip(1).ToList();
799
800
801      if (values == null) {
802        values = row.Values.ToList(); ;
803      }
804
805      // aggregate the row values by unique key and frequency value
806      var valueFrequencies = (from v in values
807                              where !IsInvalidValue(v)
808                              orderby v
809                              group v by v into g
810                              select new Tuple<double, double>(g.First(), g.Count())).ToList();
811
812      //  shift the chart to the left so the bars are placed on the intervals
813      if (classification != null || valueFrequencies.First().Item1 < doubleRange.First()) {
814        series.Points.Add(new DataPoint(min - intervalWidth, 0));
815        series.Points.Add(new DataPoint(max + intervalWidth, 0));
816      }
817
818      // add data points
819      int j = 0;
820      foreach (var d in doubleRange)
821      {
822        double sum = 0.0;
823        // sum the frequency values that fall within the same interval
824        while (j < valueFrequencies.Count && valueFrequencies[j].Item1 < d)
825        {
826          sum += valueFrequencies[j].Item2;
827          ++j;
828        }
829        string xAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.XAxisTitle)
830                              ? "X"
831                              : Content.VisualProperties.XAxisTitle;
832        string yAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.YAxisTitle)
833                              ? "Y"
834                              : Content.VisualProperties.YAxisTitle;
835        series.Points.Add(new DataPoint(d - intervalCenter, sum)
836        {
837          ToolTip =
838            xAxisTitle + ": [" + (d - intervalWidth) + "-" + d + ")" + Environment.NewLine +
839            yAxisTitle + ": " + sum
840        });
841      }
842    }
843
844    #region Helpers
845    public static IEnumerable<double> DoubleRange(double min, double max, double step)
846    {
847      double i;
848      for (i = min; i <= max; i += step)
849        yield return i;
850
851      if (i != max + step)
852        yield return i;
853    }
854
855    protected void RemoveCustomPropertyIfExists(Series series, string property)
856    {
857      if (series.IsCustomPropertySet(property)) series.DeleteCustomProperty(property);
858    }
859
860    private double HumanRoundRange(double range)
861    {
862      double base10 = Math.Pow(10.0, Math.Floor(Math.Log10(range)));
863      double rounding = range / base10;
864      if (rounding <= 1.5) rounding = 1;
865      else if (rounding <= 2.25) rounding = 2;
866      else if (rounding <= 3.75) rounding = 2.5;
867      else if (rounding <= 7.5) rounding = 5;
868      else rounding = 10;
869      return rounding * base10;
870    }
871
872    private double HumanRoundMax(double max)
873    {
874      double base10;
875      if (max > 0) base10 = Math.Pow(10.0, Math.Floor(Math.Log10(max)));
876      else base10 = Math.Pow(10.0, Math.Ceiling(Math.Log10(-max)));
877      double rounding = (max > 0) ? base10 : -base10;
878      while (rounding < max) rounding += base10;
879      return rounding;
880    }
881
882    private ChartDashStyle ConvertLineStyle(DataRowVisualProperties.DataRowLineStyle dataRowLineStyle)
883    {
884      switch (dataRowLineStyle)
885      {
886        case DataRowVisualProperties.DataRowLineStyle.Dash:
887          return ChartDashStyle.Dash;
888        case DataRowVisualProperties.DataRowLineStyle.DashDot:
889          return ChartDashStyle.DashDot;
890        case DataRowVisualProperties.DataRowLineStyle.DashDotDot:
891          return ChartDashStyle.DashDotDot;
892        case DataRowVisualProperties.DataRowLineStyle.Dot:
893          return ChartDashStyle.Dot;
894        case DataRowVisualProperties.DataRowLineStyle.NotSet:
895          return ChartDashStyle.NotSet;
896        case DataRowVisualProperties.DataRowLineStyle.Solid:
897          return ChartDashStyle.Solid;
898        default:
899          return ChartDashStyle.NotSet;
900      }
901    }
902
903    protected static bool IsInvalidValue(double x)
904    {
905      return double.IsNaN(x) || x < (double)decimal.MinValue || x > (double)decimal.MaxValue;
906    }
907    #endregion
908  }
909}
Note: See TracBrowser for help on using the repository browser.