Free cookie consent management tool by TermsFeed Policy Generator

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

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

Worked on visual appearance of data rows (#925)

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