Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Analysis.Views/3.3/DataTableView.cs @ 4682

Last change on this file since 4682 was 4648, checked in by swagner, 14 years ago

Worked on flexible coloring of data rows and on changing the initial index from 1 to 0 (#925)

File size: 18.9 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2010 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Drawing;
25using System.Linq;
26using System.Windows.Forms;
27using System.Windows.Forms.DataVisualization.Charting;
28using HeuristicLab.Collections;
29using HeuristicLab.Core.Views;
30using HeuristicLab.MainForm;
31
32namespace HeuristicLab.Analysis.Views {
33  /// <summary>
34  /// The visual representation of a <see cref="Variable"/>.
35  /// </summary>
36  [View("DataTable View")]
37  [Content(typeof(DataTable), true)]
38  public sealed partial class DataTableView : NamedItemView {
39    private List<Series> invisibleSeries;
40    private Dictionary<IObservableList<double>, DataRow> valuesRowsTable;
41    /// <summary>
42    /// Gets or sets the variable to represent visually.
43    /// </summary>
44    /// <remarks>Uses property <see cref="ViewBase.Item"/> of base class <see cref="ViewBase"/>.
45    /// No own data storage present.</remarks>
46    public new DataTable Content {
47      get { return (DataTable)base.Content; }
48      set { base.Content = value; }
49    }
50
51    /// <summary>
52    /// Initializes a new instance of <see cref="VariableView"/> with caption "Variable".
53    /// </summary>
54    public DataTableView() {
55      InitializeComponent();
56      valuesRowsTable = new Dictionary<IObservableList<double>, DataRow>();
57      invisibleSeries = new List<Series>();
58      chart.CustomizeAllChartAreas();
59      chart.ChartAreas[0].CursorX.Interval = 1;
60    }
61
62    /// <summary>
63    /// Removes the eventhandlers from the underlying <see cref="Variable"/>.
64    /// </summary>
65    /// <remarks>Calls <see cref="ViewBase.RemoveItemEvents"/> of base class <see cref="ViewBase"/>.</remarks>
66    protected override void DeregisterContentEvents() {
67      foreach (DataRow row in Content.Rows)
68        DeregisterDataRowEvents(row);
69      Content.Rows.ItemsAdded -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded);
70      Content.Rows.ItemsRemoved -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved);
71      Content.Rows.ItemsReplaced -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced);
72      Content.Rows.CollectionReset -= new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset);
73      base.DeregisterContentEvents();
74    }
75
76    /// <summary>
77    /// Adds eventhandlers to the underlying <see cref="Variable"/>.
78    /// </summary>
79    /// <remarks>Calls <see cref="ViewBase.AddItemEvents"/> of base class <see cref="ViewBase"/>.</remarks>
80    protected override void RegisterContentEvents() {
81      base.RegisterContentEvents();
82      Content.Rows.ItemsAdded += new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded);
83      Content.Rows.ItemsRemoved += new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved);
84      Content.Rows.ItemsReplaced += new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced);
85      Content.Rows.CollectionReset += new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset);
86      foreach (DataRow row in Content.Rows)
87        RegisterDataRowEvents(row);
88    }
89
90    protected override void OnContentChanged() {
91      base.OnContentChanged();
92      invisibleSeries.Clear();
93      chart.Titles[0].Text = string.Empty;
94      chart.Series.Clear();
95      if (Content != null) {
96        chart.Titles[0].Text = Content.Name;
97        foreach (DataRow row in Content.Rows)
98          AddDataRow(row);
99      }
100    }
101
102    protected override void SetEnabledStateOfControls() {
103      base.SetEnabledStateOfControls();
104      chart.Enabled = Content != null;
105    }
106
107    private void AddDataRow(DataRow row) {
108      Series series = new Series(row.Name);
109      switch (row.VisualProperties.ChartType) {
110        case DataRowVisualProperties.DataRowChartType.Line:
111          series.ChartType = SeriesChartType.FastLine;
112          break;
113        case DataRowVisualProperties.DataRowChartType.Bars:
114          series.ChartType = SeriesChartType.Bar;
115          break;
116        case DataRowVisualProperties.DataRowChartType.Columns:
117          series.ChartType = SeriesChartType.Column;
118          break;
119        case DataRowVisualProperties.DataRowChartType.Points:
120          series.ChartType = SeriesChartType.FastPoint;
121          break;
122        default:
123          series.ChartType = SeriesChartType.FastPoint;
124          break;
125      }
126      series.YAxisType = row.VisualProperties.SecondYAxis ? AxisType.Secondary : AxisType.Primary;
127      if (row.VisualProperties.Color != Color.Empty) series.Color = row.VisualProperties.Color;
128      series.ToolTip = row.Name + " X = #INDEX, Y = #VAL";
129      FillSeriesWithRowValues(series, row);
130      chart.Series.Add(series);
131      UpdateYCursorInterval();
132    }
133
134    private void UpdateYCursorInterval() {
135      double interestingValuesRange = (from series in chart.Series
136                                       where series.Enabled
137                                       let values = (from point in series.Points
138                                                     where !point.IsEmpty
139                                                     select point.YValues[0])
140                                                     .DefaultIfEmpty(1.0)
141                                       let range = values.Max() - values.Min()
142                                       where range > 0.0
143                                       select range)
144                                       .DefaultIfEmpty(1.0)
145                                       .Min();
146
147      double digits = (int)Math.Log10(interestingValuesRange) - 3;
148      double yZoomInterval = Math.Pow(10, digits);
149      this.chart.ChartAreas[0].CursorY.Interval = yZoomInterval;
150    }
151
152    private void RemoveDataRow(DataRow row) {
153      Series series = chart.Series[row.Name];
154      chart.Series.Remove(series);
155      if (invisibleSeries.Contains(series))
156        invisibleSeries.Remove(series);
157    }
158
159    #region Content Events
160    private void RegisterDataRowEvents(DataRow row) {
161      row.NameChanged += new EventHandler(Row_NameChanged);
162      row.VisualPropertiesChanged += new EventHandler(Row_VisualPropertiesChanged);
163      valuesRowsTable.Add(row.Values, row);
164      row.Values.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded);
165      row.Values.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved);
166      row.Values.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced);
167      row.Values.ItemsMoved += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved);
168      row.Values.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset);
169    }
170    private void DeregisterDataRowEvents(DataRow row) {
171      row.Values.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded);
172      row.Values.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved);
173      row.Values.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced);
174      row.Values.ItemsMoved -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved);
175      row.Values.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset);
176      valuesRowsTable.Remove(row.Values);
177      row.VisualPropertiesChanged -= new EventHandler(Row_VisualPropertiesChanged);
178      row.NameChanged -= new EventHandler(Row_NameChanged);
179    }
180    protected override void Content_NameChanged(object sender, EventArgs e) {
181      if (InvokeRequired)
182        Invoke(new EventHandler(Content_NameChanged), sender, e);
183      else {
184        chart.Titles[0].Text = Content.Name;
185        base.Content_NameChanged(sender, e);
186      }
187    }
188    private void Rows_ItemsAdded(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
189      if (InvokeRequired)
190        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded), sender, e);
191      else {
192        foreach (DataRow row in e.Items) {
193          AddDataRow(row);
194          RegisterDataRowEvents(row);
195        }
196      }
197    }
198    private void Rows_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
199      if (InvokeRequired)
200        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved), sender, e);
201      else {
202        foreach (DataRow row in e.Items) {
203          DeregisterDataRowEvents(row);
204          RemoveDataRow(row);
205        }
206      }
207    }
208    private void Rows_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
209      if (InvokeRequired)
210        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced), sender, e);
211      else {
212        foreach (DataRow row in e.OldItems) {
213          DeregisterDataRowEvents(row);
214          RemoveDataRow(row);
215        }
216        foreach (DataRow row in e.Items) {
217          AddDataRow(row);
218          RegisterDataRowEvents(row);
219        }
220      }
221    }
222    private void Rows_CollectionReset(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
223      if (InvokeRequired)
224        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset), sender, e);
225      else {
226        foreach (DataRow row in e.OldItems) {
227          DeregisterDataRowEvents(row);
228          RemoveDataRow(row);
229        }
230        foreach (DataRow row in e.Items) {
231          AddDataRow(row);
232          RegisterDataRowEvents(row);
233        }
234      }
235    }
236    private void Row_VisualPropertiesChanged(object sender, EventArgs e) {
237      if (InvokeRequired)
238        Invoke(new EventHandler(Row_VisualPropertiesChanged), sender, e);
239      else {
240        DataRow row = (DataRow)sender;
241        switch (row.VisualProperties.ChartType) {
242          case DataRowVisualProperties.DataRowChartType.Line:
243            chart.Series[row.Name].ChartType = SeriesChartType.FastLine;
244            break;
245          case DataRowVisualProperties.DataRowChartType.Bars:
246            chart.Series[row.Name].ChartType = SeriesChartType.Bar;
247            break;
248          case DataRowVisualProperties.DataRowChartType.Columns:
249            chart.Series[row.Name].ChartType = SeriesChartType.Column;
250            break;
251          case DataRowVisualProperties.DataRowChartType.Points:
252            chart.Series[row.Name].ChartType = SeriesChartType.FastPoint;
253            break;
254          default:
255            chart.Series[row.Name].ChartType = SeriesChartType.FastPoint;
256            break;
257        }
258        chart.Series[row.Name].YAxisType = row.VisualProperties.SecondYAxis ? AxisType.Secondary : AxisType.Primary;
259        if (row.VisualProperties.Color != Color.Empty) chart.Series[row.Name].Color = row.VisualProperties.Color;
260      }
261    }
262    private void Row_NameChanged(object sender, EventArgs e) {
263      if (InvokeRequired)
264        Invoke(new EventHandler(Row_NameChanged), sender, e);
265      else {
266        DataRow row = (DataRow)sender;
267        chart.Series[row.Name].Name = row.Name;
268      }
269    }
270    private void Values_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
271      if (InvokeRequired)
272        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded), sender, e);
273      else {
274        DataRow row = null;
275        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
276        if (row != null) {
277          Series rowSeries = chart.Series[row.Name];
278          if (!invisibleSeries.Contains(rowSeries)) {
279            foreach (IndexedItem<double> item in e.Items) {
280              var value = item.Value;
281              if (IsInvalidValue(item.Value)) {
282                DataPoint point = new DataPoint();
283                point.IsEmpty = true;
284                rowSeries.Points.Insert(item.Index, point);
285              } else {
286                rowSeries.Points.InsertY(item.Index, value);
287              }
288            }
289            UpdateYCursorInterval();
290          }
291        }
292      }
293    }
294    private void Values_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
295      if (InvokeRequired)
296        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved), sender, e);
297      else {
298        DataRow row = null;
299        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
300        if (row != null) {
301          Series rowSeries = chart.Series[row.Name];
302          if (!invisibleSeries.Contains(rowSeries)) {
303            List<DataPoint> points = new List<DataPoint>();
304            foreach (IndexedItem<double> item in e.Items)
305              points.Add(rowSeries.Points[item.Index]);
306            foreach (DataPoint point in points)
307              rowSeries.Points.Remove(point);
308            UpdateYCursorInterval();
309          }
310        }
311      }
312    }
313    private void Values_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
314      if (InvokeRequired)
315        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced), sender, e);
316      else {
317        DataRow row = null;
318        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
319        if (row != null) {
320          Series rowSeries = chart.Series[row.Name];
321          if (!invisibleSeries.Contains(rowSeries)) {
322            foreach (IndexedItem<double> item in e.Items) {
323              if (IsInvalidValue(item.Value))
324                rowSeries.Points[item.Index].IsEmpty = true;
325              else {
326                rowSeries.Points[item.Index].YValues = new double[] { item.Value };
327                rowSeries.Points[item.Index].IsEmpty = false;
328              }
329            }
330            UpdateYCursorInterval();
331          }
332        }
333      }
334    }
335    private void Values_ItemsMoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
336      if (InvokeRequired)
337        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved), sender, e);
338      else {
339        DataRow row = null;
340        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
341        if (row != null) {
342          Series rowSeries = chart.Series[row.Name];
343          if (!invisibleSeries.Contains(rowSeries)) {
344            foreach (IndexedItem<double> item in e.Items) {
345              if (IsInvalidValue(item.Value))
346                rowSeries.Points[item.Index].IsEmpty = true;
347              else {
348                rowSeries.Points[item.Index].YValues = new double[] { item.Value };
349                rowSeries.Points[item.Index].IsEmpty = false;
350              }
351            }
352            UpdateYCursorInterval();
353          }
354        }
355      }
356    }
357
358    private void Values_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
359      if (InvokeRequired)
360        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset), sender, e);
361      else {
362        DataRow row = null;
363        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
364        if (row != null) {
365          Series rowSeries = chart.Series[row.Name];
366          if (!invisibleSeries.Contains(rowSeries)) {
367            rowSeries.Points.Clear();
368            foreach (IndexedItem<double> item in e.Items) {
369              if (IsInvalidValue(item.Value))
370                rowSeries.Points[item.Index].IsEmpty = true;
371              else {
372                rowSeries.Points[item.Index].YValues = new double[] { item.Value };
373                rowSeries.Points[item.Index].IsEmpty = false;
374              }
375            }
376          }
377          UpdateYCursorInterval();
378        }
379      }
380    }
381    #endregion
382
383    #region Chart Events
384    private void chart_MouseDown(object sender, MouseEventArgs e) {
385      HitTestResult result = chart.HitTest(e.X, e.Y);
386      if (result.ChartElementType == ChartElementType.LegendItem) {
387        ToggleSeriesVisible(result.Series);
388      }
389    }
390
391    private void ToggleSeriesVisible(Series series) {
392      if (!invisibleSeries.Contains(series)) {
393        series.Points.Clear();
394        invisibleSeries.Add(series);
395      } else {
396        invisibleSeries.Remove(series);
397        if (Content != null) {
398
399          var row = (from r in Content.Rows
400                     where r.Name == series.Name
401                     select r).Single();
402          FillSeriesWithRowValues(series, row);
403          this.chart.Legends[series.Legend].ForeColor = Color.Black;
404          UpdateYCursorInterval();
405        }
406      }
407    }
408
409    private void FillSeriesWithRowValues(Series series, DataRow row) {
410      for (int i = 0; i < row.Values.Count; i++) {
411        var value = row.Values[i];
412        if (IsInvalidValue(value)) {
413          DataPoint point = new DataPoint();
414          point.IsEmpty = true;
415          series.Points.Add(point);
416        } else {
417          series.Points.Add(value);
418        }
419      }
420    }
421
422    private void chart_MouseMove(object sender, MouseEventArgs e) {
423      HitTestResult result = chart.HitTest(e.X, e.Y);
424      if (result.ChartElementType == ChartElementType.LegendItem)
425        this.Cursor = Cursors.Hand;
426      else
427        this.Cursor = Cursors.Default;
428    }
429    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
430      foreach (LegendItem legendItem in e.LegendItems) {
431        var series = chart.Series[legendItem.SeriesName];
432        if (series != null) {
433          bool seriesIsInvisible = invisibleSeries.Contains(series);
434          foreach (LegendCell cell in legendItem.Cells) {
435            cell.ForeColor = seriesIsInvisible ? Color.Gray : Color.Black;
436          }
437        }
438      }
439    }
440
441    #endregion
442
443    private bool IsInvalidValue(double x) {
444      return double.IsNaN(x) || x < (double)decimal.MinValue || x > (double)decimal.MaxValue;
445    }
446
447    private void exportChartToolStripMenuItem_Click(object sender, EventArgs e) {
448      if (saveFileDialog.ShowDialog() == DialogResult.OK) {
449        chart.SaveImage(saveFileDialog.FileName, ChartImageFormat.EmfDual);
450      }
451    }
452  }
453}
Note: See TracBrowser for help on using the repository browser.