Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Analysis.Views/3.3/IndexedDataTableView.cs @ 14519

Last change on this file since 14519 was 14185, checked in by swagner, 8 years ago

#2526: Updated year of copyrights in license headers

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