Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 14109 was 14102, checked in by abeham, 8 years ago

#2634: Integrated RLD analysis into trunk

  • HeuristicLab.Analysis:
    • IndexedDataTable<T>, IndexedDataRow<T>
    • QualityPerClockAnalyzer, QualityPerEvaluationsAnalyzer
    • ExpectedRuntimeHelper
  • HeuristicLab.Analysis.Views:
    • IndexedDataTableView
  • HeuristicLab.Optimization.Views:
    • RunCollectionRLDView

To test:

  1. Configure an algorithm/problem combination
  2. In the algorithm's analyzers add the QualityPerEvaluationsAnalyzer
  3. Make sure the BestAverageWorstQualityAnalyzer is executed before
  4. Run the algorithm several times
  5. In the Runs tab select the "Run-length Distribution View"
File size: 28.7 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 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.