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, 10 years ago

Add colors to histogram / remove all in one mode

File size: 36.1 KB
RevLine 
[2908]1#region License Information
2/* HeuristicLab
[9456]3 * Copyright (C) 2002-2013 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[2908]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;
[4068]23using System.Collections.Generic;
24using System.Drawing;
[3703]25using System.Linq;
[4068]26using System.Windows.Forms;
[2908]27using System.Windows.Forms.DataVisualization.Charting;
[10804]28using HeuristicLab.Collections;
29using HeuristicLab.Core.Views;
[10803]30using HeuristicLab.DataPreprocessing.Implementations;
[10804]31using HeuristicLab.MainForm;
[2908]32
[10867]33namespace HeuristicLab.Analysis.Views
34{
[10771]35  [View("Preprocessing DataTable View")]
[10803]36  [Content(typeof(PreprocessingDataTable), false)]
[10867]37  public partial class PreprocessingDataTableView : ItemView, IConfigureableView
38  {
[5097]39    protected List<Series> invisibleSeries;
40    protected Dictionary<IObservableList<double>, DataRow> valuesRowsTable;
[7244]41
[10867]42    public new PreprocessingDataTable Content
43    {
[10803]44      get { return (PreprocessingDataTable)base.Content; }
[2908]45      set { base.Content = value; }
46    }
47
[10867]48    public List<double> classification { get; set; }
49
50    public PreprocessingDataTableView()
51    {
[2908]52      InitializeComponent();
53      valuesRowsTable = new Dictionary<IObservableList<double>, DataRow>();
[3703]54      invisibleSeries = new List<Series>();
[4636]55      chart.CustomizeAllChartAreas();
56      chart.ChartAreas[0].CursorX.Interval = 1;
[2908]57    }
58
[6342]59    #region Event Handler Registration
[10867]60    protected override void DeregisterContentEvents()
61    {
[2908]62      foreach (DataRow row in Content.Rows)
63        DeregisterDataRowEvents(row);
[4870]64      Content.VisualPropertiesChanged -= new EventHandler(Content_VisualPropertiesChanged);
[2908]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);
[10847]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);
[2908]74      base.DeregisterContentEvents();
75    }
[10867]76    protected override void RegisterContentEvents()
77    {
[2908]78      base.RegisterContentEvents();
[4870]79      Content.VisualPropertiesChanged += new EventHandler(Content_VisualPropertiesChanged);
[2908]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);
[10847]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);
[2908]89    }
90
[10847]91
[6342]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    }
[10867]102    protected virtual void DeregisterDataRowEvents(DataRow row)
103    {
[6342]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
[10867]115    protected override void OnContentChanged()
116    {
[2908]117      base.OnContentChanged();
[3703]118      invisibleSeries.Clear();
[4870]119      chart.ChartAreas[0].AxisX.Title = string.Empty;
120      chart.ChartAreas[0].AxisY.Title = string.Empty;
121      chart.ChartAreas[0].AxisY2.Title = string.Empty;
[2908]122      chart.Series.Clear();
[10867]123      if (Content != null)
124      {
[10851]125
[10867]126        if (classification != null)
127        {
128          chart.Titles[0].Text = Content.Name;
129        }
130
[7977]131        AddDataRows(Content.Rows);
[10803]132        AddSelectedDataRows(Content.SelectedRows);
[6342]133        ConfigureChartArea(chart.ChartAreas[0]);
134        RecalculateAxesScale(chart.ChartAreas[0]);
[2908]135      }
136    }
137
[10867]138    protected override void SetEnabledStateOfControls()
139    {
[3904]140      base.SetEnabledStateOfControls();
[3454]141      chart.Enabled = Content != null;
142    }
143
[10867]144    public void ShowConfiguration()
145    {
146      if (Content != null)
147      {
148        using (var dialog = new DataTableVisualPropertiesDialog(Content))
149        {
[7244]150          dialog.ShowDialog(this);
151        }
[10867]152      }
153      else MessageBox.Show("Nothing to configure.");
[6342]154    }
[10867]155    protected virtual void AddDataRows(IEnumerable<DataRow> rows)
156    {
157      foreach (var row in rows)
158      {
[7977]159        RegisterDataRowEvents(row);
[9258]160        var series = new Series(row.Name);
[7977]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);
[10867]165
166        if (classification == null)
167        {
168          chart.Series.Add(series);
169        }
[7977]170      }
[10803]171
[6342]172      ConfigureChartArea(chart.ChartAreas[0]);
173      RecalculateAxesScale(chart.ChartAreas[0]);
174      UpdateYCursorInterval();
175    }
176
[10803]177    protected virtual void AddSelectedDataRows(IEnumerable<DataRow> rows) {
[10851]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
[10803]193      foreach (var row in rows) {
194        if (row.VisualProperties.ChartType == DataRowVisualProperties.DataRowChartType.Line) {
195          row.VisualProperties.IsVisibleInLegend = false;
[10851]196          row.VisualProperties.Color = Color.Green;
[10847]197          //add selected to name in order to avoid naming conflict
198          var series = new Series(row.Name+"(Selected)");
[10803]199          ConfigureSeries(series, row);
200          FillSeriesWithRowValues(series, row);
[10867]201
202          if (classification == null)
203          {
204            chart.Series.Add(series);
205          }
206
[10803]207        }
208      }
209    }
210
[10847]211    protected virtual void RemoveSelectedDataRows(IEnumerable<DataRow> rows) {
[10851]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
[10847]219      foreach (var row in rows) {
220        Series series = chart.Series[row.Name + "(Selected)"];
221        chart.Series.Remove(series);
222      }
223    }
224
[7979]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
[10867]236    private void ConfigureSeries(Series series, DataRow row)
237    {
[6342]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;
[7126]246      series.IsVisibleInLegend = row.VisualProperties.IsVisibleInLegend;
[10804]247
[4644]248      switch (row.VisualProperties.ChartType) {
249        case DataRowVisualProperties.DataRowChartType.Line:
250          series.ChartType = SeriesChartType.FastLine;
[6342]251          series.BorderWidth = row.VisualProperties.LineWidth;
252          series.BorderDashStyle = ConvertLineStyle(row.VisualProperties.LineStyle);
[4644]253          break;
254        case DataRowVisualProperties.DataRowChartType.Bars:
[6342]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;
[10867]258          else
259          {
[6342]260            series.ChartType = SeriesChartType.FastPoint; //default
261            row.VisualProperties.ChartType = DataRowVisualProperties.DataRowChartType.Points;
262          }
[4644]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;
[6342]270        case DataRowVisualProperties.DataRowChartType.Histogram:
[10867]271          series.ChartType = SeriesChartType.StackedColumn;
[6342]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;
[7297]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;
[4644]282        default:
283          series.ChartType = SeriesChartType.FastPoint;
284          break;
285      }
286      series.YAxisType = row.VisualProperties.SecondYAxis ? AxisType.Secondary : AxisType.Primary;
[6342]287      series.XAxisType = row.VisualProperties.SecondXAxis ? AxisType.Secondary : AxisType.Primary;
[6628]288      if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;
289      else series.LegendText = row.Name;
[7304]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";
[2908]301    }
[3703]302
[10867]303    private void ConfigureChartArea(ChartArea area)
304    {
[6342]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;
[7224]307      chart.Titles[0].Text = Content.VisualProperties.Title;
[5097]308
[6342]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;
[9258]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;
[6342]329    }
330
[10867]331    private void RecalculateAxesScale(ChartArea area)
332    {
[6342]333      // Reset the axes bounds so that RecalculateAxesScale() will assign new bounds
[10867]334      foreach (Axis a in area.Axes)
335      {
[6342]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;
[6676]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;
[6342]355    }
356
[10867]357    protected virtual void UpdateYCursorInterval()
358    {
[6342]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();
[3703]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
[6342]375    #region Event Handlers
376    #region Content Event Handlers
[10867]377    private void Content_VisualPropertiesChanged(object sender, EventArgs e)
378    {
[4870]379      if (InvokeRequired)
380        Invoke(new EventHandler(Content_VisualPropertiesChanged), sender, e);
[10867]381      else
382      {
[6342]383        ConfigureChartArea(chart.ChartAreas[0]);
384        RecalculateAxesScale(chart.ChartAreas[0]); // axes min/max could have changed
[4870]385      }
386    }
[6342]387    #endregion
388    #region Rows Event Handlers
[10847]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
[2908]425    private void Rows_ItemsAdded(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
426      if (InvokeRequired)
427        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded), sender, e);
[10867]428      else
429      {
[7977]430        AddDataRows(e.Items);
[2908]431      }
432    }
[10867]433    private void Rows_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<DataRow> e)
434    {
[2908]435      if (InvokeRequired)
436        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved), sender, e);
[10867]437      else
438      {
[7977]439        RemoveDataRows(e.Items);
[2908]440      }
441    }
[10867]442    private void Rows_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<DataRow> e)
443    {
[2908]444      if (InvokeRequired)
445        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced), sender, e);
[10867]446      else
447      {
[7977]448        RemoveDataRows(e.OldItems);
449        AddDataRows(e.Items);
[2908]450      }
451    }
[10867]452    private void Rows_CollectionReset(object sender, CollectionItemsChangedEventArgs<DataRow> e)
453    {
[2908]454      if (InvokeRequired)
455        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset), sender, e);
[10867]456      else
457      {
[7977]458        RemoveDataRows(e.OldItems);
459        AddDataRows(e.Items);
[2908]460      }
461    }
[6342]462    #endregion
463    #region Row Event Handlers
[10867]464    private void Row_VisualPropertiesChanged(object sender, EventArgs e)
465    {
[4644]466      if (InvokeRequired)
467        Invoke(new EventHandler(Row_VisualPropertiesChanged), sender, e);
[10867]468      else
469      {
[4644]470        DataRow row = (DataRow)sender;
[6342]471        Series series = chart.Series[row.Name];
472        series.Points.Clear();
473        ConfigureSeries(series, row);
474        FillSeriesWithRowValues(series, row);
475        RecalculateAxesScale(chart.ChartAreas[0]);
[4644]476      }
477    }
[10867]478    private void Row_NameChanged(object sender, EventArgs e)
479    {
[2908]480      if (InvokeRequired)
481        Invoke(new EventHandler(Row_NameChanged), sender, e);
[10867]482      else
483      {
[2908]484        DataRow row = (DataRow)sender;
485        chart.Series[row.Name].Name = row.Name;
486      }
487    }
[6342]488    #endregion
489    #region Values Event Handlers
[10867]490    private void Values_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e)
491    {
[2908]492      if (InvokeRequired)
493        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded), sender, e);
[10867]494      else
495      {
[3561]496        DataRow row = null;
497        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
[10867]498        if (row != null)
499        {
[3703]500          Series rowSeries = chart.Series[row.Name];
[10867]501          if (!invisibleSeries.Contains(rowSeries))
502          {
[4778]503            rowSeries.Points.Clear();
504            FillSeriesWithRowValues(rowSeries, row);
[6342]505            RecalculateAxesScale(chart.ChartAreas[0]);
[3703]506            UpdateYCursorInterval();
[3080]507          }
508        }
[2908]509      }
510    }
[10867]511    private void Values_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e)
512    {
[2908]513      if (InvokeRequired)
514        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved), sender, e);
[10867]515      else
516      {
[3561]517        DataRow row = null;
518        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
[10867]519        if (row != null)
520        {
[3703]521          Series rowSeries = chart.Series[row.Name];
[10867]522          if (!invisibleSeries.Contains(rowSeries))
523          {
[4778]524            rowSeries.Points.Clear();
525            FillSeriesWithRowValues(rowSeries, row);
[6342]526            RecalculateAxesScale(chart.ChartAreas[0]);
[3703]527            UpdateYCursorInterval();
528          }
[3561]529        }
[2908]530      }
531    }
[10867]532    private void Values_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e)
533    {
[2908]534      if (InvokeRequired)
535        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced), sender, e);
[10867]536      else
537      {
[3561]538        DataRow row = null;
539        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
[10867]540        if (row != null)
541        {
[3703]542          Series rowSeries = chart.Series[row.Name];
[10867]543          if (!invisibleSeries.Contains(rowSeries))
544          {
545            if (row.VisualProperties.ChartType == DataRowVisualProperties.DataRowChartType.Histogram)
546            {
[6342]547              rowSeries.Points.Clear();
548              FillSeriesWithRowValues(rowSeries, row);
[10867]549            }
550            else
551            {
552              foreach (IndexedItem<double> item in e.Items)
553              {
[6342]554                if (IsInvalidValue(item.Value))
555                  rowSeries.Points[item.Index].IsEmpty = true;
[10867]556                else
557                {
[6342]558                  rowSeries.Points[item.Index].YValues = new double[] { item.Value };
559                  rowSeries.Points[item.Index].IsEmpty = false;
560                }
[3703]561              }
[3561]562            }
[6342]563            RecalculateAxesScale(chart.ChartAreas[0]);
[3703]564            UpdateYCursorInterval();
[3530]565          }
[3080]566        }
[2908]567      }
568    }
[10867]569    private void Values_ItemsMoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e)
570    {
[2908]571      if (InvokeRequired)
572        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved), sender, e);
[10867]573      else
574      {
[3561]575        DataRow row = null;
576        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
[10867]577        if (row != null)
578        {
[3703]579          Series rowSeries = chart.Series[row.Name];
[10867]580          if (!invisibleSeries.Contains(rowSeries))
581          {
[4778]582            rowSeries.Points.Clear();
583            FillSeriesWithRowValues(rowSeries, row);
[6342]584            RecalculateAxesScale(chart.ChartAreas[0]);
[3703]585            UpdateYCursorInterval();
[3530]586          }
[3080]587        }
[2908]588      }
589    }
[3530]590
[10867]591    private void Values_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e)
592    {
[2908]593      if (InvokeRequired)
594        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset), sender, e);
[10867]595      else
596      {
[3561]597        DataRow row = null;
598        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
[10867]599        if (row != null)
600        {
[3703]601          Series rowSeries = chart.Series[row.Name];
[10867]602          if (!invisibleSeries.Contains(rowSeries))
603          {
[3703]604            rowSeries.Points.Clear();
[4778]605            FillSeriesWithRowValues(rowSeries, row);
[6342]606            RecalculateAxesScale(chart.ChartAreas[0]);
[4778]607            UpdateYCursorInterval();
[3530]608          }
[3080]609        }
[2908]610      }
611    }
612    #endregion
[6342]613    #endregion
[4623]614
[6342]615    #region Chart Event Handlers
[10867]616    private void chart_MouseDown(object sender, MouseEventArgs e)
617    {
[3703]618      HitTestResult result = chart.HitTest(e.X, e.Y);
[10867]619      if (result.ChartElementType == ChartElementType.LegendItem)
620      {
[3703]621        ToggleSeriesVisible(result.Series);
622      }
623    }
[10867]624    private void chart_MouseMove(object sender, MouseEventArgs e)
625    {
[6342]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    }
[10867]632    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e)
633    {
634      foreach (LegendItem legendItem in e.LegendItems)
635      {
[6342]636        var series = chart.Series[legendItem.SeriesName];
[10867]637        if (series != null)
638        {
[6342]639          bool seriesIsInvisible = invisibleSeries.Contains(series);
[10867]640          foreach (LegendCell cell in legendItem.Cells)
641          {
[6342]642            cell.ForeColor = seriesIsInvisible ? Color.Gray : Color.Black;
643          }
644        }
645      }
646    }
647    #endregion
[3530]648
[10867]649    private void ToggleSeriesVisible(Series series)
650    {
651      if (!invisibleSeries.Contains(series))
652      {
[3703]653        series.Points.Clear();
654        invisibleSeries.Add(series);
[10867]655      }
656      else
657      {
[3703]658        invisibleSeries.Remove(series);
[10867]659        if (Content != null)
660        {
[3703]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;
[6342]667          RecalculateAxesScale(chart.ChartAreas[0]);
[3703]668          UpdateYCursorInterval();
669        }
670      }
671    }
672
[10867]673    private void FillSeriesWithRowValues(Series series, DataRow row)
674    {
675      switch (row.VisualProperties.ChartType)
676      {
[6342]677        case DataRowVisualProperties.DataRowChartType.Histogram:
678          CalculateHistogram(series, row);
679          break;
[10867]680        default:
681          {
[9258]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;
[10867]688            for (int i = 0; i < row.Values.Count; i++)
689            {
[6342]690              var value = row.Values[i];
[9258]691              var point = new DataPoint();
692              point.XValue = row.VisualProperties.StartIndexZero && !xLogarithmic ? i : i + 1;
693              if (IsInvalidValue(value) || (yLogarithmic && value <= 0))
[6342]694                point.IsEmpty = true;
695              else
696                point.YValues = new double[] { value };
697              series.Points.Add(point);
698            }
699          }
700          break;
[3703]701      }
702    }
703
[10803]704    // get minimum ignores nan values
[10804]705    private double GetMinimum(IEnumerable<double> values) {
[10803]706      double min = Double.MaxValue;
707
[10867]708      foreach (double value in values)
709      {
[10803]710        if (!Double.IsNaN(value) && value < min)
[10804]711          min = value;
[10803]712      }
713      return min;
714    }
715
716    //get maximium ignores nan values
[10867]717    private double GetMaximum(IEnumerable<double> values)
718    {
[10803]719      double max = Double.MinValue;
720
[10867]721      foreach (double value in values)
722      {
[10803]723        if (!Double.IsNaN(value) && value > max)
724          max = value;
725      }
726      return max;
727    }
728
[10867]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
[6342]760      if (!row.Values.Any()) return;
761      int bins = row.VisualProperties.Bins;
762
[10803]763      double minValue = GetMinimum(row.Values);
764      double maxValue = GetMaximum(row.Values);
[6342]765      double intervalWidth = (maxValue - minValue) / bins;
766      if (intervalWidth < 0) return;
[10867]767      if (intervalWidth == 0)
768      {
[6342]769        series.Points.AddXY(minValue, row.Values.Count);
770        return;
771      }
772
[10847]773
[6342]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
[7223]780      double intervalCenter = intervalWidth / 2;
781
[8339]782      double min = 0.0, max = 0.0;
[6978]783      if (!Double.IsNaN(Content.VisualProperties.XAxisMinimumFixedValue) && !Content.VisualProperties.XAxisMinimumAuto)
[7223]784        min = Content.VisualProperties.XAxisMinimumFixedValue;
785      else min = minValue;
[8339]786      if (!Double.IsNaN(Content.VisualProperties.XAxisMaximumFixedValue) && !Content.VisualProperties.XAxisMaximumAuto)
787        max = Content.VisualProperties.XAxisMaximumFixedValue;
788      else max = maxValue + intervalWidth;
[6978]789
[7223]790      double axisInterval = intervalWidth / row.VisualProperties.ScaleFactor;
[6978]791
[7223]792      var area = chart.ChartAreas[0];
793      area.AxisX.Interval = axisInterval;
[6978]794
[7223]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
[8339]798      var doubleRange = DoubleRange(min, max, intervalWidth).Skip(1).ToList();
[7223]799
[10867]800
801      if (values == null) {
802        values = row.Values.ToList(); ;
803      }
804
[7223]805      // aggregate the row values by unique key and frequency value
[10867]806      var valueFrequencies = (from v in values
[7223]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
[10867]812      //  shift the chart to the left so the bars are placed on the intervals
813      if (classification != null || valueFrequencies.First().Item1 < doubleRange.First()) {
[7223]814        series.Points.Add(new DataPoint(min - intervalWidth, 0));
[10867]815        series.Points.Add(new DataPoint(max + intervalWidth, 0));
816      }
[7223]817
818      // add data points
819      int j = 0;
[10867]820      foreach (var d in doubleRange)
821      {
[7223]822        double sum = 0.0;
823        // sum the frequency values that fall within the same interval
[10867]824        while (j < valueFrequencies.Count && valueFrequencies[j].Item1 < d)
825        {
[7223]826          sum += valueFrequencies[j].Item2;
827          ++j;
[3703]828        }
[7304]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;
[10867]835        series.Points.Add(new DataPoint(d - intervalCenter, sum)
836        {
[7304]837          ToolTip =
838            xAxisTitle + ": [" + (d - intervalWidth) + "-" + d + ")" + Environment.NewLine +
839            yAxisTitle + ": " + sum
840        });
[3703]841      }
842    }
843
[6342]844    #region Helpers
[10867]845    public static IEnumerable<double> DoubleRange(double min, double max, double step)
846    {
[7223]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
[10867]855    protected void RemoveCustomPropertyIfExists(Series series, string property)
856    {
[6342]857      if (series.IsCustomPropertySet(property)) series.DeleteCustomProperty(property);
858    }
[5097]859
[10867]860    private double HumanRoundRange(double range)
861    {
[6342]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
[10867]872    private double HumanRoundMax(double max)
873    {
[6342]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
[10867]882    private ChartDashStyle ConvertLineStyle(DataRowVisualProperties.DataRowLineStyle dataRowLineStyle)
883    {
884      switch (dataRowLineStyle)
885      {
[6342]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
[10867]903    protected static bool IsInvalidValue(double x)
904    {
[3530]905      return double.IsNaN(x) || x < (double)decimal.MinValue || x > (double)decimal.MaxValue;
906    }
[6342]907    #endregion
[2908]908  }
909}
Note: See TracBrowser for help on using the repository browser.