Free cookie consent management tool by TermsFeed Policy Generator

source: tags/3.3.0/HeuristicLab.Analysis.Views/3.3/DataTableView.cs @ 12752

Last change on this file since 12752 was 3773, checked in by gkronber, 14 years ago

Added display of data-point index to tooltip in DataTableView. #893

File size: 16.5 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.Linq;
24using System.Collections.Generic;
25using System.Windows.Forms.DataVisualization.Charting;
26using HeuristicLab.Collections;
27using HeuristicLab.Core.Views;
28using HeuristicLab.MainForm;
29using System.Windows.Forms;
30using System.Drawing;
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    }
59
60    /// <summary>
61    /// Removes the eventhandlers from the underlying <see cref="Variable"/>.
62    /// </summary>
63    /// <remarks>Calls <see cref="ViewBase.RemoveItemEvents"/> of base class <see cref="ViewBase"/>.</remarks>
64    protected override void DeregisterContentEvents() {
65      foreach (DataRow row in Content.Rows)
66        DeregisterDataRowEvents(row);
67      Content.Rows.ItemsAdded -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded);
68      Content.Rows.ItemsRemoved -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved);
69      Content.Rows.ItemsReplaced -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced);
70      Content.Rows.CollectionReset -= new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset);
71      base.DeregisterContentEvents();
72    }
73
74    /// <summary>
75    /// Adds eventhandlers to the underlying <see cref="Variable"/>.
76    /// </summary>
77    /// <remarks>Calls <see cref="ViewBase.AddItemEvents"/> of base class <see cref="ViewBase"/>.</remarks>
78    protected override void RegisterContentEvents() {
79      base.RegisterContentEvents();
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);
84      foreach (DataRow row in Content.Rows)
85        RegisterDataRowEvents(row);
86    }
87
88    protected override void OnContentChanged() {
89      base.OnContentChanged();
90      invisibleSeries.Clear();
91      chart.Titles.Clear();
92      chart.Series.Clear();
93      if (Content != null) {
94        chart.Titles.Add(new Title(Content.Name, Docking.Top));
95        foreach (DataRow row in Content.Rows)
96          AddDataRow(row);
97      }
98      SetEnabledStateOfControls();
99    }
100
101    protected override void OnReadOnlyChanged() {
102      base.OnReadOnlyChanged();
103      SetEnabledStateOfControls();
104    }
105    private void SetEnabledStateOfControls() {
106      chart.Enabled = Content != null;
107    }
108
109    private void AddDataRow(DataRow row) {
110      Series series = new Series(row.Name);
111      series.ChartType = SeriesChartType.FastLine;
112      series.ToolTip = row.Name + " X = #INDEX, Y = #VAL";
113      FillSeriesWithRowValues(series, row);
114      chart.Series.Add(series);
115      UpdateYCursorInterval();
116    }
117
118    private void UpdateYCursorInterval() {
119      double interestingValuesRange = (from series in chart.Series
120                                       where series.Enabled
121                                       let values = (from point in series.Points
122                                                     where !point.IsEmpty
123                                                     select point.YValues[0])
124                                                     .DefaultIfEmpty(1.0)
125                                       let range = values.Max() - values.Min()
126                                       where range > 0.0
127                                       select range)
128                                       .DefaultIfEmpty(1.0)
129                                       .Min();
130
131      double digits = (int)Math.Log10(interestingValuesRange) - 3;
132      double yZoomInterval = Math.Pow(10, digits);
133      this.chart.ChartAreas[0].CursorY.Interval = yZoomInterval;
134    }
135
136    private void RemoveDataRow(DataRow row) {
137      Series series = chart.Series[row.Name];
138      chart.Series.Remove(series);
139      if (invisibleSeries.Contains(series))
140        invisibleSeries.Remove(series);
141    }
142
143    #region Content Events
144    private void RegisterDataRowEvents(DataRow row) {
145      row.NameChanged += new EventHandler(Row_NameChanged);
146      valuesRowsTable.Add(row.Values, row);
147      row.Values.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded);
148      row.Values.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved);
149      row.Values.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced);
150      row.Values.ItemsMoved += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved);
151      row.Values.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset);
152    }
153    private void DeregisterDataRowEvents(DataRow row) {
154      row.Values.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded);
155      row.Values.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved);
156      row.Values.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced);
157      row.Values.ItemsMoved -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved);
158      row.Values.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset);
159      valuesRowsTable.Remove(row.Values);
160      row.NameChanged -= new EventHandler(Row_NameChanged);
161    }
162    protected override void Content_NameChanged(object sender, EventArgs e) {
163      if (InvokeRequired)
164        Invoke(new EventHandler(Content_NameChanged), sender, e);
165      else {
166        chart.Titles[0].Text = Content.Name;
167        base.Content_NameChanged(sender, e);
168      }
169    }
170    private void Rows_ItemsAdded(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
171      if (InvokeRequired)
172        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded), sender, e);
173      else {
174        foreach (DataRow row in e.Items) {
175          AddDataRow(row);
176          RegisterDataRowEvents(row);
177        }
178      }
179    }
180    private void Rows_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
181      if (InvokeRequired)
182        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved), sender, e);
183      else {
184        foreach (DataRow row in e.Items) {
185          DeregisterDataRowEvents(row);
186          RemoveDataRow(row);
187        }
188      }
189    }
190    private void Rows_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
191      if (InvokeRequired)
192        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced), sender, e);
193      else {
194        foreach (DataRow row in e.OldItems) {
195          DeregisterDataRowEvents(row);
196          RemoveDataRow(row);
197        }
198        foreach (DataRow row in e.Items) {
199          AddDataRow(row);
200          RegisterDataRowEvents(row);
201        }
202      }
203    }
204    private void Rows_CollectionReset(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
205      if (InvokeRequired)
206        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset), sender, e);
207      else {
208        foreach (DataRow row in e.OldItems) {
209          DeregisterDataRowEvents(row);
210          RemoveDataRow(row);
211        }
212        foreach (DataRow row in e.Items) {
213          AddDataRow(row);
214          RegisterDataRowEvents(row);
215        }
216      }
217    }
218    private void Row_NameChanged(object sender, EventArgs e) {
219      if (InvokeRequired)
220        Invoke(new EventHandler(Row_NameChanged), sender, e);
221      else {
222        DataRow row = (DataRow)sender;
223        chart.Series[row.Name].Name = row.Name;
224      }
225    }
226    private void Values_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
227      if (InvokeRequired)
228        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded), sender, e);
229      else {
230        DataRow row = null;
231        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
232        if (row != null) {
233          Series rowSeries = chart.Series[row.Name];
234          if (!invisibleSeries.Contains(rowSeries)) {
235            foreach (IndexedItem<double> item in e.Items) {
236              var value = item.Value;
237              if (IsInvalidValue(item.Value)) {
238                DataPoint point = new DataPoint();
239                point.IsEmpty = true;
240                rowSeries.Points.Insert(item.Index, point);
241              } else {
242                rowSeries.Points.InsertY(item.Index, value);
243              }
244            }
245            UpdateYCursorInterval();
246          }
247        }
248      }
249    }
250    private void Values_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
251      if (InvokeRequired)
252        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved), sender, e);
253      else {
254        DataRow row = null;
255        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
256        if (row != null) {
257          Series rowSeries = chart.Series[row.Name];
258          if (!invisibleSeries.Contains(rowSeries)) {
259            List<DataPoint> points = new List<DataPoint>();
260            foreach (IndexedItem<double> item in e.Items)
261              points.Add(rowSeries.Points[item.Index]);
262            foreach (DataPoint point in points)
263              rowSeries.Points.Remove(point);
264            UpdateYCursorInterval();
265          }
266        }
267      }
268    }
269    private void Values_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
270      if (InvokeRequired)
271        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced), sender, e);
272      else {
273        DataRow row = null;
274        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
275        if (row != null) {
276          Series rowSeries = chart.Series[row.Name];
277          if (!invisibleSeries.Contains(rowSeries)) {
278            foreach (IndexedItem<double> item in e.Items) {
279              if (IsInvalidValue(item.Value))
280                rowSeries.Points[item.Index].IsEmpty = true;
281              else {
282                rowSeries.Points[item.Index].YValues = new double[] { item.Value };
283                rowSeries.Points[item.Index].IsEmpty = false;
284              }
285            }
286            UpdateYCursorInterval();
287          }
288        }
289      }
290    }
291    private void Values_ItemsMoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
292      if (InvokeRequired)
293        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved), sender, e);
294      else {
295        DataRow row = null;
296        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
297        if (row != null) {
298          Series rowSeries = chart.Series[row.Name];
299          if (!invisibleSeries.Contains(rowSeries)) {
300            foreach (IndexedItem<double> item in e.Items) {
301              if (IsInvalidValue(item.Value))
302                rowSeries.Points[item.Index].IsEmpty = true;
303              else {
304                rowSeries.Points[item.Index].YValues = new double[] { item.Value };
305                rowSeries.Points[item.Index].IsEmpty = false;
306              }
307            }
308            UpdateYCursorInterval();
309          }
310        }
311      }
312    }
313
314    private void Values_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
315      if (InvokeRequired)
316        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset), sender, e);
317      else {
318        DataRow row = null;
319        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
320        if (row != null) {
321          Series rowSeries = chart.Series[row.Name];
322          if (!invisibleSeries.Contains(rowSeries)) {
323            rowSeries.Points.Clear();
324            foreach (IndexedItem<double> item in e.Items) {
325              if (IsInvalidValue(item.Value))
326                rowSeries.Points[item.Index].IsEmpty = true;
327              else {
328                rowSeries.Points[item.Index].YValues = new double[] { item.Value };
329                rowSeries.Points[item.Index].IsEmpty = false;
330              }
331            }
332          }
333          UpdateYCursorInterval();
334        }
335      }
336    }
337    #endregion
338    #region chart events
339    private void chart_MouseDown(object sender, MouseEventArgs e) {
340      HitTestResult result = chart.HitTest(e.X, e.Y);
341      if (result.ChartElementType == ChartElementType.LegendItem) {
342        ToggleSeriesVisible(result.Series);
343      }
344    }
345
346    private void ToggleSeriesVisible(Series series) {
347      if (!invisibleSeries.Contains(series)) {
348        series.Points.Clear();
349        invisibleSeries.Add(series);
350      } else {
351        invisibleSeries.Remove(series);
352        if (Content != null) {
353
354          var row = (from r in Content.Rows
355                     where r.Name == series.Name
356                     select r).Single();
357          FillSeriesWithRowValues(series, row);
358          this.chart.Legends[series.Legend].ForeColor = Color.Black;
359          UpdateYCursorInterval();
360        }
361      }
362    }
363
364    private void FillSeriesWithRowValues(Series series, DataRow row) {
365      for (int i = 0; i < row.Values.Count; i++) {
366        var value = row.Values[i];
367        if (IsInvalidValue(value)) {
368          DataPoint point = new DataPoint();
369          point.IsEmpty = true;
370          series.Points.Add(point);
371        } else {
372          series.Points.Add(value);
373        }
374      }
375    }
376
377    private void chart_MouseMove(object sender, MouseEventArgs e) {
378      HitTestResult result = chart.HitTest(e.X, e.Y);
379      if (result.ChartElementType == ChartElementType.LegendItem)
380        this.Cursor = Cursors.Hand;
381      else
382        this.Cursor = Cursors.Default;
383    }
384    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
385      foreach (LegendItem legendItem in e.LegendItems) {
386        var series = chart.Series[legendItem.SeriesName];
387        if (series != null) {
388          bool seriesIsInvisible = invisibleSeries.Contains(series);
389          foreach (LegendCell cell in legendItem.Cells) {
390            cell.ForeColor = seriesIsInvisible ? Color.Gray : Color.Black;
391          }
392        }
393      }
394    }
395
396    #endregion
397
398    private bool IsInvalidValue(double x) {
399      return double.IsNaN(x) || x < (double)decimal.MinValue || x > (double)decimal.MaxValue;
400    }
401  }
402}
Note: See TracBrowser for help on using the repository browser.