Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2947_ConfigurableIndexedDataTable/HeuristicLab.Analysis.Views/3.3/IndexedDataTableView.cs @ 16149

Last change on this file since 16149 was 16149, checked in by pfleck, 6 years ago

#2947

  • Added interfaces for DataRow and DataTable (IDataTable is generic because the NamedItemCollection is invariant)
  • Adapted DataTableVisualPropertiesDialog to use the new interfaces
  • Added IConfigurableView to IndexedDataTableView
  • Fixed VisualProperty.Title change in IndexedDataTableView
File size: 29.1 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2018 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, IConfigureableView {
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    public void ShowConfiguration() {
136      if (Content != null) {
137        using (var dialog = new DataTableVisualPropertiesDialog<IndexedDataRow<T>>(Content)) {
138          dialog.ShowDialog(this);
139        }
140      } else MessageBox.Show("Nothing to configure.");
141    }
142
143    /// <summary>
144    /// Add the DataRow as a series to the chart.
145    /// </summary>
146    /// <param name="row">DataRow to add as series to the chart.</param>
147    protected virtual void AddDataRow(IndexedDataRow<T> row) {
148      if (row.Values.Count == 0) return;
149
150      Series series = new Series(row.Name);
151      if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;
152      else series.LegendText = row.Name;
153      ConfigureSeries(series, row);
154      FillSeriesWithRowValues(series, row);
155
156      chart.Series.Add(series);
157      ConfigureChartArea(chart.ChartAreas[0]);
158      RecalculateAxesScale(chart.ChartAreas[0]);
159      UpdateYCursorInterval();
160    }
161
162    private void ConfigureSeries(Series series, IndexedDataRow<T> row) {
163      RemoveCustomPropertyIfExists(series, "PointWidth");
164      series.BorderWidth = 1;
165      series.BorderDashStyle = ChartDashStyle.Solid;
166      series.BorderColor = Color.Empty;
167
168      series.Color = row.VisualProperties.Color;
169      series.IsVisibleInLegend = row.VisualProperties.IsVisibleInLegend;
170
171      series.SmartLabelStyle.Enabled = true;
172      series.SmartLabelStyle.AllowOutsidePlotArea = LabelOutsidePlotAreaStyle.No;
173      series.SmartLabelStyle.CalloutLineAnchorCapStyle = LineAnchorCapStyle.None;
174      series.SmartLabelStyle.CalloutLineColor = series.Color;
175      series.SmartLabelStyle.CalloutLineWidth = 2;
176      series.SmartLabelStyle.CalloutStyle = LabelCalloutStyle.Underlined;
177      series.SmartLabelStyle.IsOverlappedHidden = false;
178      series.SmartLabelStyle.MaxMovingDistance = 200;
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      chart.Titles[0].Text = Content.VisualProperties.Title;
222      chart.Titles[0].Visible = !string.IsNullOrEmpty(Content.VisualProperties.Title);
223
224      if (Content.VisualProperties.AxisTitleFont != null) area.AxisX.TitleFont = Content.VisualProperties.AxisTitleFont;
225      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisX.TitleForeColor = Content.VisualProperties.AxisTitleColor;
226      area.AxisX.Title = Content.VisualProperties.XAxisTitle;
227
228      if (Content.VisualProperties.AxisTitleFont != null) area.AxisX2.TitleFont = Content.VisualProperties.AxisTitleFont;
229      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisX2.TitleForeColor = Content.VisualProperties.AxisTitleColor;
230      area.AxisX2.Title = Content.VisualProperties.SecondXAxisTitle;
231
232      if (Content.VisualProperties.AxisTitleFont != null) area.AxisY.TitleFont = Content.VisualProperties.AxisTitleFont;
233      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisY.TitleForeColor = Content.VisualProperties.AxisTitleColor;
234      area.AxisY.Title = Content.VisualProperties.YAxisTitle;
235
236      if (Content.VisualProperties.AxisTitleFont != null) area.AxisY2.TitleFont = Content.VisualProperties.AxisTitleFont;
237      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisY2.TitleForeColor = Content.VisualProperties.AxisTitleColor;
238      area.AxisY2.Title = Content.VisualProperties.SecondYAxisTitle;
239
240      if (typeof(T).Equals(typeof(DateTime))) {
241        area.AxisX.IntervalType = DateTimeIntervalType.Hours;
242        area.AxisX.LabelStyle.Format = "dd.MM.yyyy HH:mm";
243        area.AxisX.IntervalAutoMode = IntervalAutoMode.VariableCount;
244      }
245    }
246
247    private void RecalculateAxesScale(ChartArea area) {
248      // Reset the axes bounds so that RecalculateAxesScale() will assign new bounds
249      foreach (Axis a in area.Axes) {
250        a.Minimum = double.NaN;
251        a.Maximum = double.NaN;
252      }
253
254      if (chart.Series.Any(x => x.Enabled && x.XAxisType == AxisType.Primary && (x.Points.Count == 0 || x.Points.Any(y => y.XValue <= 0))))
255        area.AxisX.IsLogarithmic = false;
256      else area.AxisX.IsLogarithmic = Content.VisualProperties.XAxisLogScale;
257      if (chart.Series.Any(x => x.Enabled && x.XAxisType == AxisType.Secondary && (x.Points.Count == 0 || x.Points.Any(y => y.XValue <= 0))))
258        area.AxisX2.IsLogarithmic = false;
259      else area.AxisX2.IsLogarithmic = Content.VisualProperties.SecondXAxisLogScale;
260
261      if (chart.Series.Any(x => x.Enabled && x.YAxisType == AxisType.Primary && (x.Points.Count == 0 || x.Points.Any(y => y.YValues.Min() <= 0))))
262        area.AxisY.IsLogarithmic = false;
263      else area.AxisY.IsLogarithmic = Content.VisualProperties.YAxisLogScale;
264      if (chart.Series.Any(x => x.Enabled && x.YAxisType == AxisType.Secondary && (x.Points.Count == 0 || x.Points.Any(y => y.YValues.Min() <= 0))))
265        area.AxisY2.IsLogarithmic = false;
266      else area.AxisY2.IsLogarithmic = Content.VisualProperties.SecondYAxisLogScale;
267
268      area.RecalculateAxesScale();
269      area.AxisX.IsMarginVisible = false;
270      area.AxisX2.IsMarginVisible = false;
271
272      area.AxisX.IsStartedFromZero = Content.Rows.Where(x => !x.VisualProperties.SecondXAxis).Any(x => x.VisualProperties.StartIndexZero);
273      area.AxisX2.IsStartedFromZero = Content.Rows.Where(x => x.VisualProperties.SecondXAxis).Any(x => x.VisualProperties.StartIndexZero);
274
275      if (!Content.VisualProperties.XAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.XAxisMinimumFixedValue)) area.AxisX.Minimum = Content.VisualProperties.XAxisMinimumFixedValue;
276      if (!Content.VisualProperties.XAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.XAxisMaximumFixedValue)) area.AxisX.Maximum = Content.VisualProperties.XAxisMaximumFixedValue;
277      if (!Content.VisualProperties.SecondXAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.SecondXAxisMinimumFixedValue)) area.AxisX2.Minimum = Content.VisualProperties.SecondXAxisMinimumFixedValue;
278      if (!Content.VisualProperties.SecondXAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.SecondXAxisMaximumFixedValue)) area.AxisX2.Maximum = Content.VisualProperties.SecondXAxisMaximumFixedValue;
279      if (!Content.VisualProperties.YAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.YAxisMinimumFixedValue)) area.AxisY.Minimum = Content.VisualProperties.YAxisMinimumFixedValue;
280      if (!Content.VisualProperties.YAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.YAxisMaximumFixedValue)) area.AxisY.Maximum = Content.VisualProperties.YAxisMaximumFixedValue;
281      if (!Content.VisualProperties.SecondYAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.SecondYAxisMinimumFixedValue)) area.AxisY2.Minimum = Content.VisualProperties.SecondYAxisMinimumFixedValue;
282      if (!Content.VisualProperties.SecondYAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.SecondYAxisMaximumFixedValue)) area.AxisY2.Maximum = Content.VisualProperties.SecondYAxisMaximumFixedValue;
283      if (area.AxisX.Minimum >= area.AxisX.Maximum) area.AxisX.Maximum = area.AxisX.Minimum + 1;
284      if (area.AxisX2.Minimum >= area.AxisX2.Maximum) area.AxisX2.Maximum = area.AxisX2.Minimum + 1;
285      if (area.AxisY.Minimum >= area.AxisY.Maximum) area.AxisY.Maximum = area.AxisY.Minimum + 1;
286      if (area.AxisY2.Minimum >= area.AxisY2.Maximum) area.AxisY2.Maximum = area.AxisY2.Minimum + 1;
287    }
288
289    /// <summary>
290    /// Set the Y Cursor interval to visible points of enabled series.
291    /// </summary>
292    protected virtual void UpdateYCursorInterval() {
293      double interestingValuesRange = (
294        from series in chart.Series
295        where series.Enabled
296        let values = (from point in series.Points
297                      where !point.IsEmpty
298                      select point.YValues[0]).DefaultIfEmpty(1.0)
299        let range = values.Max() - values.Min()
300        where range > 0.0
301        select range
302        ).DefaultIfEmpty(1.0).Min();
303
304      double digits = (int)Math.Log10(interestingValuesRange) - 3;
305      double yZoomInterval = Math.Pow(10, digits);
306      this.chart.ChartAreas[0].CursorY.Interval = yZoomInterval;
307    }
308
309
310    /// <summary>
311    /// Remove the corresponding series for a certain DataRow.
312    /// </summary>
313    /// <param name="row">DataRow which series should be removed.</param>
314    protected virtual void RemoveDataRow(IndexedDataRow<T> row) {
315      if (chart.Series.All(x => x.Name != row.Name)) return;
316      Series series = chart.Series[row.Name];
317      chart.Series.Remove(series);
318      if (invisibleSeries.Contains(series))
319        invisibleSeries.Remove(series);
320      RecalculateAxesScale(chart.ChartAreas[0]);
321    }
322
323    #region Event Handlers
324    #region Content Event Handlers
325    protected override void Content_NameChanged(object sender, EventArgs e) {
326      if (InvokeRequired)
327        Invoke(new EventHandler(Content_NameChanged), sender, e);
328      else {
329        chart.Titles[0].Text = Content.Name;
330        base.Content_NameChanged(sender, e);
331      }
332    }
333    private void Content_VisualPropertiesChanged(object sender, EventArgs e) {
334      if (InvokeRequired)
335        Invoke(new EventHandler(Content_VisualPropertiesChanged), sender, e);
336      else {
337        ConfigureChartArea(chart.ChartAreas[0]);
338        RecalculateAxesScale(chart.ChartAreas[0]); // axes min/max could have changed
339      }
340    }
341    #endregion
342    #region Rows Event Handlers
343    private void Rows_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedDataRow<T>> e) {
344      if (InvokeRequired)
345        Invoke(new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_ItemsAdded), sender, e);
346      else {
347        foreach (var row in e.Items) {
348          AddDataRow(row);
349          RegisterDataRowEvents(row);
350        }
351      }
352    }
353    private void Rows_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedDataRow<T>> e) {
354      if (InvokeRequired)
355        Invoke(new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_ItemsRemoved), sender, e);
356      else {
357        foreach (var row in e.Items) {
358          DeregisterDataRowEvents(row);
359          RemoveDataRow(row);
360        }
361      }
362    }
363    private void Rows_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedDataRow<T>> e) {
364      if (InvokeRequired)
365        Invoke(new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_ItemsReplaced), sender, e);
366      else {
367        foreach (var row in e.OldItems) {
368          DeregisterDataRowEvents(row);
369          RemoveDataRow(row);
370        }
371        foreach (var row in e.Items) {
372          AddDataRow(row);
373          RegisterDataRowEvents(row);
374        }
375      }
376    }
377    private void Rows_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedDataRow<T>> e) {
378      if (InvokeRequired)
379        Invoke(new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_CollectionReset), sender, e);
380      else {
381        foreach (var row in e.OldItems) {
382          DeregisterDataRowEvents(row);
383          RemoveDataRow(row);
384        }
385        foreach (var row in e.Items) {
386          AddDataRow(row);
387          RegisterDataRowEvents(row);
388        }
389      }
390    }
391    #endregion
392    #region Row Event Handlers
393    private void Row_VisualPropertiesChanged(object sender, EventArgs e) {
394      if (InvokeRequired)
395        Invoke(new EventHandler(Row_VisualPropertiesChanged), sender, e);
396      else {
397        var row = (IndexedDataRow<T>)sender;
398        Series series = chart.Series[row.Name];
399        series.Points.Clear();
400        ConfigureSeries(series, row);
401        FillSeriesWithRowValues(series, row);
402        RecalculateAxesScale(chart.ChartAreas[0]);
403      }
404    }
405    private void Row_NameChanged(object sender, EventArgs e) {
406      if (InvokeRequired)
407        Invoke(new EventHandler(Row_NameChanged), sender, e);
408      else {
409        var row = (IndexedDataRow<T>)sender;
410        chart.Series[row.Name].Name = row.Name;
411      }
412    }
413    #endregion
414    #region Values Event Handlers
415    private void Values_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<Tuple<T, double>>> e) {
416      if (InvokeRequired)
417        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsAdded), sender, e);
418      else {
419        IndexedDataRow<T> row = null;
420        valuesRowsTable.TryGetValue((IObservableList<Tuple<T, double>>)sender, out row);
421        if (row != null) {
422          Series rowSeries = chart.Series[row.Name];
423          if (!invisibleSeries.Contains(rowSeries)) {
424            rowSeries.Points.Clear();
425            FillSeriesWithRowValues(rowSeries, row);
426            RecalculateAxesScale(chart.ChartAreas[0]);
427            UpdateYCursorInterval();
428          }
429        } else AddDataRow((IndexedDataRow<T>)sender);
430      }
431    }
432    private void Values_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Tuple<T, double>>> e) {
433      if (InvokeRequired)
434        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsRemoved), sender, e);
435      else {
436        IndexedDataRow<T> row = null;
437        valuesRowsTable.TryGetValue((IObservableList<Tuple<T, double>>)sender, out row);
438        if (row != null) {
439          if (row.Values.Count == 0) {
440            RemoveDataRow(row);
441          } else {
442            Series rowSeries = chart.Series[row.Name];
443            if (!invisibleSeries.Contains(rowSeries)) {
444              rowSeries.Points.Clear();
445              FillSeriesWithRowValues(rowSeries, row);
446              RecalculateAxesScale(chart.ChartAreas[0]);
447              UpdateYCursorInterval();
448            }
449          }
450        }
451      }
452    }
453    private void Values_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<Tuple<T, double>>> e) {
454      if (InvokeRequired)
455        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsReplaced), sender, e);
456      else {
457        IndexedDataRow<T> row = null;
458        valuesRowsTable.TryGetValue((IObservableList<Tuple<T, double>>)sender, out row);
459        if (row != null) {
460          if (row.Values.Count == 0) {
461            RemoveDataRow(row);
462          } else {
463            Series rowSeries = chart.Series[row.Name];
464            if (!invisibleSeries.Contains(rowSeries)) {
465              if (row.VisualProperties.ChartType == DataRowVisualProperties.DataRowChartType.Histogram) {
466                rowSeries.Points.Clear();
467                FillSeriesWithRowValues(rowSeries, row);
468              } else {
469                foreach (IndexedItem<Tuple<T, double>> item in e.Items) {
470                  rowSeries.Points[item.Index].SetValueXY(item.Value.Item1, item.Value.Item2);
471                }
472              }
473              RecalculateAxesScale(chart.ChartAreas[0]);
474              UpdateYCursorInterval();
475            }
476          }
477        }
478      }
479    }
480    private void Values_ItemsMoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Tuple<T, double>>> e) {
481      if (InvokeRequired)
482        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsMoved), sender, e);
483      else {
484        IndexedDataRow<T> row = null;
485        valuesRowsTable.TryGetValue((IObservableList<Tuple<T, double>>)sender, out row);
486        if (row != null) {
487          Series rowSeries = chart.Series[row.Name];
488          if (!invisibleSeries.Contains(rowSeries)) {
489            rowSeries.Points.Clear();
490            FillSeriesWithRowValues(rowSeries, row);
491            RecalculateAxesScale(chart.ChartAreas[0]);
492            UpdateYCursorInterval();
493          }
494        }
495      }
496    }
497
498    private void Values_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<Tuple<T, double>>> e) {
499      if (InvokeRequired)
500        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_CollectionReset), sender, e);
501      else {
502        IndexedDataRow<T> row = null;
503        valuesRowsTable.TryGetValue((IObservableList<Tuple<T, double>>)sender, out row);
504        if (row != null) {
505          if (row.Values.Count == 0) {
506            RemoveDataRow(row);
507          } else {
508            Series rowSeries = chart.Series[row.Name];
509            if (!invisibleSeries.Contains(rowSeries)) {
510              rowSeries.Points.Clear();
511              FillSeriesWithRowValues(rowSeries, row);
512              RecalculateAxesScale(chart.ChartAreas[0]);
513              UpdateYCursorInterval();
514            }
515          }
516        }
517      }
518    }
519    #endregion
520    #endregion
521
522    #region Chart Event Handlers
523    private void chart_MouseDown(object sender, MouseEventArgs e) {
524      HitTestResult result = chart.HitTest(e.X, e.Y);
525      if (result.ChartElementType == ChartElementType.LegendItem) {
526        ToggleSeriesVisible(result.Series);
527      }
528    }
529    private void chart_MouseMove(object sender, MouseEventArgs e) {
530      HitTestResult result = chart.HitTest(e.X, e.Y);
531      if (result.ChartElementType == ChartElementType.LegendItem)
532        this.Cursor = Cursors.Hand;
533      else
534        this.Cursor = Cursors.Default;
535    }
536    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
537      foreach (LegendItem legendItem in e.LegendItems) {
538        var series = chart.Series[legendItem.SeriesName];
539        if (series != null) {
540          bool seriesIsInvisible = invisibleSeries.Contains(series);
541          foreach (LegendCell cell in legendItem.Cells) {
542            cell.ForeColor = seriesIsInvisible ? Color.Gray : Color.Black;
543          }
544        }
545      }
546    }
547    #endregion
548
549    private void ToggleSeriesVisible(Series series) {
550      if (!invisibleSeries.Contains(series)) {
551        series.Points.Clear();
552        invisibleSeries.Add(series);
553      } else {
554        invisibleSeries.Remove(series);
555        if (Content != null) {
556
557          var row = (from r in Content.Rows
558                     where r.Name == series.Name
559                     select r).Single();
560          FillSeriesWithRowValues(series, row);
561          this.chart.Legends[series.Legend].ForeColor = Color.Black;
562          RecalculateAxesScale(chart.ChartAreas[0]);
563          UpdateYCursorInterval();
564        }
565      }
566    }
567
568    private void FillSeriesWithRowValues(Series series, IndexedDataRow<T> row) {
569      for (int i = 0; i < row.Values.Count; i++) {
570        var value = row.Values[i];
571        if (IsInvalidValue(value.Item2)) continue;
572        var point = new DataPoint();
573        point.SetValueXY(value.Item1, value.Item2);
574        if (i == row.Values.Count - 1) {
575          point.Label = series.Name;
576          point.MarkerStyle = MarkerStyle.Cross;
577          point.MarkerSize = 15;
578          point.MarkerBorderWidth = 1;
579        }
580        series.Points.Add(point);
581      }
582    }
583
584    #region Helpers
585    protected void RemoveCustomPropertyIfExists(Series series, string property) {
586      if (series.IsCustomPropertySet(property)) series.DeleteCustomProperty(property);
587    }
588
589    private ChartDashStyle ConvertLineStyle(DataRowVisualProperties.DataRowLineStyle dataRowLineStyle) {
590      switch (dataRowLineStyle) {
591        case DataRowVisualProperties.DataRowLineStyle.Dash:
592          return ChartDashStyle.Dash;
593        case DataRowVisualProperties.DataRowLineStyle.DashDot:
594          return ChartDashStyle.DashDot;
595        case DataRowVisualProperties.DataRowLineStyle.DashDotDot:
596          return ChartDashStyle.DashDotDot;
597        case DataRowVisualProperties.DataRowLineStyle.Dot:
598          return ChartDashStyle.Dot;
599        case DataRowVisualProperties.DataRowLineStyle.NotSet:
600          return ChartDashStyle.NotSet;
601        case DataRowVisualProperties.DataRowLineStyle.Solid:
602          return ChartDashStyle.Solid;
603        default:
604          return ChartDashStyle.NotSet;
605      }
606    }
607
608    /// <summary>
609    /// Determines whether a double value can be displayed (converted to Decimal and not an NaN).
610    /// </summary>
611    /// <param name="x">The number to check.</param>
612    /// <returns><code>true</code> if the value can be safely shwon in the chart,
613    /// <code>false</code> otherwise.</returns>
614    protected static bool IsInvalidValue(double x) {
615      return double.IsNaN(x) || x < (double)decimal.MinValue || x > (double)decimal.MaxValue;
616    }
617    #endregion
618  }
619}
Note: See TracBrowser for help on using the repository browser.