Free cookie consent management tool by TermsFeed Policy Generator

source: branches/PerformanceComparison/HeuristicLab.Analysis.Views/3.3/IndexedDataTableView.cs @ 12826

Last change on this file since 12826 was 12826, checked in by abeham, 9 years ago

#2431: omitting empty series

File size: 28.5 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2015 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  [View("IndexedDataTable View")]
34  [Content(typeof(IndexedDataTable<>), true)]
35  public partial class IndexedDataTableView<T> : NamedItemView {
36    protected List<Series> invisibleSeries;
37    protected Dictionary<IObservableList<Tuple<T, double>>, IndexedDataRow<T>> valuesRowsTable;
38    /// <summary>
39    /// Gets or sets the variable to represent visually.
40    /// </summary>
41    /// <remarks>Uses property <see cref="ViewBase.Item"/> of base class <see cref="ViewBase"/>.
42    /// No own data storage present.</remarks>
43    public new IndexedDataTable<T> Content {
44      get { return (IndexedDataTable<T>)base.Content; }
45      set { base.Content = value; }
46    }
47
48    /// <summary>
49    /// Initializes a new instance of <see cref="VariableView"/> with caption "Variable".
50    /// </summary>
51    public IndexedDataTableView() {
52      InitializeComponent();
53      valuesRowsTable = new Dictionary<IObservableList<Tuple<T, double>>, IndexedDataRow<T>>();
54      invisibleSeries = new List<Series>();
55      chart.CustomizeAllChartAreas();
56      chart.ChartAreas[0].CursorX.Interval = 1;
57      chart.SuppressExceptions = true;
58    }
59
60    #region Event Handler Registration
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 (var row in Content.Rows)
67        DeregisterDataRowEvents(row);
68      Content.VisualPropertiesChanged -= new EventHandler(Content_VisualPropertiesChanged);
69      Content.Rows.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_ItemsAdded);
70      Content.Rows.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_ItemsRemoved);
71      Content.Rows.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_ItemsReplaced);
72      Content.Rows.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(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.VisualPropertiesChanged += new EventHandler(Content_VisualPropertiesChanged);
83      Content.Rows.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_ItemsAdded);
84      Content.Rows.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_ItemsRemoved);
85      Content.Rows.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_ItemsReplaced);
86      Content.Rows.CollectionReset += new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_CollectionReset);
87      foreach (var row in Content.Rows)
88        RegisterDataRowEvents(row);
89    }
90
91    /// <summary>
92    /// Automatically called for every existing data row and whenever a data row is added
93    /// to the data table. Do not call this method directly.
94    /// </summary>
95    /// <param name="row">The DataRow that was added.</param>
96    protected virtual void RegisterDataRowEvents(IndexedDataRow<T> row) {
97      row.NameChanged += new EventHandler(Row_NameChanged);
98      row.VisualPropertiesChanged += new EventHandler(Row_VisualPropertiesChanged);
99      valuesRowsTable.Add(row.Values, row);
100      row.Values.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsAdded);
101      row.Values.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsRemoved);
102      row.Values.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsReplaced);
103      row.Values.ItemsMoved += new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsMoved);
104      row.Values.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_CollectionReset);
105    }
106
107    /// <summary>
108    /// Automatically called for every data row that is removed from the DataTable. Do
109    /// not directly call this method.
110    /// </summary>
111    /// <param name="row">The DataRow that was removed.</param>
112    protected virtual void DeregisterDataRowEvents(IndexedDataRow<T> row) {
113      row.Values.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsAdded);
114      row.Values.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsRemoved);
115      row.Values.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsReplaced);
116      row.Values.ItemsMoved -= new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsMoved);
117      row.Values.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_CollectionReset);
118      valuesRowsTable.Remove(row.Values);
119      row.VisualPropertiesChanged -= new EventHandler(Row_VisualPropertiesChanged);
120      row.NameChanged -= new EventHandler(Row_NameChanged);
121    }
122    #endregion
123
124    protected override void OnContentChanged() {
125      base.OnContentChanged();
126      invisibleSeries.Clear();
127      chart.Titles[0].Text = string.Empty;
128      chart.ChartAreas[0].AxisX.Title = string.Empty;
129      chart.ChartAreas[0].AxisY.Title = string.Empty;
130      chart.ChartAreas[0].AxisY2.Title = string.Empty;
131      chart.ChartAreas[0].AxisX.IsLogarithmic = false;
132      chart.ChartAreas[0].AxisX2.IsLogarithmic = false;
133      chart.ChartAreas[0].AxisY.IsLogarithmic = false;
134      chart.ChartAreas[0].AxisY2.IsLogarithmic = false;
135      chart.Series.Clear();
136      if (Content != null) {
137        chart.Titles[0].Text = Content.Name;
138        foreach (var row in Content.Rows)
139          AddDataRow(row);
140        ConfigureChartArea(chart.ChartAreas[0]);
141        RecalculateAxesScale(chart.ChartAreas[0]);
142      }
143    }
144
145    protected override void SetEnabledStateOfControls() {
146      base.SetEnabledStateOfControls();
147      chart.Enabled = Content != null;
148    }
149
150    /// <summary>
151    /// Add the DataRow as a series to the chart.
152    /// </summary>
153    /// <param name="row">DataRow to add as series to the chart.</param>
154    protected virtual void AddDataRow(IndexedDataRow<T> row) {
155      if (row.Values.Count == 0) return;
156
157      Series series = new Series(row.Name);
158      if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;
159      else series.LegendText = row.Name;
160      ConfigureSeries(series, row);
161      FillSeriesWithRowValues(series, row);
162
163      chart.Series.Add(series);
164      ConfigureChartArea(chart.ChartAreas[0]);
165      RecalculateAxesScale(chart.ChartAreas[0]);
166      UpdateYCursorInterval();
167    }
168
169    private void ConfigureSeries(Series series, IndexedDataRow<T> row) {
170      RemoveCustomPropertyIfExists(series, "PointWidth");
171      series.BorderWidth = 1;
172      series.BorderDashStyle = ChartDashStyle.Solid;
173      series.BorderColor = Color.Empty;
174
175      if (row.VisualProperties.Color != Color.Empty)
176        series.Color = row.VisualProperties.Color;
177      else series.Color = Color.Empty;
178      series.IsVisibleInLegend = row.VisualProperties.IsVisibleInLegend;
179
180      switch (row.VisualProperties.ChartType) {
181        case DataRowVisualProperties.DataRowChartType.Line:
182          series.ChartType = SeriesChartType.FastLine;
183          series.BorderWidth = row.VisualProperties.LineWidth;
184          series.BorderDashStyle = ConvertLineStyle(row.VisualProperties.LineStyle);
185          break;
186        case DataRowVisualProperties.DataRowChartType.Bars:
187          // Bar is incompatible with anything but Bar and StackedBar*
188          if (!chart.Series.Any(x => x.ChartType != SeriesChartType.Bar && x.ChartType != SeriesChartType.StackedBar && x.ChartType != SeriesChartType.StackedBar100)) {
189            series.ChartType = SeriesChartType.Bar;
190            chart.ChartAreas[0].AxisX.Interval = 1;
191          } else {
192            series.ChartType = SeriesChartType.FastPoint; //default
193            row.VisualProperties.ChartType = DataRowVisualProperties.DataRowChartType.Points;
194          }
195          break;
196        case DataRowVisualProperties.DataRowChartType.Columns:
197          series.ChartType = SeriesChartType.Column;
198          break;
199        case DataRowVisualProperties.DataRowChartType.Points:
200          series.ChartType = SeriesChartType.FastPoint;
201          break;
202        case DataRowVisualProperties.DataRowChartType.StepLine:
203          series.ChartType = SeriesChartType.StepLine;
204          series.BorderWidth = row.VisualProperties.LineWidth;
205          series.BorderDashStyle = ConvertLineStyle(row.VisualProperties.LineStyle);
206          break;
207        default:
208          series.ChartType = SeriesChartType.FastPoint;
209          break;
210      }
211      series.YAxisType = row.VisualProperties.SecondYAxis ? AxisType.Secondary : AxisType.Primary;
212      series.XAxisType = row.VisualProperties.SecondXAxis ? AxisType.Secondary : AxisType.Primary;
213      if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;
214      else series.LegendText = row.Name;
215      series.ToolTip = series.LegendText + " X = #VALX, Y = #VALY";
216    }
217
218    private void ConfigureChartArea(ChartArea area) {
219      if (Content.VisualProperties.TitleFont != null) chart.Titles[0].Font = Content.VisualProperties.TitleFont;
220      if (!Content.VisualProperties.TitleColor.IsEmpty) chart.Titles[0].ForeColor = Content.VisualProperties.TitleColor;
221
222      if (Content.VisualProperties.AxisTitleFont != null) area.AxisX.TitleFont = Content.VisualProperties.AxisTitleFont;
223      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisX.TitleForeColor = Content.VisualProperties.AxisTitleColor;
224      area.AxisX.Title = Content.VisualProperties.XAxisTitle;
225
226      if (Content.VisualProperties.AxisTitleFont != null) area.AxisX2.TitleFont = Content.VisualProperties.AxisTitleFont;
227      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisX2.TitleForeColor = Content.VisualProperties.AxisTitleColor;
228      area.AxisX2.Title = Content.VisualProperties.SecondXAxisTitle;
229
230      if (Content.VisualProperties.AxisTitleFont != null) area.AxisY.TitleFont = Content.VisualProperties.AxisTitleFont;
231      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisY.TitleForeColor = Content.VisualProperties.AxisTitleColor;
232      area.AxisY.Title = Content.VisualProperties.YAxisTitle;
233
234      if (Content.VisualProperties.AxisTitleFont != null) area.AxisY2.TitleFont = Content.VisualProperties.AxisTitleFont;
235      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisY2.TitleForeColor = Content.VisualProperties.AxisTitleColor;
236      area.AxisY2.Title = Content.VisualProperties.SecondYAxisTitle;
237
238      if (typeof(T).Equals(typeof(DateTime))) {
239        area.AxisX.IntervalType = DateTimeIntervalType.Hours;
240        area.AxisX.LabelStyle.Format = "dd.MM.yyyy HH:mm";
241        area.AxisX.IntervalAutoMode = IntervalAutoMode.VariableCount;
242      }
243    }
244
245    private void RecalculateAxesScale(ChartArea area) {
246      // Reset the axes bounds so that RecalculateAxesScale() will assign new bounds
247      foreach (Axis a in area.Axes) {
248        a.Minimum = double.NaN;
249        a.Maximum = double.NaN;
250      }
251
252      if (chart.Series.Any(x => x.Enabled && x.XAxisType == AxisType.Primary && (x.Points.Count == 0 || x.Points.Any(y => y.XValue <= 0))))
253        area.AxisX.IsLogarithmic = false;
254      else area.AxisX.IsLogarithmic = Content.VisualProperties.XAxisLogScale;
255      if (chart.Series.Any(x => x.Enabled && x.XAxisType == AxisType.Secondary && (x.Points.Count == 0 || x.Points.Any(y => y.XValue <= 0))))
256        area.AxisX2.IsLogarithmic = false;
257      else area.AxisX2.IsLogarithmic = Content.VisualProperties.SecondXAxisLogScale;
258
259      if (chart.Series.Any(x => x.Enabled && x.YAxisType == AxisType.Primary && (x.Points.Count == 0 || x.Points.Any(y => y.YValues.Min() <= 0))))
260        area.AxisY.IsLogarithmic = false;
261      else area.AxisY.IsLogarithmic = Content.VisualProperties.YAxisLogScale;
262      if (chart.Series.Any(x => x.Enabled && x.YAxisType == AxisType.Secondary && (x.Points.Count == 0 || x.Points.Any(y => y.YValues.Min() <= 0))))
263        area.AxisY2.IsLogarithmic = false;
264      else area.AxisY2.IsLogarithmic = Content.VisualProperties.SecondYAxisLogScale;
265
266      area.RecalculateAxesScale();
267      area.AxisX.IsMarginVisible = false;
268      area.AxisX2.IsMarginVisible = false;
269
270      if (!Content.VisualProperties.XAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.XAxisMinimumFixedValue)) area.AxisX.Minimum = Content.VisualProperties.XAxisMinimumFixedValue;
271      if (!Content.VisualProperties.XAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.XAxisMaximumFixedValue)) area.AxisX.Maximum = Content.VisualProperties.XAxisMaximumFixedValue;
272      if (!Content.VisualProperties.SecondXAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.SecondXAxisMinimumFixedValue)) area.AxisX2.Minimum = Content.VisualProperties.SecondXAxisMinimumFixedValue;
273      if (!Content.VisualProperties.SecondXAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.SecondXAxisMaximumFixedValue)) area.AxisX2.Maximum = Content.VisualProperties.SecondXAxisMaximumFixedValue;
274      if (!Content.VisualProperties.YAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.YAxisMinimumFixedValue)) area.AxisY.Minimum = Content.VisualProperties.YAxisMinimumFixedValue;
275      if (!Content.VisualProperties.YAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.YAxisMaximumFixedValue)) area.AxisY.Maximum = Content.VisualProperties.YAxisMaximumFixedValue;
276      if (!Content.VisualProperties.SecondYAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.SecondYAxisMinimumFixedValue)) area.AxisY2.Minimum = Content.VisualProperties.SecondYAxisMinimumFixedValue;
277      if (!Content.VisualProperties.SecondYAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.SecondYAxisMaximumFixedValue)) area.AxisY2.Maximum = Content.VisualProperties.SecondYAxisMaximumFixedValue;
278      if (area.AxisX.Minimum >= area.AxisX.Maximum) area.AxisX.Maximum = area.AxisX.Minimum + 1;
279      if (area.AxisX2.Minimum >= area.AxisX2.Maximum) area.AxisX2.Maximum = area.AxisX2.Minimum + 1;
280      if (area.AxisY.Minimum >= area.AxisY.Maximum) area.AxisY.Maximum = area.AxisY.Minimum + 1;
281      if (area.AxisY2.Minimum >= area.AxisY2.Maximum) area.AxisY2.Maximum = area.AxisY2.Minimum + 1;
282    }
283
284    /// <summary>
285    /// Set the Y Cursor interval to visible points of enabled series.
286    /// </summary>
287    protected virtual void UpdateYCursorInterval() {
288      double interestingValuesRange = (
289        from series in chart.Series
290        where series.Enabled
291        let values = (from point in series.Points
292                      where !point.IsEmpty
293                      select point.YValues[0]).DefaultIfEmpty(1.0)
294        let range = values.Max() - values.Min()
295        where range > 0.0
296        select range
297        ).DefaultIfEmpty(1.0).Min();
298
299      double digits = (int)Math.Log10(interestingValuesRange) - 3;
300      double yZoomInterval = Math.Pow(10, digits);
301      this.chart.ChartAreas[0].CursorY.Interval = yZoomInterval;
302    }
303
304
305    /// <summary>
306    /// Remove the corresponding series for a certain DataRow.
307    /// </summary>
308    /// <param name="row">DataRow which series should be removed.</param>
309    protected virtual void RemoveDataRow(IndexedDataRow<T> row) {
310      if (chart.Series.All(x => x.Name != row.Name)) return;
311      Series series = chart.Series[row.Name];
312      chart.Series.Remove(series);
313      if (invisibleSeries.Contains(series))
314        invisibleSeries.Remove(series);
315      RecalculateAxesScale(chart.ChartAreas[0]);
316    }
317
318    #region Event Handlers
319    #region Content Event Handlers
320    protected override void Content_NameChanged(object sender, EventArgs e) {
321      if (InvokeRequired)
322        Invoke(new EventHandler(Content_NameChanged), sender, e);
323      else {
324        chart.Titles[0].Text = Content.Name;
325        base.Content_NameChanged(sender, e);
326      }
327    }
328    private void Content_VisualPropertiesChanged(object sender, EventArgs e) {
329      if (InvokeRequired)
330        Invoke(new EventHandler(Content_VisualPropertiesChanged), sender, e);
331      else {
332        ConfigureChartArea(chart.ChartAreas[0]);
333        RecalculateAxesScale(chart.ChartAreas[0]); // axes min/max could have changed
334      }
335    }
336    #endregion
337    #region Rows Event Handlers
338    private void Rows_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedDataRow<T>> e) {
339      if (InvokeRequired)
340        Invoke(new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_ItemsAdded), sender, e);
341      else {
342        foreach (var row in e.Items) {
343          AddDataRow(row);
344          RegisterDataRowEvents(row);
345        }
346      }
347    }
348    private void Rows_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedDataRow<T>> e) {
349      if (InvokeRequired)
350        Invoke(new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_ItemsRemoved), sender, e);
351      else {
352        foreach (var row in e.Items) {
353          DeregisterDataRowEvents(row);
354          RemoveDataRow(row);
355        }
356      }
357    }
358    private void Rows_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedDataRow<T>> e) {
359      if (InvokeRequired)
360        Invoke(new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_ItemsReplaced), sender, e);
361      else {
362        foreach (var row in e.OldItems) {
363          DeregisterDataRowEvents(row);
364          RemoveDataRow(row);
365        }
366        foreach (var row in e.Items) {
367          AddDataRow(row);
368          RegisterDataRowEvents(row);
369        }
370      }
371    }
372    private void Rows_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedDataRow<T>> e) {
373      if (InvokeRequired)
374        Invoke(new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_CollectionReset), sender, e);
375      else {
376        foreach (var row in e.OldItems) {
377          DeregisterDataRowEvents(row);
378          RemoveDataRow(row);
379        }
380        foreach (var row in e.Items) {
381          AddDataRow(row);
382          RegisterDataRowEvents(row);
383        }
384      }
385    }
386    #endregion
387    #region Row Event Handlers
388    private void Row_VisualPropertiesChanged(object sender, EventArgs e) {
389      if (InvokeRequired)
390        Invoke(new EventHandler(Row_VisualPropertiesChanged), sender, e);
391      else {
392        var row = (IndexedDataRow<T>)sender;
393        Series series = chart.Series[row.Name];
394        series.Points.Clear();
395        ConfigureSeries(series, row);
396        FillSeriesWithRowValues(series, row);
397        RecalculateAxesScale(chart.ChartAreas[0]);
398      }
399    }
400    private void Row_NameChanged(object sender, EventArgs e) {
401      if (InvokeRequired)
402        Invoke(new EventHandler(Row_NameChanged), sender, e);
403      else {
404        var row = (IndexedDataRow<T>)sender;
405        chart.Series[row.Name].Name = row.Name;
406      }
407    }
408    #endregion
409    #region Values Event Handlers
410    private void Values_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<Tuple<T, double>>> e) {
411      if (InvokeRequired)
412        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsAdded), sender, e);
413      else {
414        IndexedDataRow<T> row = null;
415        valuesRowsTable.TryGetValue((IObservableList<Tuple<T, double>>)sender, out row);
416        if (row != null) {
417          Series rowSeries = chart.Series[row.Name];
418          if (!invisibleSeries.Contains(rowSeries)) {
419            rowSeries.Points.Clear();
420            FillSeriesWithRowValues(rowSeries, row);
421            RecalculateAxesScale(chart.ChartAreas[0]);
422            UpdateYCursorInterval();
423          }
424        } else AddDataRow((IndexedDataRow<T>)sender);
425      }
426    }
427    private void Values_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Tuple<T, double>>> e) {
428      if (InvokeRequired)
429        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsRemoved), sender, e);
430      else {
431        IndexedDataRow<T> row = null;
432        valuesRowsTable.TryGetValue((IObservableList<Tuple<T, double>>)sender, out row);
433        if (row != null) {
434          if (row.Values.Count == 0) {
435            RemoveDataRow(row);
436          } else {
437            Series rowSeries = chart.Series[row.Name];
438            if (!invisibleSeries.Contains(rowSeries)) {
439              rowSeries.Points.Clear();
440              FillSeriesWithRowValues(rowSeries, row);
441              RecalculateAxesScale(chart.ChartAreas[0]);
442              UpdateYCursorInterval();
443            }
444          }
445        }
446      }
447    }
448    private void Values_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<Tuple<T, double>>> e) {
449      if (InvokeRequired)
450        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsReplaced), sender, e);
451      else {
452        IndexedDataRow<T> row = null;
453        valuesRowsTable.TryGetValue((IObservableList<Tuple<T, double>>)sender, out row);
454        if (row != null) {
455          if (row.Values.Count == 0) {
456            RemoveDataRow(row);
457          } else {
458            Series rowSeries = chart.Series[row.Name];
459            if (!invisibleSeries.Contains(rowSeries)) {
460              if (row.VisualProperties.ChartType == DataRowVisualProperties.DataRowChartType.Histogram) {
461                rowSeries.Points.Clear();
462                FillSeriesWithRowValues(rowSeries, row);
463              } else {
464                foreach (IndexedItem<Tuple<T, double>> item in e.Items) {
465                  rowSeries.Points[item.Index].SetValueXY(item.Value.Item1, item.Value.Item2);
466                }
467              }
468              RecalculateAxesScale(chart.ChartAreas[0]);
469              UpdateYCursorInterval();
470            }
471          }
472        }
473      }
474    }
475    private void Values_ItemsMoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Tuple<T, double>>> e) {
476      if (InvokeRequired)
477        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsMoved), sender, e);
478      else {
479        IndexedDataRow<T> row = null;
480        valuesRowsTable.TryGetValue((IObservableList<Tuple<T, double>>)sender, out row);
481        if (row != null) {
482          Series rowSeries = chart.Series[row.Name];
483          if (!invisibleSeries.Contains(rowSeries)) {
484            rowSeries.Points.Clear();
485            FillSeriesWithRowValues(rowSeries, row);
486            RecalculateAxesScale(chart.ChartAreas[0]);
487            UpdateYCursorInterval();
488          }
489        }
490      }
491    }
492
493    private void Values_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<Tuple<T, double>>> e) {
494      if (InvokeRequired)
495        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_CollectionReset), sender, e);
496      else {
497        IndexedDataRow<T> row = null;
498        valuesRowsTable.TryGetValue((IObservableList<Tuple<T, double>>)sender, out row);
499        if (row != null) {
500          if (row.Values.Count == 0) {
501            RemoveDataRow(row);
502          } else {
503            Series rowSeries = chart.Series[row.Name];
504            if (!invisibleSeries.Contains(rowSeries)) {
505              rowSeries.Points.Clear();
506              FillSeriesWithRowValues(rowSeries, row);
507              RecalculateAxesScale(chart.ChartAreas[0]);
508              UpdateYCursorInterval();
509            }
510          }
511        }
512      }
513    }
514    #endregion
515    #endregion
516
517    #region Chart Event Handlers
518    private void chart_MouseDown(object sender, MouseEventArgs e) {
519      HitTestResult result = chart.HitTest(e.X, e.Y);
520      if (result.ChartElementType == ChartElementType.LegendItem) {
521        ToggleSeriesVisible(result.Series);
522      }
523    }
524    private void chart_MouseMove(object sender, MouseEventArgs e) {
525      HitTestResult result = chart.HitTest(e.X, e.Y);
526      if (result.ChartElementType == ChartElementType.LegendItem)
527        this.Cursor = Cursors.Hand;
528      else
529        this.Cursor = Cursors.Default;
530    }
531    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
532      foreach (LegendItem legendItem in e.LegendItems) {
533        var series = chart.Series[legendItem.SeriesName];
534        if (series != null) {
535          bool seriesIsInvisible = invisibleSeries.Contains(series);
536          foreach (LegendCell cell in legendItem.Cells) {
537            cell.ForeColor = seriesIsInvisible ? Color.Gray : Color.Black;
538          }
539        }
540      }
541    }
542    #endregion
543
544    private void ToggleSeriesVisible(Series series) {
545      if (!invisibleSeries.Contains(series)) {
546        series.Points.Clear();
547        invisibleSeries.Add(series);
548      } else {
549        invisibleSeries.Remove(series);
550        if (Content != null) {
551
552          var row = (from r in Content.Rows
553                     where r.Name == series.Name
554                     select r).Single();
555          FillSeriesWithRowValues(series, row);
556          this.chart.Legends[series.Legend].ForeColor = Color.Black;
557          RecalculateAxesScale(chart.ChartAreas[0]);
558          UpdateYCursorInterval();
559        }
560      }
561    }
562
563    private void FillSeriesWithRowValues(Series series, IndexedDataRow<T> row) {
564      for (int i = 0; i < row.Values.Count; i++) {
565        var value = row.Values[i];
566        DataPoint point = new DataPoint();
567        point.SetValueXY(value.Item1, value.Item2);
568        series.Points.Add(point);
569      }
570    }
571
572    #region Helpers
573    protected void RemoveCustomPropertyIfExists(Series series, string property) {
574      if (series.IsCustomPropertySet(property)) series.DeleteCustomProperty(property);
575    }
576
577    private ChartDashStyle ConvertLineStyle(DataRowVisualProperties.DataRowLineStyle dataRowLineStyle) {
578      switch (dataRowLineStyle) {
579        case DataRowVisualProperties.DataRowLineStyle.Dash:
580          return ChartDashStyle.Dash;
581        case DataRowVisualProperties.DataRowLineStyle.DashDot:
582          return ChartDashStyle.DashDot;
583        case DataRowVisualProperties.DataRowLineStyle.DashDotDot:
584          return ChartDashStyle.DashDotDot;
585        case DataRowVisualProperties.DataRowLineStyle.Dot:
586          return ChartDashStyle.Dot;
587        case DataRowVisualProperties.DataRowLineStyle.NotSet:
588          return ChartDashStyle.NotSet;
589        case DataRowVisualProperties.DataRowLineStyle.Solid:
590          return ChartDashStyle.Solid;
591        default:
592          return ChartDashStyle.NotSet;
593      }
594    }
595
596    /// <summary>
597    /// Determines whether a double value can be displayed (converted to Decimal and not an NaN).
598    /// </summary>
599    /// <param name="x">The number to check.</param>
600    /// <returns><code>true</code> if the value can be safely shwon in the chart,
601    /// <code>false</code> otherwise.</returns>
602    protected static bool IsInvalidValue(double x) {
603      return double.IsNaN(x) || x < (double)decimal.MinValue || x > (double)decimal.MaxValue;
604    }
605    #endregion
606  }
607}
Note: See TracBrowser for help on using the repository browser.