Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 4613 was 4142, checked in by gkronber, 14 years ago

Added export to file functionality for the DataTableView. #1127

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