Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 4870 was 4870, checked in by swagner, 13 years ago

Added axis labels for data tables (#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.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    private void AddDataRow(DataRow row) {
116      Series series = new Series(row.Name);
117      switch (row.VisualProperties.ChartType) {
118        case DataRowVisualProperties.DataRowChartType.Line:
119          series.ChartType = SeriesChartType.FastLine;
120          break;
121        case DataRowVisualProperties.DataRowChartType.Bars:
122          series.ChartType = SeriesChartType.Bar;
123          break;
124        case DataRowVisualProperties.DataRowChartType.Columns:
125          series.ChartType = SeriesChartType.Column;
126          break;
127        case DataRowVisualProperties.DataRowChartType.Points:
128          series.ChartType = SeriesChartType.FastPoint;
129          break;
130        default:
131          series.ChartType = SeriesChartType.FastPoint;
132          break;
133      }
134      series.YAxisType = row.VisualProperties.SecondYAxis ? AxisType.Secondary : AxisType.Primary;
135      if (row.VisualProperties.Color != Color.Empty) series.Color = row.VisualProperties.Color;
136      series.ToolTip = row.Name + " X = #INDEX, Y = #VAL";
137      FillSeriesWithRowValues(series, row);
138      chart.Series.Add(series);
139      chart.ChartAreas[0].RecalculateAxesScale();
140      UpdateYCursorInterval();
141    }
142
143    private void UpdateYCursorInterval() {
144      double interestingValuesRange = (from series in chart.Series
145                                       where series.Enabled
146                                       let values = (from point in series.Points
147                                                     where !point.IsEmpty
148                                                     select point.YValues[0])
149                                                     .DefaultIfEmpty(1.0)
150                                       let range = values.Max() - values.Min()
151                                       where range > 0.0
152                                       select range)
153                                       .DefaultIfEmpty(1.0)
154                                       .Min();
155
156      double digits = (int)Math.Log10(interestingValuesRange) - 3;
157      double yZoomInterval = Math.Pow(10, digits);
158      this.chart.ChartAreas[0].CursorY.Interval = yZoomInterval;
159    }
160
161    private void RemoveDataRow(DataRow row) {
162      Series series = chart.Series[row.Name];
163      chart.Series.Remove(series);
164      if (invisibleSeries.Contains(series))
165        invisibleSeries.Remove(series);
166      chart.ChartAreas[0].RecalculateAxesScale();
167    }
168
169    #region Content Events
170    private void RegisterDataRowEvents(DataRow row) {
171      row.NameChanged += new EventHandler(Row_NameChanged);
172      row.VisualPropertiesChanged += new EventHandler(Row_VisualPropertiesChanged);
173      valuesRowsTable.Add(row.Values, row);
174      row.Values.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded);
175      row.Values.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved);
176      row.Values.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced);
177      row.Values.ItemsMoved += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved);
178      row.Values.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset);
179    }
180    private void DeregisterDataRowEvents(DataRow row) {
181      row.Values.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded);
182      row.Values.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved);
183      row.Values.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced);
184      row.Values.ItemsMoved -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved);
185      row.Values.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset);
186      valuesRowsTable.Remove(row.Values);
187      row.VisualPropertiesChanged -= new EventHandler(Row_VisualPropertiesChanged);
188      row.NameChanged -= new EventHandler(Row_NameChanged);
189    }
190    protected override void Content_NameChanged(object sender, EventArgs e) {
191      if (InvokeRequired)
192        Invoke(new EventHandler(Content_NameChanged), sender, e);
193      else {
194        chart.Titles[0].Text = Content.Name;
195        base.Content_NameChanged(sender, e);
196      }
197    }
198    private void Content_VisualPropertiesChanged(object sender, EventArgs e) {
199      if (InvokeRequired)
200        Invoke(new EventHandler(Content_VisualPropertiesChanged), sender, e);
201      else {
202        chart.ChartAreas[0].AxisX.Title = Content.VisualProperties.XAxisTitle;
203        chart.ChartAreas[0].AxisY.Title = Content.VisualProperties.YAxisTitle;
204        chart.ChartAreas[0].AxisY2.Title = Content.VisualProperties.SecondYAxisTitle;
205      }
206    }
207    private void Rows_ItemsAdded(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
208      if (InvokeRequired)
209        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded), sender, e);
210      else {
211        foreach (DataRow row in e.Items) {
212          AddDataRow(row);
213          RegisterDataRowEvents(row);
214        }
215      }
216    }
217    private void Rows_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
218      if (InvokeRequired)
219        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved), sender, e);
220      else {
221        foreach (DataRow row in e.Items) {
222          DeregisterDataRowEvents(row);
223          RemoveDataRow(row);
224        }
225      }
226    }
227    private void Rows_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
228      if (InvokeRequired)
229        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced), sender, e);
230      else {
231        foreach (DataRow row in e.OldItems) {
232          DeregisterDataRowEvents(row);
233          RemoveDataRow(row);
234        }
235        foreach (DataRow row in e.Items) {
236          AddDataRow(row);
237          RegisterDataRowEvents(row);
238        }
239      }
240    }
241    private void Rows_CollectionReset(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
242      if (InvokeRequired)
243        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset), sender, e);
244      else {
245        foreach (DataRow row in e.OldItems) {
246          DeregisterDataRowEvents(row);
247          RemoveDataRow(row);
248        }
249        foreach (DataRow row in e.Items) {
250          AddDataRow(row);
251          RegisterDataRowEvents(row);
252        }
253      }
254    }
255    private void Row_VisualPropertiesChanged(object sender, EventArgs e) {
256      if (InvokeRequired)
257        Invoke(new EventHandler(Row_VisualPropertiesChanged), sender, e);
258      else {
259        DataRow row = (DataRow)sender;
260        switch (row.VisualProperties.ChartType) {
261          case DataRowVisualProperties.DataRowChartType.Line:
262            chart.Series[row.Name].ChartType = SeriesChartType.FastLine;
263            break;
264          case DataRowVisualProperties.DataRowChartType.Bars:
265            chart.Series[row.Name].ChartType = SeriesChartType.Bar;
266            break;
267          case DataRowVisualProperties.DataRowChartType.Columns:
268            chart.Series[row.Name].ChartType = SeriesChartType.Column;
269            break;
270          case DataRowVisualProperties.DataRowChartType.Points:
271            chart.Series[row.Name].ChartType = SeriesChartType.FastPoint;
272            break;
273          default:
274            chart.Series[row.Name].ChartType = SeriesChartType.FastPoint;
275            break;
276        }
277        chart.Series[row.Name].YAxisType = row.VisualProperties.SecondYAxis ? AxisType.Secondary : AxisType.Primary;
278        if (row.VisualProperties.Color != Color.Empty) chart.Series[row.Name].Color = row.VisualProperties.Color;
279        chart.ChartAreas[0].RecalculateAxesScale();
280      }
281    }
282    private void Row_NameChanged(object sender, EventArgs e) {
283      if (InvokeRequired)
284        Invoke(new EventHandler(Row_NameChanged), sender, e);
285      else {
286        DataRow row = (DataRow)sender;
287        chart.Series[row.Name].Name = row.Name;
288      }
289    }
290    private void Values_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
291      if (InvokeRequired)
292        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded), sender, e);
293      else {
294        DataRow row = null;
295        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
296        if (row != null) {
297          Series rowSeries = chart.Series[row.Name];
298          if (!invisibleSeries.Contains(rowSeries)) {
299            rowSeries.Points.Clear();
300            FillSeriesWithRowValues(rowSeries, row);
301            UpdateYCursorInterval();
302          }
303        }
304      }
305    }
306    private void Values_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
307      if (InvokeRequired)
308        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved), sender, e);
309      else {
310        DataRow row = null;
311        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
312        if (row != null) {
313          Series rowSeries = chart.Series[row.Name];
314          if (!invisibleSeries.Contains(rowSeries)) {
315            rowSeries.Points.Clear();
316            FillSeriesWithRowValues(rowSeries, row);
317            UpdateYCursorInterval();
318          }
319        }
320      }
321    }
322    private void Values_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
323      if (InvokeRequired)
324        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced), sender, e);
325      else {
326        DataRow row = null;
327        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
328        if (row != null) {
329          Series rowSeries = chart.Series[row.Name];
330          if (!invisibleSeries.Contains(rowSeries)) {
331            foreach (IndexedItem<double> item in e.Items) {
332              if (IsInvalidValue(item.Value))
333                rowSeries.Points[item.Index].IsEmpty = true;
334              else {
335                rowSeries.Points[item.Index].YValues = new double[] { item.Value };
336                rowSeries.Points[item.Index].IsEmpty = false;
337              }
338            }
339            UpdateYCursorInterval();
340          }
341        }
342      }
343    }
344    private void Values_ItemsMoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
345      if (InvokeRequired)
346        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved), sender, e);
347      else {
348        DataRow row = null;
349        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
350        if (row != null) {
351          Series rowSeries = chart.Series[row.Name];
352          if (!invisibleSeries.Contains(rowSeries)) {
353            rowSeries.Points.Clear();
354            FillSeriesWithRowValues(rowSeries, row);
355            UpdateYCursorInterval();
356          }
357        }
358      }
359    }
360
361    private void Values_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
362      if (InvokeRequired)
363        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset), sender, e);
364      else {
365        DataRow row = null;
366        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
367        if (row != null) {
368          Series rowSeries = chart.Series[row.Name];
369          if (!invisibleSeries.Contains(rowSeries)) {
370            rowSeries.Points.Clear();
371            FillSeriesWithRowValues(rowSeries, row);
372            UpdateYCursorInterval();
373          }
374        }
375      }
376    }
377    #endregion
378
379    #region Chart Events
380    private void chart_MouseDown(object sender, MouseEventArgs e) {
381      HitTestResult result = chart.HitTest(e.X, e.Y);
382      if (result.ChartElementType == ChartElementType.LegendItem) {
383        ToggleSeriesVisible(result.Series);
384      }
385    }
386
387    private void ToggleSeriesVisible(Series series) {
388      if (!invisibleSeries.Contains(series)) {
389        series.Points.Clear();
390        invisibleSeries.Add(series);
391      } else {
392        invisibleSeries.Remove(series);
393        if (Content != null) {
394
395          var row = (from r in Content.Rows
396                     where r.Name == series.Name
397                     select r).Single();
398          FillSeriesWithRowValues(series, row);
399          this.chart.Legends[series.Legend].ForeColor = Color.Black;
400          UpdateYCursorInterval();
401        }
402      }
403    }
404
405    private void FillSeriesWithRowValues(Series series, DataRow row) {
406      for (int i = 0; i < row.Values.Count; i++) {
407        var value = row.Values[i];
408        DataPoint point = new DataPoint();
409        point.XValue = row.VisualProperties.StartIndexZero ? i : i + 1;
410        if (IsInvalidValue(value))
411          point.IsEmpty = true;
412        else
413          point.YValues = new double[] { value };
414        series.Points.Add(point);
415      }
416    }
417
418    private void chart_MouseMove(object sender, MouseEventArgs e) {
419      HitTestResult result = chart.HitTest(e.X, e.Y);
420      if (result.ChartElementType == ChartElementType.LegendItem)
421        this.Cursor = Cursors.Hand;
422      else
423        this.Cursor = Cursors.Default;
424    }
425    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
426      foreach (LegendItem legendItem in e.LegendItems) {
427        var series = chart.Series[legendItem.SeriesName];
428        if (series != null) {
429          bool seriesIsInvisible = invisibleSeries.Contains(series);
430          foreach (LegendCell cell in legendItem.Cells) {
431            cell.ForeColor = seriesIsInvisible ? Color.Gray : Color.Black;
432          }
433        }
434      }
435    }
436    #endregion
437
438    private bool IsInvalidValue(double x) {
439      return double.IsNaN(x) || x < (double)decimal.MinValue || x > (double)decimal.MaxValue;
440    }
441  }
442}
Note: See TracBrowser for help on using the repository browser.