Free cookie consent management tool by TermsFeed Policy Generator

source: branches/symbreg-factors-2650/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionTargetResponseGradientView.cs @ 14277

Last change on this file since 14277 was 14277, checked in by gkronber, 8 years ago

#2650: merged r14245:14273 from trunk to branch (fixing conflicts in RegressionSolutionTargetResponseGradientView)

File size: 22.4 KB
RevLine 
[13817]1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
[13828]22using System;
[14248]23using System.Collections;
[13817]24using System.Collections.Generic;
[13850]25using System.Drawing;
[14277]26using System.Globalization;
[13808]27using System.Linq;
[13843]28using System.Threading.Tasks;
[13808]29using System.Windows.Forms;
30using HeuristicLab.Common;
31using HeuristicLab.MainForm;
[13843]32using HeuristicLab.Visualization.ChartControlsExtensions;
[13808]33
34namespace HeuristicLab.Problems.DataAnalysis.Views {
[14014]35  [View("Target Response Gradients")]
[13808]36  [Content(typeof(IRegressionSolution))]
37  public partial class RegressionSolutionTargetResponseGradientView : DataAnalysisSolutionEvaluationView {
[14248]38    private readonly Dictionary<string, IGradientChart> gradientCharts;
[13850]39    private readonly Dictionary<string, DensityChart> densityCharts;
40    private readonly Dictionary<string, Panel> groupingPanels;
[14277]41    private ModifiableDataset sharedFixedVariables;
[13850]42
[13831]43    private const int Points = 200;
[13995]44    private int MaxColumns = 4;
[13808]45
[13850]46    private IEnumerable<string> VisibleVariables {
47      get {
48        foreach (ListViewItem item in variableListView.CheckedItems)
49          yield return item.Text;
50      }
[13843]51    }
[14248]52    private IEnumerable<IGradientChart> VisibleGradientCharts {
[13850]53      get { return VisibleVariables.Select(v => gradientCharts[v]); }
54    }
55    private IEnumerable<DensityChart> VisibleDensityCharts {
56      get { return VisibleVariables.Select(v => densityCharts[v]); }
57    }
58    private IEnumerable<Panel> VisibleChartsPanels {
59      get { return VisibleVariables.Select(v => groupingPanels[v]); }
60    }
[13843]61
[13808]62    public RegressionSolutionTargetResponseGradientView() {
63      InitializeComponent();
[14248]64      gradientCharts = new Dictionary<string, IGradientChart>();
[13850]65      densityCharts = new Dictionary<string, DensityChart>();
66      groupingPanels = new Dictionary<string, Panel>();
[13846]67
68      limitView.Content = new DoubleLimit(0, 1);
[14021]69      limitView.Content.ValueChanged += limit_ValueChanged;
[13850]70
[14021]71      densityComboBox.SelectedIndex = 1; // select Training
72
[13850]73      // Avoid additional horizontal scrollbar
74      var vertScrollWidth = SystemInformation.VerticalScrollBarWidth;
75      scrollPanel.Padding = new Padding(0, 0, vertScrollWidth, 0);
76      scrollPanel.AutoScroll = true;
[13808]77    }
78
79    public new IRegressionSolution Content {
80      get { return (IRegressionSolution)base.Content; }
[13831]81      set { base.Content = value; }
[13808]82    }
83
84    protected override void RegisterContentEvents() {
85      base.RegisterContentEvents();
[13995]86      Content.ModelChanged += solution_ModelChanged;
[13808]87    }
88
89    protected override void DeregisterContentEvents() {
[13995]90      Content.ModelChanged -= solution_ModelChanged;
[13808]91      base.DeregisterContentEvents();
92    }
93
94    protected override void OnContentChanged() {
95      base.OnContentChanged();
[13817]96      if (Content == null) return;
[13831]97      var problemData = Content.ProblemData;
[13846]98
99      // Init Y-axis range
100      double min = double.MaxValue, max = double.MinValue;
101      var trainingTarget = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.TrainingIndices);
102      foreach (var t in trainingTarget) {
103        if (t < min) min = t;
104        if (t > max) max = t;
105      }
106      double range = max - min;
107      const double scale = 1.0 / 3.0;
108      double axisMin, axisMax, axisInterval;
109      ChartUtil.CalculateAxisInterval(min - scale * range, max + scale * range, 5, out axisMin, out axisMax, out axisInterval);
110      automaticYAxisCheckBox.Checked = false;
111      limitView.ReadOnly = false;
112      limitView.Content.Lower = axisMin;
113      limitView.Content.Upper = axisMax;
114
[13828]115      // create dataset
[13845]116      var allowedInputVariables = Content.ProblemData.AllowedInputVariables;
[14248]117      var doubleVariables = allowedInputVariables.Where(problemData.Dataset.VariableHasType<double>);
118      var doubleVariableValues = (IEnumerable<IList>)doubleVariables.Select(x => new List<double> { problemData.Dataset.GetDoubleValues(x, problemData.TrainingIndices).Median() });
[13850]119
[14248]120      var factorVariables = allowedInputVariables.Where(problemData.Dataset.VariableHasType<string>);
121      var factorVariableValues = (IEnumerable<IList>)factorVariables.Select(x => new List<string> {
122        problemData.Dataset.GetStringValues(x, problemData.TrainingIndices)
123        .GroupBy(val => val).OrderByDescending(g => g.Count()).First().Key // most frequent value
124      });
125
[14277]126      if (sharedFixedVariables != null)
127        sharedFixedVariables.ItemChanged += SharedFixedVariables_ItemChanged;
[14248]128
[14277]129      sharedFixedVariables = new ModifiableDataset(doubleVariables.Concat(factorVariables), doubleVariableValues.Concat(factorVariableValues));
[14248]130
[14277]131
[13850]132      // create controls
133      gradientCharts.Clear();
134      densityCharts.Clear();
135      groupingPanels.Clear();
[14248]136      foreach (var variableName in doubleVariables) {
[13850]137        var gradientChart = CreateGradientChart(variableName, sharedFixedVariables);
138        gradientCharts.Add(variableName, gradientChart);
139
140        var densityChart = new DensityChart() {
141          Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right,
142          Margin = Padding.Empty,
143          Height = 12,
[14131]144          Visible = false,
145          Top = (int)(gradientChart.Height * 0.1),
[13850]146        };
147        densityCharts.Add(variableName, densityChart);
148
[14089]149        gradientChart.ZoomChanged += (o, e) => {
150          var gradient = (GradientChart)o;
151          var density = densityCharts[gradient.FreeVariable];
152          density.Visible = densityComboBox.SelectedIndex != 0 && !gradient.IsZoomed;
153          if (density.Visible)
154            UpdateDensityChart(density, gradient.FreeVariable);
155        };
[14131]156        gradientChart.SizeChanged += (o, e) => {
157          var gradient = (GradientChart)o;
158          var density = densityCharts[gradient.FreeVariable];
159          density.Top = (int)(gradient.Height * 0.1);
160        };
[14089]161
[14158]162        // Initially, the inner plot areas are not initialized for hidden charts (scollpanel, ...)
163        // This event handler listens for the paint event once (where everything is already initialized) to do some manual layouting.
[14248]164        gradientChart.ChartPostPaint += OnGradientChartPostPaint;
[14158]165
[13850]166        var panel = new Panel() {
[13820]167          Dock = DockStyle.Fill,
168          Margin = Padding.Empty,
[13850]169          BackColor = Color.White
[13820]170        };
[13843]171
[13850]172        panel.Controls.Add(densityChart);
173        panel.Controls.Add(gradientChart);
174        groupingPanels.Add(variableName, panel);
[13808]175      }
[14248]176      foreach (var variableName in factorVariables) {
177        var gradientChart = CreateFactorGradientChart(variableName, sharedFixedVariables);
178        gradientCharts.Add(variableName, gradientChart);
[13850]179
[14248]180        var densityChart = new DensityChart() {
181          Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right,
182          Margin = Padding.Empty,
183          Height = 12,
184          Visible = false,
185          Top = (int)(gradientChart.Height * 0.1),
186        };
187        densityCharts.Add(variableName, densityChart);
188        gradientChart.ZoomChanged += (o, e) => {
189          var gradient = (FactorGradientChart)o;
190          var density = densityCharts[gradient.FreeVariable];
191          density.Visible = densityComboBox.SelectedIndex != 0 && !gradient.IsZoomed;
192          if (density.Visible)
193            UpdateDensityChart(density, gradient.FreeVariable);
194        };
195        gradientChart.SizeChanged += (o, e) => {
196          var gradient = (FactorGradientChart)o;
197          var density = densityCharts[gradient.FreeVariable];
198          density.Top = (int)(gradient.Height * 0.1);
199        };
200
201        // Initially, the inner plot areas are not initialized for hidden charts (scollpanel, ...)
202        // This event handler listens for the paint event once (where everything is already initialized) to do some manual layouting.
203        gradientChart.ChartPostPaint += OnFactorGradientChartPostPaint;
204
205        var panel = new Panel() {
206          Dock = DockStyle.Fill,
207          Margin = Padding.Empty,
208          BackColor = Color.White
209        };
210
211        panel.Controls.Add(densityChart);
212        panel.Controls.Add(gradientChart);
213        groupingPanels.Add(variableName, panel);
214      }
[13828]215      // update variable list
[14021]216      variableListView.ItemChecked -= variableListView_ItemChecked;
[13828]217      variableListView.Items.Clear();
[13845]218      foreach (var variable in allowedInputVariables)
219        variableListView.Items.Add(key: variable, text: variable, imageIndex: 0);
220
[13948]221      foreach (var variable in Content.Model.VariablesUsedForPrediction)
[13845]222        variableListView.Items[variable].Checked = true;
[14021]223      variableListView.ItemChecked += variableListView_ItemChecked;
224
[14277]225      sharedFixedVariables.ItemChanged += SharedFixedVariables_ItemChanged;
226
[14021]227      RecalculateAndRelayoutCharts();
[13808]228    }
[14021]229
[14277]230    private void SharedFixedVariables_ItemChanged(object sender, EventArgs<int, int> e) {
231      double yValue = Content.Model.GetEstimatedValues(sharedFixedVariables, new[] { 0 }).Single();
232      string title = Content.ProblemData.TargetVariable + ": " + yValue.ToString("G5", CultureInfo.CurrentCulture);
233      foreach (var chart in gradientCharts.Values) {
234        if (!string.IsNullOrEmpty(chart.YAxisTitle)) { // only show title for first column in grid
235          chart.YAxisTitle = title;
236        }
237      }
238    }
239
240
[14248]241    private void OnGradientChartPostPaint(object o, EventArgs e) {
[14158]242      var gradient = (GradientChart)o;
243      var density = densityCharts[gradient.FreeVariable];
244
245      density.Width = gradient.Width;
246
247      var gcPlotPosition = gradient.InnerPlotPosition;
248      density.Left = (int)(gcPlotPosition.X / 100.0 * gradient.Width);
249      density.Width = (int)(gcPlotPosition.Width / 100.0 * gradient.Width);
250      gradient.UpdateTitlePosition();
251
252      // removed after succesful layouting due to performance reasons
253      if (gcPlotPosition.Width != 0)
[14248]254        gradient.ChartPostPaint -= OnGradientChartPostPaint;
[14158]255    }
256
[14248]257    private void OnFactorGradientChartPostPaint(object o, EventArgs e) {
258      var gradient = (FactorGradientChart)o;
259      var density = densityCharts[gradient.FreeVariable];
260
261      density.Width = gradient.Width;
262
263      var gcPlotPosition = gradient.InnerPlotPosition;
264      density.Left = (int)(gcPlotPosition.X / 100.0 * gradient.Width);
265      density.Width = (int)(gcPlotPosition.Width / 100.0 * gradient.Width);
266      gradient.UpdateTitlePosition();
267
268      // removed after succesful layouting due to performance reasons
269      if (gcPlotPosition.Width != 0)
270        gradient.ChartPostPaint -= OnFactorGradientChartPostPaint;
271    }
272
[14021]273    private async void RecalculateAndRelayoutCharts() {
274      foreach (var variable in VisibleVariables) {
275        var gradientChart = gradientCharts[variable];
[14248]276        await gradientChart.RecalculateAsync(false, false);
[14021]277      }
278      gradientChartTableLayout.SuspendLayout();
279      SetupYAxis();
280      ReOrderControls();
281      SetStyles();
282      gradientChartTableLayout.ResumeLayout();
283      gradientChartTableLayout.Refresh();
284      foreach (var variable in VisibleVariables) {
[14248]285        DensityChart densityChart;
286        if (densityCharts.TryGetValue(variable, out densityChart)) {
287          UpdateDensityChart(densityChart, variable);
288        }
[14021]289      }
290    }
[13850]291    private GradientChart CreateGradientChart(string variableName, ModifiableDataset sharedFixedVariables) {
292      var gradientChart = new GradientChart {
293        Dock = DockStyle.Fill,
294        Margin = Padding.Empty,
295        ShowLegend = false,
296        ShowCursor = true,
[13855]297        ShowConfigButton = false,
[13850]298        YAxisTicks = 5,
299      };
300      gradientChart.VariableValueChanged += async (o, e) => {
[14248]301        var recalculations = VisibleGradientCharts
302          .Except(new[] { (IGradientChart)o })
303          .Select(async chart => {
304            await chart.RecalculateAsync(updateOnFinish: false, resetYAxis: false);
305          }).ToList();
[13850]306        await Task.WhenAll(recalculations);
[13843]307
[13850]308        if (recalculations.All(t => t.IsCompleted))
309          SetupYAxis();
310      };
311      gradientChart.Configure(new[] { Content }, sharedFixedVariables, variableName, Points);
[13995]312      gradientChart.SolutionAdded += gradientChart_SolutionAdded;
313      gradientChart.SolutionRemoved += gradientChart_SolutionRemoved;
[13850]314      return gradientChart;
315    }
[14248]316    private FactorGradientChart CreateFactorGradientChart(string variableName, ModifiableDataset sharedFixedVariables) {
317      var gradientChart = new FactorGradientChart {
318        Dock = DockStyle.Fill,
319        Margin = Padding.Empty,
320        ShowLegend = false,
321        ShowCursor = true,
322        YAxisTicks = 5,
323      };
324      gradientChart.VariableValueChanged += async (o, e) => {
325        var recalculations = VisibleGradientCharts
326          .Except(new[] { (FactorGradientChart)o })
327          .Select(async chart => {
328            await chart.RecalculateAsync(updateOnFinish: false, resetYAxis: false);
329          }).ToList();
330        await Task.WhenAll(recalculations);
[13850]331
[14248]332        if (recalculations.All(t => t.IsCompleted))
333          SetupYAxis();
334      };
335      var variableValues = Content.ProblemData.Dataset.GetStringValues(variableName).Distinct().OrderBy(n => n).ToList();
336      gradientChart.Configure(new[] { Content }, sharedFixedVariables, variableName, variableValues);
337      gradientChart.SolutionAdded += gradientChart_SolutionAdded;
338      gradientChart.SolutionRemoved += gradientChart_SolutionRemoved;
339      return gradientChart;
340    }
[13846]341    private void SetupYAxis() {
342      double axisMin, axisMax;
343      if (automaticYAxisCheckBox.Checked) {
344        double min = double.MaxValue, max = double.MinValue;
[13850]345        foreach (var chart in VisibleGradientCharts) {
[13846]346          if (chart.YMin < min) min = chart.YMin;
347          if (chart.YMax > max) max = chart.YMax;
348        }
349
350        double axisInterval;
351        ChartUtil.CalculateAxisInterval(min, max, 5, out axisMin, out axisMax, out axisInterval);
352      } else {
353        axisMin = limitView.Content.Lower;
354        axisMax = limitView.Content.Upper;
[13843]355      }
356
[13850]357      foreach (var chart in VisibleGradientCharts) {
[13843]358        chart.FixedYAxisMin = axisMin;
359        chart.FixedYAxisMax = axisMax;
360      }
361    }
362
[13850]363    // reorder chart controls so that they always appear in the same order as in the list view
364    // the table layout containing the controls should be suspended before calling this method
365    private void ReOrderControls() {
[13828]366      var tl = gradientChartTableLayout;
367      tl.Controls.Clear();
[13850]368      int row = 0, column = 0;
[14277]369      double yValue = Content.Model.GetEstimatedValues(sharedFixedVariables, new[] { 0 }).Single();
370      string title = Content.ProblemData.TargetVariable + ": " + yValue.ToString("G5", CultureInfo.CurrentCulture);
371
[14014]372      foreach (var v in VisibleVariables) {
373        var chartsPanel = groupingPanels[v];
[13850]374        tl.Controls.Add(chartsPanel, column, row);
[14014]375
[14021]376        var chart = gradientCharts[v];
[14277]377        chart.YAxisTitle = column == 0 ? title : string.Empty;
[13850]378        column++;
[14014]379
[13850]380        if (column == MaxColumns) {
381          row++;
382          column = 0;
383        }
384      }
[13828]385    }
[13808]386
[13995]387    private void SetStyles() {
388      var tl = gradientChartTableLayout;
389      tl.RowStyles.Clear();
390      tl.ColumnStyles.Clear();
391      int numVariables = VisibleVariables.Count();
392      if (numVariables == 0)
393        return;
394
395      // set column styles
396      tl.ColumnCount = Math.Min(numVariables, MaxColumns);
397      for (int c = 0; c < tl.ColumnCount; c++)
398        tl.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100.0f / tl.ColumnCount));
399
400      // set row styles
401      tl.RowCount = (int)Math.Ceiling((double)numVariables / tl.ColumnCount);
402      var columnWidth = tl.Width / tl.ColumnCount; // assume all columns have the same width
403      var rowHeight = (int)(0.8 * columnWidth);
404      for (int r = 0; r < tl.RowCount; r++)
405        tl.RowStyles.Add(new RowStyle(SizeType.Absolute, rowHeight));
406    }
407
408    private async void gradientChart_SolutionAdded(object sender, EventArgs<IRegressionSolution> e) {
409      var solution = e.Value;
410      foreach (var chart in gradientCharts.Values) {
411        if (sender == chart) continue;
412        await chart.AddSolutionAsync(solution);
413      }
414    }
415
416    private async void gradientChart_SolutionRemoved(object sender, EventArgs<IRegressionSolution> e) {
417      var solution = e.Value;
418      foreach (var chart in gradientCharts.Values) {
419        if (sender == chart) continue;
420        await chart.RemoveSolutionAsync(solution);
421      }
422    }
423
[13842]424    private async void variableListView_ItemChecked(object sender, ItemCheckedEventArgs e) {
[13808]425      var item = e.Item;
426      var variable = item.Text;
[13850]427      var gradientChart = gradientCharts[variable];
428      var chartsPanel = groupingPanels[variable];
[13817]429      var tl = gradientChartTableLayout;
[13850]430
[13817]431      tl.SuspendLayout();
[13808]432      if (item.Checked) {
[13850]433        tl.Controls.Add(chartsPanel);
[14248]434        await gradientChart.RecalculateAsync(false, false);
[13808]435      } else {
[13850]436        tl.Controls.Remove(chartsPanel);
[13808]437      }
[13843]438
[13850]439      if (tl.Controls.Count > 0) {
440        SetupYAxis();
441        ReOrderControls();
[13995]442        SetStyles();
[13828]443      }
[13817]444      tl.ResumeLayout();
[14021]445      tl.Refresh();
446      densityComboBox_SelectedIndexChanged(this, EventArgs.Empty);
[13808]447    }
[13846]448
449    private void automaticYAxisCheckBox_CheckedChanged(object sender, EventArgs e) {
450      limitView.ReadOnly = automaticYAxisCheckBox.Checked;
451      SetupYAxis();
[14021]452      gradientChartTableLayout.Refresh();
453      densityComboBox_SelectedIndexChanged(this, EventArgs.Empty); // necessary to realign the density plots
[13846]454    }
455
456    private void limit_ValueChanged(object sender, EventArgs e) {
457      if (automaticYAxisCheckBox.Checked)
458        return;
459      SetupYAxis();
[14021]460      gradientChartTableLayout.Refresh();
461      densityComboBox_SelectedIndexChanged(this, EventArgs.Empty); // necessary to realign the density plots
[13846]462    }
[13850]463
464    private void densityComboBox_SelectedIndexChanged(object sender, EventArgs e) {
[14021]465      if (Content == null)
466        return;
467
[13850]468      int si = densityComboBox.SelectedIndex;
469      if (si == 0) {
470        foreach (var densityChart in densityCharts.Values)
471          densityChart.Visible = false;
472      } else {
473        var indices = GetDensityIndices(si).ToList();
474
475        foreach (var entry in densityCharts) {
476          var variableName = entry.Key;
477          var densityChart = entry.Value;
[14089]478          if (!VisibleVariables.Contains(variableName) || gradientCharts[variableName].IsZoomed)
[13850]479            continue;
[14089]480
[13850]481          UpdateDensityChart(densityChart, variableName, indices);
482        }
483      }
484    }
485    private IEnumerable<int> GetDensityIndices(int selectedIndex) {
486      var problemData = Content.ProblemData;
487      return
488        selectedIndex == 1 ? problemData.TrainingIndices :
489        selectedIndex == 2 ? problemData.TestIndices :
490        problemData.AllIndices;
491    }
492    private void UpdateDensityChart(DensityChart densityChart, string variable, IList<int> indices = null) {
493      if (densityComboBox.SelectedIndex == 0)
494        return;
495      if (indices == null) {
496        indices = GetDensityIndices(densityComboBox.SelectedIndex).ToList();
497      }
[14248]498      if (Content.ProblemData.Dataset.VariableHasType<double>(variable)) {
499        var data = Content.ProblemData.Dataset.GetDoubleValues(variable, indices).ToList();
500        var gradientChart = gradientCharts[variable] as GradientChart;
501        if (gradientChart != null) {
502          var min = gradientChart.FixedXAxisMin;
503          var max = gradientChart.FixedXAxisMax;
504          var buckets = gradientChart.DrawingSteps;
505          if (min.HasValue && max.HasValue) {
506            densityChart.UpdateChart(data, min.Value, max.Value, buckets);
507            densityChart.Width = gradientChart.Width;
[13850]508
[14248]509            var gcPlotPosition = gradientChart.InnerPlotPosition;
510            densityChart.Left = (int)(gcPlotPosition.X / 100.0 * gradientChart.Width);
511            densityChart.Width = (int)(gcPlotPosition.Width / 100.0 * gradientChart.Width);
[13850]512
[14248]513            densityChart.Visible = true;
514          }
515          gradientChart.UpdateTitlePosition();
516        }
517      } else if (Content.ProblemData.Dataset.VariableHasType<string>(variable)) {
518        var data = Content.ProblemData.Dataset.GetStringValues(variable).ToList();
519        var gradientChart = gradientCharts[variable] as FactorGradientChart;
520        if (gradientChart != null) {
521          densityChart.UpdateChart(data);
522          densityChart.Width = gradientChart.Width;
523
524          var gcPlotPosition = gradientChart.InnerPlotPosition;
525          densityChart.Left = (int)(gcPlotPosition.X / 100.0 * gradientChart.Width);
526          densityChart.Width = (int)(gcPlotPosition.Width / 100.0 * gradientChart.Width);
527
528          densityChart.Visible = true;
529
530          gradientChart.UpdateTitlePosition();
531        }
[13850]532      }
533    }
[13995]534
[14089]535    private void columnsNumericUpDown_ValueChanged(object sender, EventArgs e) {
536      MaxColumns = (int)columnsNumericUpDown.Value;
[13995]537      int columns = Math.Min(VisibleVariables.Count(), MaxColumns);
538      if (columns > 0) {
539        var tl = gradientChartTableLayout;
540        MaxColumns = columns;
541        tl.SuspendLayout();
542        ReOrderControls();
543        SetStyles();
[14021]544        tl.ResumeLayout();
545        tl.Refresh();
546        densityComboBox_SelectedIndexChanged(this, EventArgs.Empty);
[13995]547      }
548    }
549
[14021]550    private async void solution_ModelChanged(object sender, EventArgs e) {
551      foreach (var variable in VisibleVariables) {
552        var gradientChart = gradientCharts[variable];
553        var densityChart = densityCharts[variable];
554        // recalculate and refresh
[14248]555        await gradientChart.RecalculateAsync(false, false);
[14021]556        gradientChart.Refresh();
557        UpdateDensityChart(densityChart, variable);
[13996]558      }
[13995]559    }
[13808]560  }
561}
Note: See TracBrowser for help on using the repository browser.