Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2431:

  • added ability to plot curves for multiple targets at once
  • fixed bug in IndexedDataTableView regarding log-scaling
  • added result that counts restarts
  • fixed bugs in IRRestarter
  • Set LineWidth = 2 in analyzers for chart
  • fixed bugs in RLD view
File size: 28.0 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Drawing;
25using System.Linq;
26using System.Windows.Forms;
27using System.Windows.Forms.DataVisualization.Charting;
28using HeuristicLab.Collections;
29using HeuristicLab.Core.Views;
30using HeuristicLab.MainForm;
31
32namespace HeuristicLab.Analysis.Views {
33  [View("IndexedDataTable View")]
34  [Content(typeof(IndexedDataTable<>), true)]
35  public partial class IndexedDataTableView<T> : NamedItemView {
36    protected List<Series> invisibleSeries;
37    protected Dictionary<IObservableList<Tuple<T, double>>, IndexedDataRow<T>> valuesRowsTable;
38    /// <summary>
39    /// Gets or sets the variable to represent visually.
40    /// </summary>
41    /// <remarks>Uses property <see cref="ViewBase.Item"/> of base class <see cref="ViewBase"/>.
42    /// No own data storage present.</remarks>
43    public new IndexedDataTable<T> Content {
44      get { return (IndexedDataTable<T>)base.Content; }
45      set { base.Content = value; }
46    }
47
48    /// <summary>
49    /// Initializes a new instance of <see cref="VariableView"/> with caption "Variable".
50    /// </summary>
51    public IndexedDataTableView() {
52      InitializeComponent();
53      valuesRowsTable = new Dictionary<IObservableList<Tuple<T, double>>, IndexedDataRow<T>>();
54      invisibleSeries = new List<Series>();
55      chart.CustomizeAllChartAreas();
56      chart.ChartAreas[0].CursorX.Interval = 1;
57    }
58
59    #region Event Handler Registration
60    /// <summary>
61    /// Removes the eventhandlers from the underlying <see cref="Variable"/>.
62    /// </summary>
63    /// <remarks>Calls <see cref="ViewBase.RemoveItemEvents"/> of base class <see cref="ViewBase"/>.</remarks>
64    protected override void DeregisterContentEvents() {
65      foreach (var row in Content.Rows)
66        DeregisterDataRowEvents(row);
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      base.DeregisterContentEvents();
73    }
74
75    /// <summary>
76    /// Adds eventhandlers to the underlying <see cref="Variable"/>.
77    /// </summary>
78    /// <remarks>Calls <see cref="ViewBase.AddItemEvents"/> of base class <see cref="ViewBase"/>.</remarks>
79    protected override void RegisterContentEvents() {
80      base.RegisterContentEvents();
81      Content.VisualPropertiesChanged += new EventHandler(Content_VisualPropertiesChanged);
82      Content.Rows.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_ItemsAdded);
83      Content.Rows.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_ItemsRemoved);
84      Content.Rows.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_ItemsReplaced);
85      Content.Rows.CollectionReset += new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_CollectionReset);
86      foreach (var row in Content.Rows)
87        RegisterDataRowEvents(row);
88    }
89
90    /// <summary>
91    /// Automatically called for every existing data row and whenever a data row is added
92    /// to the data table. Do not call this method directly.
93    /// </summary>
94    /// <param name="row">The DataRow that was added.</param>
95    protected virtual void RegisterDataRowEvents(IndexedDataRow<T> row) {
96      row.NameChanged += new EventHandler(Row_NameChanged);
97      row.VisualPropertiesChanged += new EventHandler(Row_VisualPropertiesChanged);
98      valuesRowsTable.Add(row.Values, row);
99      row.Values.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsAdded);
100      row.Values.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsRemoved);
101      row.Values.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsReplaced);
102      row.Values.ItemsMoved += new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsMoved);
103      row.Values.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_CollectionReset);
104    }
105
106    /// <summary>
107    /// Automatically called for every data row that is removed from the DataTable. Do
108    /// not directly call this method.
109    /// </summary>
110    /// <param name="row">The DataRow that was removed.</param>
111    protected virtual void DeregisterDataRowEvents(IndexedDataRow<T> row) {
112      row.Values.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsAdded);
113      row.Values.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsRemoved);
114      row.Values.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsReplaced);
115      row.Values.ItemsMoved -= new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsMoved);
116      row.Values.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_CollectionReset);
117      valuesRowsTable.Remove(row.Values);
118      row.VisualPropertiesChanged -= new EventHandler(Row_VisualPropertiesChanged);
119      row.NameChanged -= new EventHandler(Row_NameChanged);
120    }
121    #endregion
122
123    protected override void OnContentChanged() {
124      base.OnContentChanged();
125      invisibleSeries.Clear();
126      chart.Titles[0].Text = string.Empty;
127      chart.ChartAreas[0].AxisX.Title = string.Empty;
128      chart.ChartAreas[0].AxisY.Title = string.Empty;
129      chart.ChartAreas[0].AxisY2.Title = string.Empty;
130      chart.ChartAreas[0].AxisX.IsLogarithmic = false;
131      chart.ChartAreas[0].AxisX2.IsLogarithmic = false;
132      chart.ChartAreas[0].AxisY.IsLogarithmic = false;
133      chart.ChartAreas[0].AxisY2.IsLogarithmic = false;
134      chart.Series.Clear();
135      if (Content != null) {
136        chart.Titles[0].Text = Content.Name;
137        foreach (var row in Content.Rows)
138          AddDataRow(row);
139        ConfigureChartArea(chart.ChartAreas[0]);
140        RecalculateAxesScale(chart.ChartAreas[0]);
141      }
142    }
143
144    protected override void SetEnabledStateOfControls() {
145      base.SetEnabledStateOfControls();
146      chart.Enabled = Content != null;
147    }
148
149    /// <summary>
150    /// Add the DataRow as a series to the chart.
151    /// </summary>
152    /// <param name="row">DataRow to add as series to the chart.</param>
153    protected virtual void AddDataRow(IndexedDataRow<T> row) {
154      Series series = new Series(row.Name);
155      if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;
156      else series.LegendText = row.Name;
157      ConfigureSeries(series, row);
158      FillSeriesWithRowValues(series, row);
159
160      chart.Series.Add(series);
161      ConfigureChartArea(chart.ChartAreas[0]);
162      RecalculateAxesScale(chart.ChartAreas[0]);
163      UpdateYCursorInterval();
164    }
165
166    private void ConfigureSeries(Series series, IndexedDataRow<T> row) {
167      RemoveCustomPropertyIfExists(series, "PointWidth");
168      series.BorderWidth = 1;
169      series.BorderDashStyle = ChartDashStyle.Solid;
170      series.BorderColor = Color.Empty;
171
172      if (row.VisualProperties.Color != Color.Empty)
173        series.Color = row.VisualProperties.Color;
174      else series.Color = Color.Empty;
175      series.IsVisibleInLegend = row.VisualProperties.IsVisibleInLegend;
176
177      switch (row.VisualProperties.ChartType) {
178        case DataRowVisualProperties.DataRowChartType.Line:
179          series.ChartType = SeriesChartType.FastLine;
180          series.BorderWidth = row.VisualProperties.LineWidth;
181          series.BorderDashStyle = ConvertLineStyle(row.VisualProperties.LineStyle);
182          break;
183        case DataRowVisualProperties.DataRowChartType.Bars:
184          // Bar is incompatible with anything but Bar and StackedBar*
185          if (!chart.Series.Any(x => x.ChartType != SeriesChartType.Bar && x.ChartType != SeriesChartType.StackedBar && x.ChartType != SeriesChartType.StackedBar100)) {
186            series.ChartType = SeriesChartType.Bar;
187            chart.ChartAreas[0].AxisX.Interval = 1;
188          } else {
189            series.ChartType = SeriesChartType.FastPoint; //default
190            row.VisualProperties.ChartType = DataRowVisualProperties.DataRowChartType.Points;
191          }
192          break;
193        case DataRowVisualProperties.DataRowChartType.Columns:
194          series.ChartType = SeriesChartType.Column;
195          break;
196        case DataRowVisualProperties.DataRowChartType.Points:
197          series.ChartType = SeriesChartType.FastPoint;
198          break;
199        case DataRowVisualProperties.DataRowChartType.StepLine:
200          series.ChartType = SeriesChartType.StepLine;
201          series.BorderWidth = row.VisualProperties.LineWidth;
202          series.BorderDashStyle = ConvertLineStyle(row.VisualProperties.LineStyle);
203          break;
204        default:
205          series.ChartType = SeriesChartType.FastPoint;
206          break;
207      }
208      series.YAxisType = row.VisualProperties.SecondYAxis ? AxisType.Secondary : AxisType.Primary;
209      series.XAxisType = row.VisualProperties.SecondXAxis ? AxisType.Secondary : AxisType.Primary;
210      if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;
211      else series.LegendText = row.Name;
212      series.ToolTip = series.LegendText + " X = #VALX, Y = #VALY";
213    }
214
215    private void ConfigureChartArea(ChartArea area) {
216      if (Content.VisualProperties.TitleFont != null) chart.Titles[0].Font = Content.VisualProperties.TitleFont;
217      if (!Content.VisualProperties.TitleColor.IsEmpty) chart.Titles[0].ForeColor = Content.VisualProperties.TitleColor;
218
219      if (Content.VisualProperties.AxisTitleFont != null) area.AxisX.TitleFont = Content.VisualProperties.AxisTitleFont;
220      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisX.TitleForeColor = Content.VisualProperties.AxisTitleColor;
221      area.AxisX.Title = Content.VisualProperties.XAxisTitle;
222
223      if (Content.VisualProperties.AxisTitleFont != null) area.AxisX2.TitleFont = Content.VisualProperties.AxisTitleFont;
224      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisX2.TitleForeColor = Content.VisualProperties.AxisTitleColor;
225      area.AxisX2.Title = Content.VisualProperties.SecondXAxisTitle;
226
227      if (Content.VisualProperties.AxisTitleFont != null) area.AxisY.TitleFont = Content.VisualProperties.AxisTitleFont;
228      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisY.TitleForeColor = Content.VisualProperties.AxisTitleColor;
229      area.AxisY.Title = Content.VisualProperties.YAxisTitle;
230
231      if (Content.VisualProperties.AxisTitleFont != null) area.AxisY2.TitleFont = Content.VisualProperties.AxisTitleFont;
232      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisY2.TitleForeColor = Content.VisualProperties.AxisTitleColor;
233      area.AxisY2.Title = Content.VisualProperties.SecondYAxisTitle;
234
235      if (typeof(T).Equals(typeof(DateTime))) {
236        area.AxisX.IntervalType = DateTimeIntervalType.Hours;
237        area.AxisX.LabelStyle.Format = "dd.MM.yyyy HH:mm";
238        area.AxisX.IntervalAutoMode = IntervalAutoMode.VariableCount;
239      }
240    }
241
242    private void RecalculateAxesScale(ChartArea area) {
243      // Reset the axes bounds so that RecalculateAxesScale() will assign new bounds
244      foreach (Axis a in area.Axes) {
245        a.Minimum = double.NaN;
246        a.Maximum = double.NaN;
247      }
248      // chart controls handle log scaling problems not in a graceful way
249      if (chart.Series.Any(x => x.XAxisType == AxisType.Primary && (x.Points.Count == 0 || x.Points.Any(y => y.XValue <= 0))))
250        area.AxisX.IsLogarithmic = false;
251      else area.AxisX.IsLogarithmic = Content.VisualProperties.XAxisLogScale;
252      if (chart.Series.Any(x => x.XAxisType == AxisType.Secondary && (x.Points.Count == 0 || x.Points.Any(y => y.XValue <= 0))))
253        area.AxisX2.IsLogarithmic = false;
254      else area.AxisX2.IsLogarithmic = Content.VisualProperties.SecondXAxisLogScale;
255
256      if (chart.Series.Any(x => x.YAxisType == AxisType.Primary && (x.Points.Count == 0 || x.Points.Any(y => y.YValues.Min() <= 0))))
257        area.AxisY.IsLogarithmic = false;
258      else area.AxisY.IsLogarithmic = Content.VisualProperties.YAxisLogScale;
259      if (chart.Series.Any(x => x.YAxisType == AxisType.Secondary && (x.Points.Count == 0 || x.Points.Any(y => y.YValues.Min() <= 0))))
260        area.AxisY2.IsLogarithmic = false;
261      else area.AxisY2.IsLogarithmic = Content.VisualProperties.SecondYAxisLogScale;
262
263      area.RecalculateAxesScale();
264      area.AxisX.IsMarginVisible = false;
265      area.AxisX2.IsMarginVisible = false;
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      Series series = chart.Series[row.Name];
308      chart.Series.Remove(series);
309      if (invisibleSeries.Contains(series))
310        invisibleSeries.Remove(series);
311      RecalculateAxesScale(chart.ChartAreas[0]);
312    }
313
314    #region Event Handlers
315    #region Content Event Handlers
316    protected override void Content_NameChanged(object sender, EventArgs e) {
317      if (InvokeRequired)
318        Invoke(new EventHandler(Content_NameChanged), sender, e);
319      else {
320        chart.Titles[0].Text = Content.Name;
321        base.Content_NameChanged(sender, e);
322      }
323    }
324    private void Content_VisualPropertiesChanged(object sender, EventArgs e) {
325      if (InvokeRequired)
326        Invoke(new EventHandler(Content_VisualPropertiesChanged), sender, e);
327      else {
328        ConfigureChartArea(chart.ChartAreas[0]);
329        RecalculateAxesScale(chart.ChartAreas[0]); // axes min/max could have changed
330      }
331    }
332    #endregion
333    #region Rows Event Handlers
334    private void Rows_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedDataRow<T>> e) {
335      if (InvokeRequired)
336        Invoke(new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_ItemsAdded), sender, e);
337      else {
338        foreach (var row in e.Items) {
339          AddDataRow(row);
340          RegisterDataRowEvents(row);
341        }
342      }
343    }
344    private void Rows_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedDataRow<T>> e) {
345      if (InvokeRequired)
346        Invoke(new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_ItemsRemoved), sender, e);
347      else {
348        foreach (var row in e.Items) {
349          DeregisterDataRowEvents(row);
350          RemoveDataRow(row);
351        }
352      }
353    }
354    private void Rows_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedDataRow<T>> e) {
355      if (InvokeRequired)
356        Invoke(new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_ItemsReplaced), sender, e);
357      else {
358        foreach (var row in e.OldItems) {
359          DeregisterDataRowEvents(row);
360          RemoveDataRow(row);
361        }
362        foreach (var row in e.Items) {
363          AddDataRow(row);
364          RegisterDataRowEvents(row);
365        }
366      }
367    }
368    private void Rows_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedDataRow<T>> e) {
369      if (InvokeRequired)
370        Invoke(new CollectionItemsChangedEventHandler<IndexedDataRow<T>>(Rows_CollectionReset), sender, e);
371      else {
372        foreach (var row in e.OldItems) {
373          DeregisterDataRowEvents(row);
374          RemoveDataRow(row);
375        }
376        foreach (var row in e.Items) {
377          AddDataRow(row);
378          RegisterDataRowEvents(row);
379        }
380      }
381    }
382    #endregion
383    #region Row Event Handlers
384    private void Row_VisualPropertiesChanged(object sender, EventArgs e) {
385      if (InvokeRequired)
386        Invoke(new EventHandler(Row_VisualPropertiesChanged), sender, e);
387      else {
388        var row = (IndexedDataRow<T>)sender;
389        Series series = chart.Series[row.Name];
390        series.Points.Clear();
391        ConfigureSeries(series, row);
392        FillSeriesWithRowValues(series, row);
393        RecalculateAxesScale(chart.ChartAreas[0]);
394      }
395    }
396    private void Row_NameChanged(object sender, EventArgs e) {
397      if (InvokeRequired)
398        Invoke(new EventHandler(Row_NameChanged), sender, e);
399      else {
400        var row = (IndexedDataRow<T>)sender;
401        chart.Series[row.Name].Name = row.Name;
402      }
403    }
404    #endregion
405    #region Values Event Handlers
406    private void Values_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<Tuple<T, double>>> e) {
407      if (InvokeRequired)
408        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsAdded), sender, e);
409      else {
410        IndexedDataRow<T> row = null;
411        valuesRowsTable.TryGetValue((IObservableList<Tuple<T, double>>)sender, out row);
412        if (row != null) {
413          Series rowSeries = chart.Series[row.Name];
414          if (!invisibleSeries.Contains(rowSeries)) {
415            rowSeries.Points.Clear();
416            FillSeriesWithRowValues(rowSeries, row);
417            RecalculateAxesScale(chart.ChartAreas[0]);
418            UpdateYCursorInterval();
419          }
420        }
421      }
422    }
423    private void Values_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Tuple<T, double>>> e) {
424      if (InvokeRequired)
425        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsRemoved), sender, e);
426      else {
427        IndexedDataRow<T> row = null;
428        valuesRowsTable.TryGetValue((IObservableList<Tuple<T, double>>)sender, out row);
429        if (row != null) {
430          Series rowSeries = chart.Series[row.Name];
431          if (!invisibleSeries.Contains(rowSeries)) {
432            rowSeries.Points.Clear();
433            FillSeriesWithRowValues(rowSeries, row);
434            RecalculateAxesScale(chart.ChartAreas[0]);
435            UpdateYCursorInterval();
436          }
437        }
438      }
439    }
440    private void Values_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<Tuple<T, double>>> e) {
441      if (InvokeRequired)
442        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsReplaced), sender, e);
443      else {
444        IndexedDataRow<T> row = null;
445        valuesRowsTable.TryGetValue((IObservableList<Tuple<T, double>>)sender, out row);
446        if (row != null) {
447          Series rowSeries = chart.Series[row.Name];
448          if (!invisibleSeries.Contains(rowSeries)) {
449            if (row.VisualProperties.ChartType == DataRowVisualProperties.DataRowChartType.Histogram) {
450              rowSeries.Points.Clear();
451              FillSeriesWithRowValues(rowSeries, row);
452            } else {
453              foreach (IndexedItem<Tuple<T, double>> item in e.Items) {
454                rowSeries.Points[item.Index].SetValueXY(item.Value.Item1, item.Value.Item2);
455              }
456            }
457            RecalculateAxesScale(chart.ChartAreas[0]);
458            UpdateYCursorInterval();
459          }
460        }
461      }
462    }
463    private void Values_ItemsMoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Tuple<T, double>>> e) {
464      if (InvokeRequired)
465        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_ItemsMoved), sender, e);
466      else {
467        IndexedDataRow<T> row = null;
468        valuesRowsTable.TryGetValue((IObservableList<Tuple<T, double>>)sender, out row);
469        if (row != null) {
470          Series rowSeries = chart.Series[row.Name];
471          if (!invisibleSeries.Contains(rowSeries)) {
472            rowSeries.Points.Clear();
473            FillSeriesWithRowValues(rowSeries, row);
474            RecalculateAxesScale(chart.ChartAreas[0]);
475            UpdateYCursorInterval();
476          }
477        }
478      }
479    }
480
481    private void Values_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<Tuple<T, double>>> e) {
482      if (InvokeRequired)
483        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Tuple<T, double>>>(Values_CollectionReset), sender, e);
484      else {
485        IndexedDataRow<T> row = null;
486        valuesRowsTable.TryGetValue((IObservableList<Tuple<T, double>>)sender, out row);
487        if (row != null) {
488          Series rowSeries = chart.Series[row.Name];
489          if (!invisibleSeries.Contains(rowSeries)) {
490            rowSeries.Points.Clear();
491            FillSeriesWithRowValues(rowSeries, row);
492            RecalculateAxesScale(chart.ChartAreas[0]);
493            UpdateYCursorInterval();
494          }
495        }
496      }
497    }
498    #endregion
499    #endregion
500
501    #region Chart Event Handlers
502    private void chart_MouseDown(object sender, MouseEventArgs e) {
503      HitTestResult result = chart.HitTest(e.X, e.Y);
504      if (result.ChartElementType == ChartElementType.LegendItem) {
505        ToggleSeriesVisible(result.Series);
506      }
507    }
508    private void chart_MouseMove(object sender, MouseEventArgs e) {
509      HitTestResult result = chart.HitTest(e.X, e.Y);
510      if (result.ChartElementType == ChartElementType.LegendItem)
511        this.Cursor = Cursors.Hand;
512      else
513        this.Cursor = Cursors.Default;
514    }
515    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
516      foreach (LegendItem legendItem in e.LegendItems) {
517        var series = chart.Series[legendItem.SeriesName];
518        if (series != null) {
519          bool seriesIsInvisible = invisibleSeries.Contains(series);
520          foreach (LegendCell cell in legendItem.Cells) {
521            cell.ForeColor = seriesIsInvisible ? Color.Gray : Color.Black;
522          }
523        }
524      }
525    }
526    #endregion
527
528    private void ToggleSeriesVisible(Series series) {
529      if (!invisibleSeries.Contains(series)) {
530        series.Points.Clear();
531        invisibleSeries.Add(series);
532      } else {
533        invisibleSeries.Remove(series);
534        if (Content != null) {
535
536          var row = (from r in Content.Rows
537                     where r.Name == series.Name
538                     select r).Single();
539          FillSeriesWithRowValues(series, row);
540          this.chart.Legends[series.Legend].ForeColor = Color.Black;
541          RecalculateAxesScale(chart.ChartAreas[0]);
542          UpdateYCursorInterval();
543        }
544      }
545    }
546
547    private void FillSeriesWithRowValues(Series series, IndexedDataRow<T> row) {
548      for (int i = 0; i < row.Values.Count; i++) {
549        var value = row.Values[i];
550        DataPoint point = new DataPoint();
551        point.SetValueXY(value.Item1, value.Item2);
552        series.Points.Add(point);
553      }
554    }
555
556    #region Helpers
557    protected void RemoveCustomPropertyIfExists(Series series, string property) {
558      if (series.IsCustomPropertySet(property)) series.DeleteCustomProperty(property);
559    }
560
561    private ChartDashStyle ConvertLineStyle(DataRowVisualProperties.DataRowLineStyle dataRowLineStyle) {
562      switch (dataRowLineStyle) {
563        case DataRowVisualProperties.DataRowLineStyle.Dash:
564          return ChartDashStyle.Dash;
565        case DataRowVisualProperties.DataRowLineStyle.DashDot:
566          return ChartDashStyle.DashDot;
567        case DataRowVisualProperties.DataRowLineStyle.DashDotDot:
568          return ChartDashStyle.DashDotDot;
569        case DataRowVisualProperties.DataRowLineStyle.Dot:
570          return ChartDashStyle.Dot;
571        case DataRowVisualProperties.DataRowLineStyle.NotSet:
572          return ChartDashStyle.NotSet;
573        case DataRowVisualProperties.DataRowLineStyle.Solid:
574          return ChartDashStyle.Solid;
575        default:
576          return ChartDashStyle.NotSet;
577      }
578    }
579
580    /// <summary>
581    /// Determines whether a double value can be displayed (converted to Decimal and not an NaN).
582    /// </summary>
583    /// <param name="x">The number to check.</param>
584    /// <returns><code>true</code> if the value can be safely shwon in the chart,
585    /// <code>false</code> otherwise.</returns>
586    protected static bool IsInvalidValue(double x) {
587      return double.IsNaN(x) || x < (double)decimal.MinValue || x > (double)decimal.MaxValue;
588    }
589    #endregion
590  }
591}
Note: See TracBrowser for help on using the repository browser.