Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 5656 was 5445, checked in by swagner, 14 years ago

Updated year of copyrights (#1406)

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