Index: trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/ConfidenceBoundRegressionSolutionEstimatedValuesView.Designer.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/ConfidenceBoundRegressionSolutionEstimatedValuesView.Designer.cs	(revision 14095)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/ConfidenceBoundRegressionSolutionEstimatedValuesView.Designer.cs	(revision 14095)
@@ -0,0 +1,62 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+namespace HeuristicLab.Algorithms.DataAnalysis.Views {
+  partial class ConfidenceBoundRegressionSolutionEstimatedValuesView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.SuspendLayout();
+      // 
+      // EstimatedValuesView
+      // 
+      this.AllowDrop = true;
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
+      this.Name = "EstimatedValuesView";
+      this.Size = new System.Drawing.Size(310, 285);
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+  }
+}
Index: trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/ConfidenceBoundRegressionSolutionEstimatedValuesView.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/ConfidenceBoundRegressionSolutionEstimatedValuesView.cs	(revision 14095)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/ConfidenceBoundRegressionSolutionEstimatedValuesView.cs	(revision 14095)
@@ -0,0 +1,80 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+using System.Linq;
+using System.Windows.Forms;
+using HeuristicLab.Data;
+using HeuristicLab.MainForm;
+using HeuristicLab.Problems.DataAnalysis;
+using HeuristicLab.Problems.DataAnalysis.Views;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.Views {
+  [View("Estimated Values")]
+  [Content(typeof(IConfidenceBoundRegressionSolution), false)]
+  public partial class ConfidenceBoundRegressionSolutionEstimatedValuesView : RegressionSolutionEstimatedValuesView {
+    private const string ESTIMATEDVARIANCES_SERIES_NAME = "Estimated Variances (all)";
+    private const string ESTIMATEDVARIANCES_TRAINING_SERIES_NAME = "Estimated Variances (training)";
+    private const string ESTIMATEDVARIANCES_TEST_SERIES_NAME = "Estimated Variances (test)";
+
+    public new IConfidenceBoundRegressionSolution Content {
+      get { return (IConfidenceBoundRegressionSolution)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public ConfidenceBoundRegressionSolutionEstimatedValuesView()
+      : base() {
+      InitializeComponent();
+    }
+
+
+    protected override StringMatrix CreateValueMatrix() {
+      var matrix = base.CreateValueMatrix();
+
+      var columnNames = matrix.ColumnNames.Concat(new[] { ESTIMATEDVARIANCES_SERIES_NAME, ESTIMATEDVARIANCES_TRAINING_SERIES_NAME, ESTIMATEDVARIANCES_TEST_SERIES_NAME }).ToList();
+      ((IStringConvertibleMatrix)matrix).Columns += 3;
+      matrix.ColumnNames = columnNames;
+
+      var trainingRows = Content.ProblemData.TrainingIndices;
+      var testRows = Content.ProblemData.TestIndices;
+
+      var estimated_var = Content.EstimatedVariances.GetEnumerator();
+      var estimated_var_training = Content.GetEstimatedVariances(trainingRows).GetEnumerator();
+      var estimated_var_test = Content.GetEstimatedVariances(testRows).GetEnumerator();
+
+      foreach (var row in Enumerable.Range(0, Content.ProblemData.Dataset.Rows)) {
+        estimated_var.MoveNext();
+        matrix[row, 7] = estimated_var.Current.ToString();
+      }
+
+      foreach (var row in Content.ProblemData.TrainingIndices) {
+        estimated_var_training.MoveNext();
+        matrix[row, 8] = estimated_var_training.Current.ToString();
+      }
+
+      foreach (var row in Content.ProblemData.TestIndices) {
+        estimated_var_test.MoveNext();
+        matrix[row, 9] = estimated_var_test.Current.ToString();
+      }
+
+
+      return matrix;
+    }
+  }
+}
Index: trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/ConfidenceBoundRegressionSolutionLineChartView.Designer.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/ConfidenceBoundRegressionSolutionLineChartView.Designer.cs	(revision 14095)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/ConfidenceBoundRegressionSolutionLineChartView.Designer.cs	(revision 14095)
@@ -0,0 +1,87 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+namespace HeuristicLab.Algorithms.DataAnalysis.Views {
+  partial class ConfidenceBasedRegressionSolutionLineChartView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
+      System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend();
+      this.chart = new HeuristicLab.Visualization.ChartControlsExtensions.EnhancedChart();
+      ((System.ComponentModel.ISupportInitialize)(this.chart)).BeginInit();
+      this.SuspendLayout();
+      // 
+      // chart
+      // 
+      chartArea1.Name = "ChartArea";
+      this.chart.ChartAreas.Add(chartArea1);
+      this.chart.Dock = System.Windows.Forms.DockStyle.Fill;
+      legend1.Alignment = System.Drawing.StringAlignment.Center;
+      legend1.Docking = System.Windows.Forms.DataVisualization.Charting.Docking.Top;
+      legend1.Name = "Default";
+      this.chart.Legends.Add(legend1);
+      this.chart.Location = new System.Drawing.Point(0, 0);
+      this.chart.Name = "chart";
+      this.chart.Size = new System.Drawing.Size(358, 225);
+      this.chart.TabIndex = 0;
+      this.chart.CustomizeLegend += new System.EventHandler<System.Windows.Forms.DataVisualization.Charting.CustomizeLegendEventArgs>(this.chart_CustomizeLegend);
+      this.chart.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.Chart_MouseDoubleClick);
+      this.chart.MouseDown += new System.Windows.Forms.MouseEventHandler(this.chart_MouseDown);
+      this.chart.MouseMove += new System.Windows.Forms.MouseEventHandler(this.chart_MouseMove);
+      // 
+      // RegressionSolutionLineChartView
+      // 
+      this.AllowDrop = true;
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
+      this.Controls.Add(this.chart);
+      this.Name = "RegressionSolutionLineChartView";
+      this.Size = new System.Drawing.Size(358, 225);
+      ((System.ComponentModel.ISupportInitialize)(this.chart)).EndInit();
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+    private HeuristicLab.Visualization.ChartControlsExtensions.EnhancedChart chart;
+  }
+}
Index: trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/ConfidenceBoundRegressionSolutionLineChartView.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/ConfidenceBoundRegressionSolutionLineChartView.cs	(revision 14095)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/ConfidenceBoundRegressionSolutionLineChartView.cs	(revision 14095)
@@ -0,0 +1,337 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Windows.Forms;
+using System.Windows.Forms.DataVisualization.Charting;
+using HeuristicLab.MainForm;
+using HeuristicLab.Problems.DataAnalysis;
+using HeuristicLab.Problems.DataAnalysis.Views;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.Views {
+  [View("Line Chart (95% confidence interval)")]
+  [Content(typeof(IConfidenceBoundRegressionSolution))]
+  public partial class ConfidenceBasedRegressionSolutionLineChartView : DataAnalysisSolutionEvaluationView {
+    private const string TARGETVARIABLE_SERIES_NAME = "Target Variable";
+    private const string ESTIMATEDVALUES_TRAINING_SERIES_NAME = "Estimated Values (training)";
+    private const string ESTIMATEDVALUES_TEST_SERIES_NAME = "Estimated Values (test)";
+    private const string ESTIMATEDVALUES_ALL_SERIES_NAME = "Estimated Values (all samples)";
+
+    public new IConfidenceBoundRegressionSolution Content {
+      get { return (IConfidenceBoundRegressionSolution)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public ConfidenceBasedRegressionSolutionLineChartView()
+      : base() {
+      InitializeComponent();
+      //configure axis
+      this.chart.CustomizeAllChartAreas();
+      this.chart.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
+      this.chart.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
+      this.chart.ChartAreas[0].AxisX.IsStartedFromZero = true;
+      this.chart.ChartAreas[0].CursorX.Interval = 1;
+
+      this.chart.ChartAreas[0].CursorY.IsUserSelectionEnabled = true;
+      this.chart.ChartAreas[0].AxisY.ScaleView.Zoomable = true;
+      this.chart.ChartAreas[0].CursorY.Interval = 0;
+    }
+
+    private void RedrawChart() {
+      this.chart.Series.Clear();
+      if (Content != null) {
+        this.chart.ChartAreas[0].AxisX.Minimum = 0;
+        this.chart.ChartAreas[0].AxisX.Maximum = Content.ProblemData.Dataset.Rows - 1;
+
+        // training series
+        this.chart.Series.Add(ESTIMATEDVALUES_TRAINING_SERIES_NAME);
+        this.chart.Series[ESTIMATEDVALUES_TRAINING_SERIES_NAME].LegendText = ESTIMATEDVALUES_TRAINING_SERIES_NAME;
+        this.chart.Series[ESTIMATEDVALUES_TRAINING_SERIES_NAME].ChartType = SeriesChartType.Range;
+        this.chart.Series[ESTIMATEDVALUES_TRAINING_SERIES_NAME].EmptyPointStyle.Color = this.chart.Series[ESTIMATEDVALUES_TRAINING_SERIES_NAME].Color;
+        var mean = Content.EstimatedTrainingValues.ToArray();
+        var s2 = Content.EstimatedTrainingVariances.ToArray();
+        var lower = mean.Zip(s2, GetLowerConfBound).ToArray();
+        var upper = mean.Zip(s2, GetUpperConfBound).ToArray();
+        this.chart.Series[ESTIMATEDVALUES_TRAINING_SERIES_NAME].Points.DataBindXY(Content.ProblemData.TrainingIndices.ToArray(), lower, upper);
+        this.InsertEmptyPoints(this.chart.Series[ESTIMATEDVALUES_TRAINING_SERIES_NAME]);
+        this.chart.Series[ESTIMATEDVALUES_TRAINING_SERIES_NAME].Tag = Content;
+
+        // test series
+        this.chart.Series.Add(ESTIMATEDVALUES_TEST_SERIES_NAME);
+        this.chart.Series[ESTIMATEDVALUES_TEST_SERIES_NAME].LegendText = ESTIMATEDVALUES_TEST_SERIES_NAME;
+        this.chart.Series[ESTIMATEDVALUES_TEST_SERIES_NAME].ChartType = SeriesChartType.Range;
+
+        mean = Content.EstimatedTestValues.ToArray();
+        s2 = Content.EstimatedTestVariances.ToArray();
+        lower = mean.Zip(s2, GetLowerConfBound).ToArray();
+        upper = mean.Zip(s2, GetUpperConfBound).ToArray();
+        this.chart.Series[ESTIMATEDVALUES_TEST_SERIES_NAME].Points.DataBindXY(Content.ProblemData.TestIndices.ToArray(), lower, upper);
+        this.InsertEmptyPoints(this.chart.Series[ESTIMATEDVALUES_TEST_SERIES_NAME]);
+        this.chart.Series[ESTIMATEDVALUES_TEST_SERIES_NAME].Tag = Content;
+
+        // series of remaining points
+        int[] allIndices = Enumerable.Range(0, Content.ProblemData.Dataset.Rows).Except(Content.ProblemData.TrainingIndices).Except(Content.ProblemData.TestIndices).ToArray();
+        mean = Content.EstimatedValues.ToArray();
+        s2 = Content.EstimatedVariances.ToArray();
+        lower = mean.Zip(s2, GetLowerConfBound).ToArray();
+        upper = mean.Zip(s2, GetUpperConfBound).ToArray();
+        List<double> allLower = allIndices.Select(index => lower[index]).ToList();
+        List<double> allUpper = allIndices.Select(index => upper[index]).ToList();
+        this.chart.Series.Add(ESTIMATEDVALUES_ALL_SERIES_NAME);
+        this.chart.Series[ESTIMATEDVALUES_ALL_SERIES_NAME].LegendText = ESTIMATEDVALUES_ALL_SERIES_NAME;
+        this.chart.Series[ESTIMATEDVALUES_ALL_SERIES_NAME].ChartType = SeriesChartType.Range;
+        if (allIndices.Count() > 0) {
+          this.chart.Series[ESTIMATEDVALUES_ALL_SERIES_NAME].Points.DataBindXY(allIndices, allLower, allUpper);
+          this.InsertEmptyPoints(this.chart.Series[ESTIMATEDVALUES_ALL_SERIES_NAME]);
+        }
+        this.chart.Series[ESTIMATEDVALUES_ALL_SERIES_NAME].Tag = Content;
+
+        // target
+        this.chart.Series.Add(TARGETVARIABLE_SERIES_NAME);
+        this.chart.Series[TARGETVARIABLE_SERIES_NAME].LegendText = Content.ProblemData.TargetVariable;
+        this.chart.Series[TARGETVARIABLE_SERIES_NAME].ChartType = SeriesChartType.FastLine;
+        this.chart.Series[TARGETVARIABLE_SERIES_NAME].Points.DataBindXY(Enumerable.Range(0, Content.ProblemData.Dataset.Rows).ToArray(),
+          Content.ProblemData.Dataset.GetDoubleValues(Content.ProblemData.TargetVariable).ToArray());
+
+        this.ToggleSeriesData(this.chart.Series[ESTIMATEDVALUES_ALL_SERIES_NAME]);
+
+
+        // the series have been added in different order than in the normal line chart 
+        // --> adapt coloring;
+        chart.ApplyPaletteColors();
+        this.chart.Palette = ChartColorPalette.None;
+        var s0Color = chart.Series[0].Color;
+        var s1Color = chart.Series[1].Color;
+        var s2Color = chart.Series[2].Color;
+        var s3Color = chart.Series[3].Color;
+        this.chart.PaletteCustomColors = new Color[] { s1Color, s2Color, s3Color, s0Color };
+
+        UpdateCursorInterval();
+        this.UpdateStripLines();
+      }
+    }
+
+    private void InsertEmptyPoints(Series series) {
+      int i = 0;
+      while (i < series.Points.Count - 1) {
+        if (series.Points[i].IsEmpty) {
+          ++i;
+          continue;
+        }
+
+        var p1 = series.Points[i];
+        var p2 = series.Points[i + 1];
+        // check for consecutive indices
+        if ((int)p2.XValue - (int)p1.XValue != 1) {
+          // insert an empty point between p1 and p2 so that the line will be invisible (transparent)
+          var p = new DataPoint((int)((p1.XValue + p2.XValue) / 2), new double[] { 0.0, 0.0 }) { IsEmpty = true };
+          // insert 
+          series.Points.Insert(i + 1, p);
+        }
+        ++i;
+      }
+    }
+
+    private void UpdateCursorInterval() {
+      var estimatedValues = this.chart.Series[ESTIMATEDVALUES_TRAINING_SERIES_NAME].Points.Select(x => x.YValues[0]).DefaultIfEmpty(1.0);
+      var targetValues = this.chart.Series[TARGETVARIABLE_SERIES_NAME].Points.Select(x => x.YValues[0]).DefaultIfEmpty(1.0);
+      double estimatedValuesRange = estimatedValues.Max() - estimatedValues.Min();
+      double targetValuesRange = targetValues.Max() - targetValues.Min();
+      double interestingValuesRange = Math.Min(Math.Max(targetValuesRange, 1.0), Math.Max(estimatedValuesRange, 1.0));
+      double digits = (int)Math.Log10(interestingValuesRange) - 3;
+      double yZoomInterval = Math.Max(Math.Pow(10, digits), 10E-5);
+      this.chart.ChartAreas[0].CursorY.Interval = yZoomInterval;
+    }
+
+    #region events
+    protected override void RegisterContentEvents() {
+      base.RegisterContentEvents();
+      Content.ModelChanged += new EventHandler(Content_ModelChanged);
+      Content.ProblemDataChanged += new EventHandler(Content_ProblemDataChanged);
+    }
+    protected override void DeregisterContentEvents() {
+      base.DeregisterContentEvents();
+      Content.ModelChanged -= new EventHandler(Content_ModelChanged);
+      Content.ProblemDataChanged -= new EventHandler(Content_ProblemDataChanged);
+    }
+
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      RedrawChart();
+    }
+    private void Content_ProblemDataChanged(object sender, EventArgs e) {
+      RedrawChart();
+    }
+    private void Content_ModelChanged(object sender, EventArgs e) {
+      RedrawChart();
+    }
+
+
+
+    private void Chart_MouseDoubleClick(object sender, MouseEventArgs e) {
+      HitTestResult result = chart.HitTest(e.X, e.Y);
+      if (result.ChartArea != null && (result.ChartElementType == ChartElementType.PlottingArea ||
+                                       result.ChartElementType == ChartElementType.Gridlines) ||
+                                       result.ChartElementType == ChartElementType.StripLines) {
+        foreach (var axis in result.ChartArea.Axes)
+          axis.ScaleView.ZoomReset(int.MaxValue);
+      }
+    }
+    #endregion
+
+    private void UpdateStripLines() {
+      this.chart.ChartAreas[0].AxisX.StripLines.Clear();
+
+      int[] attr = new int[Content.ProblemData.Dataset.Rows + 1]; // add a virtual last row that is again empty to simplify loop further down
+      foreach (var row in Content.ProblemData.TrainingIndices) {
+        attr[row] += 1;
+      }
+      foreach (var row in Content.ProblemData.TestIndices) {
+        attr[row] += 2;
+      }
+      int start = 0;
+      int curAttr = attr[start];
+      for (int row = 0; row < attr.Length; row++) {
+        if (attr[row] != curAttr) {
+          switch (curAttr) {
+            case 0: break;
+            case 1:
+              this.CreateAndAddStripLine("Training", start, row, Color.FromArgb(40, Color.Green), Color.Transparent);
+              break;
+            case 2:
+              this.CreateAndAddStripLine("Test", start, row, Color.FromArgb(40, Color.Red), Color.Transparent);
+              break;
+            case 3:
+              this.CreateAndAddStripLine("Training and Test", start, row, Color.FromArgb(40, Color.Green), Color.FromArgb(40, Color.Red), ChartHatchStyle.WideUpwardDiagonal);
+              break;
+            default:
+              // should not happen
+              break;
+          }
+          curAttr = attr[row];
+          start = row;
+        }
+      }
+    }
+
+    private void CreateAndAddStripLine(string title, int start, int end, Color color, Color secondColor, ChartHatchStyle hatchStyle = ChartHatchStyle.None) {
+      StripLine stripLine = new StripLine();
+      stripLine.BackColor = color;
+      stripLine.BackSecondaryColor = secondColor;
+      stripLine.BackHatchStyle = hatchStyle;
+      stripLine.Text = title;
+      stripLine.Font = new Font("Times New Roman", 12, FontStyle.Bold);
+      // strip range is [start .. end] inclusive, but we evaluate [start..end[ (end is exclusive)
+      // the strip should be by one longer (starting at start - 0.5 and ending at end + 0.5)
+      stripLine.StripWidth = end - start;
+      stripLine.IntervalOffset = start - 0.5; // start slightly to the left of the first point to clearly indicate the first point in the partition
+      this.chart.ChartAreas[0].AxisX.StripLines.Add(stripLine);
+    }
+
+    private void ToggleSeriesData(Series series) {
+      if (series.Points.Count > 0) {  //checks if series is shown
+        if (this.chart.Series.Any(s => s != series && s.Points.Count > 0)) {
+          ClearPointsQuick(series.Points);
+        }
+      } else if (Content != null) {
+
+        IEnumerable<int> indices = null;
+        IEnumerable<double> mean = null;
+        IEnumerable<double> s2 = null;
+        double[] lower = null;
+        double[] upper = null;
+        switch (series.Name) {
+          case ESTIMATEDVALUES_ALL_SERIES_NAME:
+            indices = Enumerable.Range(0, Content.ProblemData.Dataset.Rows).Except(Content.ProblemData.TrainingIndices).Except(Content.ProblemData.TestIndices).ToArray();
+            mean = Content.EstimatedValues.ToArray();
+            s2 = Content.EstimatedVariances.ToArray();
+            lower = mean.Zip(s2, GetLowerConfBound).ToArray();
+            upper = mean.Zip(s2, GetUpperConfBound).ToArray();
+            lower = indices.Select(index => lower[index]).ToArray();
+            upper = indices.Select(index => upper[index]).ToArray();
+            break;
+          case ESTIMATEDVALUES_TRAINING_SERIES_NAME:
+            indices = Content.ProblemData.TrainingIndices.ToArray();
+            mean = Content.EstimatedTrainingValues.ToArray();
+            s2 = Content.EstimatedTrainingVariances.ToArray();
+            lower = mean.Zip(s2, GetLowerConfBound).ToArray();
+            upper = mean.Zip(s2, GetUpperConfBound).ToArray();
+            break;
+          case ESTIMATEDVALUES_TEST_SERIES_NAME:
+            indices = Content.ProblemData.TestIndices.ToArray();
+            mean = Content.EstimatedTestValues.ToArray();
+            s2 = Content.EstimatedTestVariances.ToArray();
+            lower = mean.Zip(s2, GetLowerConfBound).ToArray();
+            upper = mean.Zip(s2, GetUpperConfBound).ToArray();
+            break;
+        }
+        if (indices.Count() > 0) {
+          series.Points.DataBindXY(indices, lower, upper);
+          this.InsertEmptyPoints(series);
+          chart.Legends[series.Legend].ForeColor = Color.Black;
+          UpdateCursorInterval();
+          chart.Refresh();
+        }
+      }
+    }
+
+    private double GetLowerConfBound(double m, double s) {
+      return m - 1.96 * Math.Sqrt(s);
+    }
+
+
+    private double GetUpperConfBound(double m, double s) {
+      return m + 1.96 * Math.Sqrt(s);
+    }
+
+    // workaround as per http://stackoverflow.com/questions/5744930/datapointcollection-clear-performance
+    private static void ClearPointsQuick(DataPointCollection points) {
+      points.SuspendUpdates();
+      while (points.Count > 0)
+        points.RemoveAt(points.Count - 1);
+      points.ResumeUpdates();
+    }
+
+    private void chart_MouseMove(object sender, MouseEventArgs e) {
+      HitTestResult result = chart.HitTest(e.X, e.Y);
+      if (result.ChartElementType == ChartElementType.LegendItem && result.Series.Name != TARGETVARIABLE_SERIES_NAME)
+        Cursor = Cursors.Hand;
+      else
+        Cursor = Cursors.Default;
+    }
+    private void chart_MouseDown(object sender, MouseEventArgs e) {
+      HitTestResult result = chart.HitTest(e.X, e.Y);
+      if (result.ChartElementType == ChartElementType.LegendItem && result.Series.Name != TARGETVARIABLE_SERIES_NAME) {
+        ToggleSeriesData(result.Series);
+      }
+    }
+
+    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
+      if (chart.Series.Count != 4) return;
+      e.LegendItems[0].Cells[1].ForeColor = this.chart.Series[ESTIMATEDVALUES_TRAINING_SERIES_NAME].Points.Count == 0 ? Color.Gray : Color.Black;
+      e.LegendItems[1].Cells[1].ForeColor = this.chart.Series[ESTIMATEDVALUES_TEST_SERIES_NAME].Points.Count == 0 ? Color.Gray : Color.Black;
+      e.LegendItems[2].Cells[1].ForeColor = this.chart.Series[ESTIMATEDVALUES_ALL_SERIES_NAME].Points.Count == 0 ? Color.Gray : Color.Black;
+      e.LegendItems[3].Cells[1].ForeColor = this.chart.Series[TARGETVARIABLE_SERIES_NAME].Points.Count == 0 ? Color.Gray : Color.Black;
+    }
+  }
+}
Index: trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionGradientView.Designer.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionGradientView.Designer.cs	(revision 14095)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionGradientView.Designer.cs	(revision 14095)
@@ -0,0 +1,135 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+namespace HeuristicLab.Algorithms.DataAnalysis.Views {
+  partial class RegressionSolutionGradientView {
+    /// <summary> 
+    /// Required designer variableNames.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.splitContainer = new System.Windows.Forms.SplitContainer();
+      this.gradientChart = new HeuristicLab.Problems.DataAnalysis.Views.GradientChart();
+      this.configurationGroupBox = new System.Windows.Forms.GroupBox();
+      this.tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
+      this.splitContainer.Panel1.SuspendLayout();
+      this.splitContainer.Panel2.SuspendLayout();
+      this.splitContainer.SuspendLayout();
+      this.configurationGroupBox.SuspendLayout();
+      this.SuspendLayout();
+      // 
+      // splitContainer
+      // 
+      this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.splitContainer.Location = new System.Drawing.Point(0, 0);
+      this.splitContainer.Name = "splitContainer";
+      this.splitContainer.Orientation = System.Windows.Forms.Orientation.Horizontal;
+      // 
+      // splitContainer.Panel1
+      // 
+      this.splitContainer.Panel1.Controls.Add(this.gradientChart);
+      // 
+      // splitContainer.Panel2
+      // 
+      this.splitContainer.Panel2.Controls.Add(this.configurationGroupBox);
+      this.splitContainer.Size = new System.Drawing.Size(715, 591);
+      this.splitContainer.SplitterDistance = 376;
+      this.splitContainer.TabIndex = 1;
+      // 
+      // gradientChart
+      // 
+      this.gradientChart.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.gradientChart.DrawingSteps = 1000;
+      this.gradientChart.Location = new System.Drawing.Point(0, 0);
+      this.gradientChart.Name = "gradientChart";
+      this.gradientChart.ShowCursor = false;
+      this.gradientChart.ShowLegend = false;
+      this.gradientChart.Size = new System.Drawing.Size(715, 376);
+      this.gradientChart.TabIndex = 0;
+      this.gradientChart.XAxisTicks = 10;
+      this.gradientChart.YAxisTicks = 5;
+      // 
+      // configurationGroupBox
+      // 
+      this.configurationGroupBox.Controls.Add(this.tableLayoutPanel);
+      this.configurationGroupBox.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.configurationGroupBox.Location = new System.Drawing.Point(0, 0);
+      this.configurationGroupBox.Name = "configurationGroupBox";
+      this.configurationGroupBox.Size = new System.Drawing.Size(715, 211);
+      this.configurationGroupBox.TabIndex = 1;
+      this.configurationGroupBox.TabStop = false;
+      this.configurationGroupBox.Text = "Configuration";
+      // 
+      // tableLayoutPanel
+      // 
+      this.tableLayoutPanel.AutoScroll = true;
+      this.tableLayoutPanel.ColumnCount = 1;
+      this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
+      this.tableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.tableLayoutPanel.Location = new System.Drawing.Point(3, 16);
+      this.tableLayoutPanel.Name = "tableLayoutPanel";
+      this.tableLayoutPanel.RowCount = 1;
+      this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle());
+      this.tableLayoutPanel.Size = new System.Drawing.Size(709, 192);
+      this.tableLayoutPanel.TabIndex = 0;
+      // 
+      // RegressionSolutionGradientView
+      // 
+      this.AllowDrop = true;
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
+      this.Controls.Add(this.splitContainer);
+      this.Name = "RegressionSolutionGradientView";
+      this.Size = new System.Drawing.Size(715, 591);
+      this.splitContainer.Panel1.ResumeLayout(false);
+      this.splitContainer.Panel2.ResumeLayout(false);
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
+      this.splitContainer.ResumeLayout(false);
+      this.configurationGroupBox.ResumeLayout(false);
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+    private System.Windows.Forms.SplitContainer splitContainer;
+    private System.Windows.Forms.GroupBox configurationGroupBox;
+    private System.Windows.Forms.TableLayoutPanel tableLayoutPanel;
+    private Problems.DataAnalysis.Views.GradientChart gradientChart;
+  }
+}
Index: trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionGradientView.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionGradientView.cs	(revision 14095)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionGradientView.cs	(revision 14095)
@@ -0,0 +1,223 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Forms;
+using HeuristicLab.Collections;
+using HeuristicLab.Core.Views;
+using HeuristicLab.MainForm;
+using HeuristicLab.MainForm.WindowsForms;
+using HeuristicLab.Problems.DataAnalysis;
+using HeuristicLab.Visualization.ChartControlsExtensions;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.Views {
+  [View("Gradient View")]
+  [Content(typeof(IRegressionSolution))]
+  public partial class RegressionSolutionGradientView : ItemView {
+    private const int DrawingSteps = 1000;
+
+    private readonly List<string> variableNames;
+    private readonly ObservableList<DensityTrackbar> trackbars;
+    private ModifiableDataset sharedFixedVariables;
+
+    private int ActiveDimension {
+      get { return trackbars.FindIndex(tb => tb.Checked); }
+    }
+
+    public new IRegressionSolution Content {
+      get { return (IRegressionSolution)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public RegressionSolutionGradientView()
+      : base() {
+      variableNames = new List<string>();
+
+      trackbars = new ObservableList<DensityTrackbar>();
+      trackbars.ItemsAdded += (sender, args) => {
+        args.Items.Select(i => i.Value).ForEach(RegisterEvents);
+      };
+      trackbars.ItemsRemoved += (sender, args) => {
+        args.Items.Select(i => i.Value).ForEach(DeregisterEvents);
+      };
+      trackbars.CollectionReset += (sender, args) => {
+        args.OldItems.Select(i => i.Value).ForEach(DeregisterEvents);
+        args.Items.Select(i => i.Value).ForEach(RegisterEvents);
+      };
+
+      InitializeComponent();
+
+      // Avoid additional horizontal scrollbar
+      var vertScrollWidth = SystemInformation.VerticalScrollBarWidth;
+      tableLayoutPanel.Padding = new Padding(0, 0, vertScrollWidth, 0);
+    }
+
+    private async void UpdateConfigurationControls() {
+      variableNames.Clear();
+      trackbars.Clear();
+
+      tableLayoutPanel.SuspendRepaint();
+      tableLayoutPanel.SuspendLayout();
+
+      tableLayoutPanel.RowCount = 0;
+      tableLayoutPanel.Controls.Clear();
+
+      if (Content == null) {
+        tableLayoutPanel.ResumeLayout(false);
+        tableLayoutPanel.ResumeRepaint(false);
+        return;
+      }
+
+      variableNames.AddRange(Content.ProblemData.AllowedInputVariables);
+
+      var newTrackbars = CreateConfiguration();
+
+      sharedFixedVariables = new ModifiableDataset(variableNames, newTrackbars.Select(tb => new List<double>(1) { (double)tb.Value }));
+      gradientChart.Configure(new[] { Content }, sharedFixedVariables, variableNames.First(), DrawingSteps);
+      await gradientChart.RecalculateAsync();
+
+      // Add to table and observable lists
+      tableLayoutPanel.RowCount = variableNames.Count;
+      while (tableLayoutPanel.RowStyles.Count < variableNames.Count)
+        tableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
+      for (int i = 0; i < newTrackbars.Count; i++) {
+        // events registered automatically
+        trackbars.Add(newTrackbars[i]);
+        tableLayoutPanel.Controls.Add(newTrackbars[i], 0, i);
+      }
+
+      tableLayoutPanel.ResumeLayout(true);
+      tableLayoutPanel.ResumeRepaint(true);
+
+      // Init Y-axis range
+      var problemData = Content.ProblemData;
+      double min = double.MaxValue, max = double.MinValue;
+      var trainingTarget = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.TrainingIndices);
+      foreach (var t in trainingTarget) {
+        if (t < min) min = t;
+        if (t > max) max = t;
+      }
+      double range = max - min;
+      const double scale = 1.0 / 3.0;
+      double axisMin, axisMax, axisInterval;
+      ChartUtil.CalculateAxisInterval(min - scale * range, max + scale * range, 5, out axisMin, out axisMax, out axisInterval);
+      gradientChart.FixedYAxisMin = axisMin;
+      gradientChart.FixedYAxisMax = axisMax;
+
+      trackbars.First().Checked = true;
+    }
+
+    private IList<DensityTrackbar> CreateConfiguration() {
+      var ranges = new List<DoubleLimit>();
+      foreach (string variableName in variableNames) {
+        var values = Content.ProblemData.Dataset.GetDoubleValues(variableName, Content.ProblemData.AllIndices);
+        double min, max, interval;
+        ChartUtil.CalculateAxisInterval(values.Min(), values.Max(), 10, out min, out max, out interval);
+        ranges.Add(new DoubleLimit(min, max));
+      }
+
+      var newTrackbars = new List<DensityTrackbar>();
+      for (int i = 0; i < variableNames.Count; i++) {
+        var name = variableNames[i];
+        var trainingData = Content.ProblemData.Dataset.GetDoubleValues(name, Content.ProblemData.TrainingIndices).ToList();
+
+        var dimensionTrackbar = new DensityTrackbar(name, ranges[i], trainingData) {
+          Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right
+        };
+        newTrackbars.Add(dimensionTrackbar);
+      }
+
+      return newTrackbars;
+    }
+
+    private void RegisterEvents(DensityTrackbar trackbar) {
+      trackbar.CheckedChanged += trackbar_CheckedChanged;
+      trackbar.ValueChanged += trackbar_ValueChanged;
+      trackbar.LimitsChanged += trackbar_LimitsChanged;
+    }
+    private void DeregisterEvents(DensityTrackbar trackbar) {
+      trackbar.CheckedChanged -= trackbar_CheckedChanged;
+      trackbar.ValueChanged -= trackbar_ValueChanged;
+      trackbar.LimitsChanged -= trackbar_LimitsChanged;
+    }
+
+    private void trackbar_CheckedChanged(object sender, EventArgs e) {
+      var trackBar = sender as DensityTrackbar;
+      if (trackBar == null || !trackBar.Checked) return;
+      // Uncheck all others
+      foreach (var tb in trackbars.Except(new[] { trackBar }))
+        tb.Checked = false;
+      gradientChart.FreeVariable = variableNames[trackbars.IndexOf(trackBar)];
+      gradientChart.RecalculateAsync();
+    }
+
+    private void trackbar_LimitsChanged(object sender, EventArgs e) {
+      var trackBar = sender as DensityTrackbar;
+      if (trackBar == null || !trackBar.Checked) return;
+      gradientChart.FixedXAxisMin = trackBar.Limits.Lower;
+      gradientChart.FixedXAxisMax = trackBar.Limits.Upper;
+      gradientChart.RecalculateAsync();
+    }
+
+    private void trackbar_ValueChanged(object sender, EventArgs e) {
+      var trackBar = sender as DensityTrackbar;
+      if (trackBar == null) return;
+      sharedFixedVariables.SetVariableValue((double)trackBar.Value, variableNames[trackbars.IndexOf(trackBar)], 0);
+      gradientChart.RecalculateAsync();
+    }
+
+    #region Events
+    protected override void RegisterContentEvents() {
+      base.RegisterContentEvents();
+      Content.ModelChanged += Content_ModelChanged;
+      Content.ProblemDataChanged += Content_ProblemDataChanged;
+    }
+
+    protected override void DeregisterContentEvents() {
+      base.DeregisterContentEvents();
+      Content.ModelChanged -= Content_ModelChanged;
+      Content.ProblemDataChanged -= Content_ProblemDataChanged;
+    }
+
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      UpdateConfigurationControls();
+    }
+
+    private void Content_ModelChanged(object sender, EventArgs e) {
+      UpdateConfigurationControls();
+    }
+
+    private void Content_ProblemDataChanged(object sender, EventArgs e) {
+      UpdateConfigurationControls();
+    }
+    #endregion
+  }
+
+  internal static class Extensions {
+    public static void ForEach<T>(this IEnumerable<T> source, Action<T> action) {
+      foreach (T item in source)
+        action(item);
+    }
+  }
+}
Index: trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionTargetResponseGradientView.Designer.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionTargetResponseGradientView.Designer.cs	(revision 14095)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionTargetResponseGradientView.Designer.cs	(revision 14095)
@@ -0,0 +1,271 @@
+﻿namespace HeuristicLab.Problems.DataAnalysis.Views {
+  partial class RegressionSolutionTargetResponseGradientView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      this.variableListView = new System.Windows.Forms.ListView();
+      this.gradientChartTableLayout = new System.Windows.Forms.TableLayoutPanel();
+      this.yAxisConfigGroupBox = new System.Windows.Forms.GroupBox();
+      this.limitView = new HeuristicLab.Problems.DataAnalysis.Views.DoubleLimitView();
+      this.automaticYAxisCheckBox = new System.Windows.Forms.CheckBox();
+      this.densityGroupBox = new System.Windows.Forms.GroupBox();
+      this.columnsLabel = new System.Windows.Forms.Label();
+      this.densityComboBox = new System.Windows.Forms.ComboBox();
+      this.label1 = new System.Windows.Forms.Label();
+      this.configSplitContainer = new System.Windows.Forms.SplitContainer();
+      this.variableGroupBox = new System.Windows.Forms.GroupBox();
+      this.scrollPanel = new System.Windows.Forms.Panel();
+      this.errorProvider = new System.Windows.Forms.ErrorProvider(this.components);
+      this.columnsNumericUpDown = new System.Windows.Forms.NumericUpDown();
+      this.yAxisConfigGroupBox.SuspendLayout();
+      this.densityGroupBox.SuspendLayout();
+      ((System.ComponentModel.ISupportInitialize)(this.configSplitContainer)).BeginInit();
+      this.configSplitContainer.Panel1.SuspendLayout();
+      this.configSplitContainer.Panel2.SuspendLayout();
+      this.configSplitContainer.SuspendLayout();
+      this.variableGroupBox.SuspendLayout();
+      this.scrollPanel.SuspendLayout();
+      ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).BeginInit();
+      ((System.ComponentModel.ISupportInitialize)(this.columnsNumericUpDown)).BeginInit();
+      this.SuspendLayout();
+      // 
+      // variableListView
+      // 
+      this.variableListView.CheckBoxes = true;
+      this.variableListView.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.variableListView.Location = new System.Drawing.Point(3, 16);
+      this.variableListView.Name = "variableListView";
+      this.variableListView.Size = new System.Drawing.Size(163, 478);
+      this.variableListView.TabIndex = 0;
+      this.variableListView.UseCompatibleStateImageBehavior = false;
+      this.variableListView.View = System.Windows.Forms.View.List;
+      this.variableListView.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.variableListView_ItemChecked);
+      // 
+      // gradientChartTableLayout
+      // 
+      this.gradientChartTableLayout.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.gradientChartTableLayout.AutoSize = true;
+      this.gradientChartTableLayout.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
+      this.gradientChartTableLayout.ColumnCount = 1;
+      this.gradientChartTableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
+      this.gradientChartTableLayout.Location = new System.Drawing.Point(0, 0);
+      this.gradientChartTableLayout.Name = "gradientChartTableLayout";
+      this.gradientChartTableLayout.RowCount = 1;
+      this.gradientChartTableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+      this.gradientChartTableLayout.Size = new System.Drawing.Size(542, 0);
+      this.gradientChartTableLayout.TabIndex = 2;
+      // 
+      // yAxisConfigGroupBox
+      // 
+      this.yAxisConfigGroupBox.Controls.Add(this.limitView);
+      this.yAxisConfigGroupBox.Controls.Add(this.automaticYAxisCheckBox);
+      this.yAxisConfigGroupBox.Dock = System.Windows.Forms.DockStyle.Top;
+      this.yAxisConfigGroupBox.Location = new System.Drawing.Point(0, 0);
+      this.yAxisConfigGroupBox.Name = "yAxisConfigGroupBox";
+      this.yAxisConfigGroupBox.Size = new System.Drawing.Size(169, 77);
+      this.yAxisConfigGroupBox.TabIndex = 2;
+      this.yAxisConfigGroupBox.TabStop = false;
+      this.yAxisConfigGroupBox.Text = "Y-Axis";
+      // 
+      // limitView
+      // 
+      this.limitView.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.limitView.Caption = "DoubleLimit View";
+      this.limitView.Content = null;
+      this.limitView.Location = new System.Drawing.Point(6, 20);
+      this.limitView.Name = "limitView";
+      this.limitView.ReadOnly = false;
+      this.limitView.Size = new System.Drawing.Size(157, 47);
+      this.limitView.TabIndex = 1;
+      // 
+      // automaticYAxisCheckBox
+      // 
+      this.automaticYAxisCheckBox.AutoSize = true;
+      this.automaticYAxisCheckBox.Location = new System.Drawing.Point(49, -1);
+      this.automaticYAxisCheckBox.Name = "automaticYAxisCheckBox";
+      this.automaticYAxisCheckBox.Size = new System.Drawing.Size(73, 17);
+      this.automaticYAxisCheckBox.TabIndex = 0;
+      this.automaticYAxisCheckBox.Text = "Automatic";
+      this.automaticYAxisCheckBox.UseVisualStyleBackColor = true;
+      this.automaticYAxisCheckBox.CheckedChanged += new System.EventHandler(this.automaticYAxisCheckBox_CheckedChanged);
+      // 
+      // densityGroupBox
+      // 
+      this.densityGroupBox.Controls.Add(this.columnsNumericUpDown);
+      this.densityGroupBox.Controls.Add(this.columnsLabel);
+      this.densityGroupBox.Controls.Add(this.densityComboBox);
+      this.densityGroupBox.Controls.Add(this.label1);
+      this.densityGroupBox.Dock = System.Windows.Forms.DockStyle.Top;
+      this.densityGroupBox.Location = new System.Drawing.Point(0, 77);
+      this.densityGroupBox.Name = "densityGroupBox";
+      this.densityGroupBox.Size = new System.Drawing.Size(169, 74);
+      this.densityGroupBox.TabIndex = 3;
+      this.densityGroupBox.TabStop = false;
+      this.densityGroupBox.Text = "Settings";
+      // 
+      // columnsLabel
+      // 
+      this.columnsLabel.AutoSize = true;
+      this.columnsLabel.Location = new System.Drawing.Point(7, 49);
+      this.columnsLabel.Name = "columnsLabel";
+      this.columnsLabel.Size = new System.Drawing.Size(50, 13);
+      this.columnsLabel.TabIndex = 0;
+      this.columnsLabel.Text = "Columns:";
+      // 
+      // densityComboBox
+      // 
+      this.densityComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.densityComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+      this.densityComboBox.FormattingEnabled = true;
+      this.densityComboBox.Items.AddRange(new object[] {
+            "None",
+            "Training",
+            "Test",
+            "All"});
+      this.densityComboBox.Location = new System.Drawing.Point(66, 19);
+      this.densityComboBox.Name = "densityComboBox";
+      this.densityComboBox.Size = new System.Drawing.Size(94, 21);
+      this.densityComboBox.TabIndex = 0;
+      this.densityComboBox.SelectedIndexChanged += new System.EventHandler(this.densityComboBox_SelectedIndexChanged);
+      // 
+      // label1
+      // 
+      this.label1.AutoSize = true;
+      this.label1.Location = new System.Drawing.Point(7, 22);
+      this.label1.Name = "label1";
+      this.label1.Size = new System.Drawing.Size(45, 13);
+      this.label1.TabIndex = 0;
+      this.label1.Text = "Density:";
+      // 
+      // configSplitContainer
+      // 
+      this.configSplitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.configSplitContainer.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
+      this.configSplitContainer.Location = new System.Drawing.Point(0, 0);
+      this.configSplitContainer.Name = "configSplitContainer";
+      // 
+      // configSplitContainer.Panel1
+      // 
+      this.configSplitContainer.Panel1.Controls.Add(this.variableGroupBox);
+      this.configSplitContainer.Panel1.Controls.Add(this.densityGroupBox);
+      this.configSplitContainer.Panel1.Controls.Add(this.yAxisConfigGroupBox);
+      // 
+      // configSplitContainer.Panel2
+      // 
+      this.configSplitContainer.Panel2.Controls.Add(this.scrollPanel);
+      this.configSplitContainer.Size = new System.Drawing.Size(715, 648);
+      this.configSplitContainer.SplitterDistance = 169;
+      this.configSplitContainer.TabIndex = 0;
+      this.configSplitContainer.TabStop = false;
+      // 
+      // variableGroupBox
+      // 
+      this.variableGroupBox.Controls.Add(this.variableListView);
+      this.variableGroupBox.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.variableGroupBox.Location = new System.Drawing.Point(0, 151);
+      this.variableGroupBox.Name = "variableGroupBox";
+      this.variableGroupBox.Size = new System.Drawing.Size(169, 497);
+      this.variableGroupBox.TabIndex = 1;
+      this.variableGroupBox.TabStop = false;
+      this.variableGroupBox.Text = "Variables";
+      // 
+      // scrollPanel
+      // 
+      this.scrollPanel.Controls.Add(this.gradientChartTableLayout);
+      this.scrollPanel.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.scrollPanel.Location = new System.Drawing.Point(0, 0);
+      this.scrollPanel.Name = "scrollPanel";
+      this.scrollPanel.Size = new System.Drawing.Size(542, 648);
+      this.scrollPanel.TabIndex = 0;
+      // 
+      // errorProvider
+      // 
+      this.errorProvider.BlinkStyle = System.Windows.Forms.ErrorBlinkStyle.NeverBlink;
+      this.errorProvider.ContainerControl = this;
+      // 
+      // columnsNumericUpDown
+      // 
+      this.columnsNumericUpDown.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.columnsNumericUpDown.Location = new System.Drawing.Point(66, 46);
+      this.columnsNumericUpDown.Minimum = new decimal(new int[] {
+            1,
+            0,
+            0,
+            0});
+      this.columnsNumericUpDown.Name = "columnsNumericUpDown";
+      this.columnsNumericUpDown.Size = new System.Drawing.Size(94, 20);
+      this.columnsNumericUpDown.TabIndex = 1;
+      this.columnsNumericUpDown.Value = new decimal(new int[] {
+            4,
+            0,
+            0,
+            0});
+      this.columnsNumericUpDown.ValueChanged += new System.EventHandler(this.columnsNumericUpDown_ValueChanged);
+      // 
+      // RegressionSolutionTargetResponseGradientView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.configSplitContainer);
+      this.Name = "RegressionSolutionTargetResponseGradientView";
+      this.Size = new System.Drawing.Size(715, 648);
+      this.yAxisConfigGroupBox.ResumeLayout(false);
+      this.yAxisConfigGroupBox.PerformLayout();
+      this.densityGroupBox.ResumeLayout(false);
+      this.densityGroupBox.PerformLayout();
+      this.configSplitContainer.Panel1.ResumeLayout(false);
+      this.configSplitContainer.Panel2.ResumeLayout(false);
+      ((System.ComponentModel.ISupportInitialize)(this.configSplitContainer)).EndInit();
+      this.configSplitContainer.ResumeLayout(false);
+      this.variableGroupBox.ResumeLayout(false);
+      this.scrollPanel.ResumeLayout(false);
+      this.scrollPanel.PerformLayout();
+      ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).EndInit();
+      ((System.ComponentModel.ISupportInitialize)(this.columnsNumericUpDown)).EndInit();
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.ListView variableListView;
+    private System.Windows.Forms.TableLayoutPanel gradientChartTableLayout;
+    private System.Windows.Forms.GroupBox yAxisConfigGroupBox;
+    private System.Windows.Forms.CheckBox automaticYAxisCheckBox;
+    private DoubleLimitView limitView;
+    private System.Windows.Forms.GroupBox densityGroupBox;
+    private System.Windows.Forms.ComboBox densityComboBox;
+    private System.Windows.Forms.SplitContainer configSplitContainer;
+    private System.Windows.Forms.GroupBox variableGroupBox;
+    private System.Windows.Forms.Panel scrollPanel;
+    private System.Windows.Forms.Label columnsLabel;
+    private System.Windows.Forms.ErrorProvider errorProvider;
+    private System.Windows.Forms.Label label1;
+    private System.Windows.Forms.NumericUpDown columnsNumericUpDown;
+  }
+}
Index: trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionTargetResponseGradientView.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionTargetResponseGradientView.cs	(revision 14095)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionTargetResponseGradientView.cs	(revision 14095)
@@ -0,0 +1,402 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using HeuristicLab.Common;
+using HeuristicLab.MainForm;
+using HeuristicLab.Visualization.ChartControlsExtensions;
+
+namespace HeuristicLab.Problems.DataAnalysis.Views {
+  [View("Target Response Gradients")]
+  [Content(typeof(IRegressionSolution))]
+  public partial class RegressionSolutionTargetResponseGradientView : DataAnalysisSolutionEvaluationView {
+    private readonly Dictionary<string, GradientChart> gradientCharts;
+    private readonly Dictionary<string, DensityChart> densityCharts;
+    private readonly Dictionary<string, Panel> groupingPanels;
+
+    private const int Points = 200;
+    private int MaxColumns = 4;
+
+    private IEnumerable<string> VisibleVariables {
+      get {
+        foreach (ListViewItem item in variableListView.CheckedItems)
+          yield return item.Text;
+      }
+    }
+    private IEnumerable<GradientChart> VisibleGradientCharts {
+      get { return VisibleVariables.Select(v => gradientCharts[v]); }
+    }
+    private IEnumerable<DensityChart> VisibleDensityCharts {
+      get { return VisibleVariables.Select(v => densityCharts[v]); }
+    }
+    private IEnumerable<Panel> VisibleChartsPanels {
+      get { return VisibleVariables.Select(v => groupingPanels[v]); }
+    }
+
+    public RegressionSolutionTargetResponseGradientView() {
+      InitializeComponent();
+      gradientCharts = new Dictionary<string, GradientChart>();
+      densityCharts = new Dictionary<string, DensityChart>();
+      groupingPanels = new Dictionary<string, Panel>();
+
+      limitView.Content = new DoubleLimit(0, 1);
+      limitView.Content.ValueChanged += limit_ValueChanged;
+
+      densityComboBox.SelectedIndex = 1; // select Training
+
+      // Avoid additional horizontal scrollbar
+      var vertScrollWidth = SystemInformation.VerticalScrollBarWidth;
+      scrollPanel.Padding = new Padding(0, 0, vertScrollWidth, 0);
+      scrollPanel.AutoScroll = true;
+    }
+
+    public new IRegressionSolution Content {
+      get { return (IRegressionSolution)base.Content; }
+      set { base.Content = value; }
+    }
+
+    protected override void RegisterContentEvents() {
+      base.RegisterContentEvents();
+      Content.ModelChanged += solution_ModelChanged;
+    }
+
+    protected override void DeregisterContentEvents() {
+      Content.ModelChanged -= solution_ModelChanged;
+      base.DeregisterContentEvents();
+    }
+
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      if (Content == null) return;
+      var problemData = Content.ProblemData;
+
+      // Init Y-axis range
+      double min = double.MaxValue, max = double.MinValue;
+      var trainingTarget = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.TrainingIndices);
+      foreach (var t in trainingTarget) {
+        if (t < min) min = t;
+        if (t > max) max = t;
+      }
+      double range = max - min;
+      const double scale = 1.0 / 3.0;
+      double axisMin, axisMax, axisInterval;
+      ChartUtil.CalculateAxisInterval(min - scale * range, max + scale * range, 5, out axisMin, out axisMax, out axisInterval);
+      automaticYAxisCheckBox.Checked = false;
+      limitView.ReadOnly = false;
+      limitView.Content.Lower = axisMin;
+      limitView.Content.Upper = axisMax;
+
+      // create dataset
+      var allowedInputVariables = Content.ProblemData.AllowedInputVariables;
+      var variableValues = allowedInputVariables.Select(x => new List<double> { problemData.Dataset.GetDoubleValues(x, problemData.TrainingIndices).Median() });
+      var sharedFixedVariables = new ModifiableDataset(allowedInputVariables, variableValues);
+
+      // create controls
+      gradientCharts.Clear();
+      densityCharts.Clear();
+      groupingPanels.Clear();
+      foreach (var variableName in allowedInputVariables) {
+        var gradientChart = CreateGradientChart(variableName, sharedFixedVariables);
+        gradientCharts.Add(variableName, gradientChart);
+
+        var densityChart = new DensityChart() {
+          Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right,
+          Margin = Padding.Empty,
+          Height = 12,
+          Visible = false
+        };
+        densityCharts.Add(variableName, densityChart);
+
+        gradientChart.ZoomChanged += (o, e) => {
+          var gradient = (GradientChart)o;
+          var density = densityCharts[gradient.FreeVariable];
+          density.Visible = densityComboBox.SelectedIndex != 0 && !gradient.IsZoomed;
+          if (density.Visible)
+            UpdateDensityChart(density, gradient.FreeVariable);
+        };
+
+        var panel = new Panel() {
+          Dock = DockStyle.Fill,
+          Margin = Padding.Empty,
+          BackColor = Color.White
+        };
+
+        panel.Controls.Add(densityChart);
+        panel.Controls.Add(gradientChart);
+        groupingPanels.Add(variableName, panel);
+      }
+
+      // update variable list
+      variableListView.ItemChecked -= variableListView_ItemChecked;
+      variableListView.Items.Clear();
+      foreach (var variable in allowedInputVariables)
+        variableListView.Items.Add(key: variable, text: variable, imageIndex: 0);
+
+      foreach (var variable in Content.Model.VariablesUsedForPrediction)
+        variableListView.Items[variable].Checked = true;
+      variableListView.ItemChecked += variableListView_ItemChecked;
+
+      RecalculateAndRelayoutCharts();
+    }
+
+    private async void RecalculateAndRelayoutCharts() {
+      foreach (var variable in VisibleVariables) {
+        var gradientChart = gradientCharts[variable];
+        await gradientChart.RecalculateAsync();
+      }
+      gradientChartTableLayout.SuspendLayout();
+      SetupYAxis();
+      ReOrderControls();
+      SetStyles();
+      gradientChartTableLayout.ResumeLayout();
+      gradientChartTableLayout.Refresh();
+      foreach (var variable in VisibleVariables) {
+        var densityChart = densityCharts[variable];
+        UpdateDensityChart(densityChart, variable);
+      }
+    }
+    private GradientChart CreateGradientChart(string variableName, ModifiableDataset sharedFixedVariables) {
+      var gradientChart = new GradientChart {
+        Dock = DockStyle.Fill,
+        Margin = Padding.Empty,
+        ShowLegend = false,
+        ShowCursor = true,
+        ShowConfigButton = false,
+        YAxisTicks = 5,
+      };
+      gradientChart.VariableValueChanged += async (o, e) => {
+        var recalculations = VisibleGradientCharts.Except(new[] { (GradientChart)o }).Select(async chart => {
+          await chart.RecalculateAsync(updateOnFinish: false, resetYAxis: false);
+        }).ToList();
+        await Task.WhenAll(recalculations);
+
+        if (recalculations.All(t => t.IsCompleted))
+          SetupYAxis();
+      };
+      gradientChart.Configure(new[] { Content }, sharedFixedVariables, variableName, Points);
+      gradientChart.SolutionAdded += gradientChart_SolutionAdded;
+      gradientChart.SolutionRemoved += gradientChart_SolutionRemoved;
+      return gradientChart;
+    }
+
+    private void SetupYAxis() {
+      double axisMin, axisMax;
+      if (automaticYAxisCheckBox.Checked) {
+        double min = double.MaxValue, max = double.MinValue;
+        foreach (var chart in VisibleGradientCharts) {
+          if (chart.YMin < min) min = chart.YMin;
+          if (chart.YMax > max) max = chart.YMax;
+        }
+
+        double axisInterval;
+        ChartUtil.CalculateAxisInterval(min, max, 5, out axisMin, out axisMax, out axisInterval);
+      } else {
+        axisMin = limitView.Content.Lower;
+        axisMax = limitView.Content.Upper;
+      }
+
+      foreach (var chart in VisibleGradientCharts) {
+        chart.FixedYAxisMin = axisMin;
+        chart.FixedYAxisMax = axisMax;
+      }
+    }
+
+    // reorder chart controls so that they always appear in the same order as in the list view
+    // the table layout containing the controls should be suspended before calling this method
+    private void ReOrderControls() {
+      var tl = gradientChartTableLayout;
+      tl.Controls.Clear();
+      int row = 0, column = 0;
+      foreach (var v in VisibleVariables) {
+        var chartsPanel = groupingPanels[v];
+        tl.Controls.Add(chartsPanel, column, row);
+
+        var chart = gradientCharts[v];
+        chart.YAxisTitle = column == 0 ? Content.ProblemData.TargetVariable : string.Empty;
+        column++;
+
+        if (column == MaxColumns) {
+          row++;
+          column = 0;
+        }
+      }
+    }
+
+    private void SetStyles() {
+      var tl = gradientChartTableLayout;
+      tl.RowStyles.Clear();
+      tl.ColumnStyles.Clear();
+      int numVariables = VisibleVariables.Count();
+      if (numVariables == 0)
+        return;
+
+      // set column styles
+      tl.ColumnCount = Math.Min(numVariables, MaxColumns);
+      for (int c = 0; c < tl.ColumnCount; c++)
+        tl.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100.0f / tl.ColumnCount));
+
+      // set row styles
+      tl.RowCount = (int)Math.Ceiling((double)numVariables / tl.ColumnCount);
+      var columnWidth = tl.Width / tl.ColumnCount; // assume all columns have the same width
+      var rowHeight = (int)(0.8 * columnWidth);
+      for (int r = 0; r < tl.RowCount; r++)
+        tl.RowStyles.Add(new RowStyle(SizeType.Absolute, rowHeight));
+    }
+
+    private async void gradientChart_SolutionAdded(object sender, EventArgs<IRegressionSolution> e) {
+      var solution = e.Value;
+      foreach (var chart in gradientCharts.Values) {
+        if (sender == chart) continue;
+        await chart.AddSolutionAsync(solution);
+      }
+    }
+
+    private async void gradientChart_SolutionRemoved(object sender, EventArgs<IRegressionSolution> e) {
+      var solution = e.Value;
+      foreach (var chart in gradientCharts.Values) {
+        if (sender == chart) continue;
+        await chart.RemoveSolutionAsync(solution);
+      }
+    }
+
+    private async void variableListView_ItemChecked(object sender, ItemCheckedEventArgs e) {
+      var item = e.Item;
+      var variable = item.Text;
+      var gradientChart = gradientCharts[variable];
+      var chartsPanel = groupingPanels[variable];
+      var tl = gradientChartTableLayout;
+
+      tl.SuspendLayout();
+      if (item.Checked) {
+        tl.Controls.Add(chartsPanel);
+        await gradientChart.RecalculateAsync();
+      } else {
+        tl.Controls.Remove(chartsPanel);
+      }
+
+      if (tl.Controls.Count > 0) {
+        SetupYAxis();
+        ReOrderControls();
+        SetStyles();
+      }
+      tl.ResumeLayout();
+      tl.Refresh();
+      densityComboBox_SelectedIndexChanged(this, EventArgs.Empty);
+    }
+
+    private void automaticYAxisCheckBox_CheckedChanged(object sender, EventArgs e) {
+      limitView.ReadOnly = automaticYAxisCheckBox.Checked;
+      SetupYAxis();
+      gradientChartTableLayout.Refresh();
+      densityComboBox_SelectedIndexChanged(this, EventArgs.Empty); // necessary to realign the density plots
+    }
+
+    private void limit_ValueChanged(object sender, EventArgs e) {
+      if (automaticYAxisCheckBox.Checked)
+        return;
+      SetupYAxis();
+      gradientChartTableLayout.Refresh();
+      densityComboBox_SelectedIndexChanged(this, EventArgs.Empty); // necessary to realign the density plots
+    }
+
+    private void densityComboBox_SelectedIndexChanged(object sender, EventArgs e) {
+      if (Content == null)
+        return;
+
+      int si = densityComboBox.SelectedIndex;
+      if (si == 0) {
+        foreach (var densityChart in densityCharts.Values)
+          densityChart.Visible = false;
+      } else {
+        var indices = GetDensityIndices(si).ToList();
+
+        foreach (var entry in densityCharts) {
+          var variableName = entry.Key;
+          var densityChart = entry.Value;
+          if (!VisibleVariables.Contains(variableName) || gradientCharts[variableName].IsZoomed)
+            continue;
+
+          UpdateDensityChart(densityChart, variableName, indices);
+        }
+      }
+    }
+    private IEnumerable<int> GetDensityIndices(int selectedIndex) {
+      var problemData = Content.ProblemData;
+      return
+        selectedIndex == 1 ? problemData.TrainingIndices :
+        selectedIndex == 2 ? problemData.TestIndices :
+        problemData.AllIndices;
+    }
+    private void UpdateDensityChart(DensityChart densityChart, string variable, IList<int> indices = null) {
+      if (densityComboBox.SelectedIndex == 0)
+        return;
+      if (indices == null) {
+        indices = GetDensityIndices(densityComboBox.SelectedIndex).ToList();
+      }
+      var data = Content.ProblemData.Dataset.GetDoubleValues(variable, indices).ToList();
+      var gradientChart = gradientCharts[variable];
+      var min = gradientChart.FixedXAxisMin;
+      var max = gradientChart.FixedXAxisMax;
+      var buckets = gradientChart.DrawingSteps;
+      if (min.HasValue && max.HasValue) {
+        densityChart.UpdateChart(data, min.Value, max.Value, buckets);
+        densityChart.Width = gradientChart.Width;
+
+        var gcPlotPosition = gradientChart.InnerPlotPosition;
+        densityChart.Left = (int)(gcPlotPosition.X / 100.0 * gradientChart.Width);
+        densityChart.Width = (int)(gcPlotPosition.Width / 100.0 * gradientChart.Width);
+
+        densityChart.Visible = true;
+      }
+    }
+
+    private void columnsNumericUpDown_ValueChanged(object sender, EventArgs e) {
+      MaxColumns = (int)columnsNumericUpDown.Value;
+      int columns = Math.Min(VisibleVariables.Count(), MaxColumns);
+      if (columns > 0) {
+        var tl = gradientChartTableLayout;
+        MaxColumns = columns;
+        tl.SuspendLayout();
+        ReOrderControls();
+        SetStyles();
+        tl.ResumeLayout();
+        tl.Refresh();
+        densityComboBox_SelectedIndexChanged(this, EventArgs.Empty);
+      }
+    }
+
+    private async void solution_ModelChanged(object sender, EventArgs e) {
+      foreach (var variable in VisibleVariables) {
+        var gradientChart = gradientCharts[variable];
+        var densityChart = densityCharts[variable];
+        // recalculate and refresh
+        await gradientChart.RecalculateAsync();
+        gradientChart.Refresh();
+        UpdateDensityChart(densityChart, variable);
+      }
+    }
+  }
+}
