Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 3724 was 3703, checked in by gkronber, 15 years ago

Adjust zoom interval in DataTableView based on range of displayed values. Added functionality to hide and show series. #999

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