Free cookie consent management tool by TermsFeed Policy Generator

Ignore:
Timestamp:
05/02/13 13:34:32 (11 years ago)
Author:
bburlacu
Message:

#1772: Merged trunk changes for HeuristicLab.Optimization.Views.

Location:
branches/HeuristicLab.EvolutionaryTracking/HeuristicLab.Optimization.Views
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • branches/HeuristicLab.EvolutionaryTracking/HeuristicLab.Optimization.Views

  • branches/HeuristicLab.EvolutionaryTracking/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionBubbleChartView.cs

    r9240 r9421  
    2020#endregion
    2121
    22 using HeuristicLab.Common;
    23 using HeuristicLab.Core;
    24 using HeuristicLab.Data;
    25 using HeuristicLab.MainForm;
    26 using HeuristicLab.MainForm.WindowsForms;
    2722using System;
    2823using System.Collections.Generic;
     
    3126using System.Windows.Forms;
    3227using System.Windows.Forms.DataVisualization.Charting;
     28using HeuristicLab.Common;
     29using HeuristicLab.Core;
     30using HeuristicLab.Data;
     31using HeuristicLab.MainForm;
     32using HeuristicLab.MainForm.WindowsForms;
    3333
    3434namespace HeuristicLab.Optimization.Views {
     
    4343    private string sizeAxisValue;
    4444
    45     private Dictionary<IRun, List<DataPoint>> runToDataPointMapping;
    46     private Dictionary<int, Dictionary<object, double>> categoricalMapping;
    47     private Dictionary<IRun, double> xJitter;
    48     private Dictionary<IRun, double> yJitter;
     45    private readonly Dictionary<IRun, List<DataPoint>> runToDataPointMapping = new Dictionary<IRun, List<DataPoint>>();
     46    private readonly Dictionary<IRun, int> runToIndexMapping = new Dictionary<IRun, int>();
     47    private readonly Dictionary<int, Dictionary<object, double>> categoricalMapping = new Dictionary<int, Dictionary<object, double>>();
     48    private readonly Dictionary<IRun, double> xJitter = new Dictionary<IRun, double>();
     49    private readonly Dictionary<IRun, double> yJitter = new Dictionary<IRun, double>();
     50
     51    private readonly HashSet<IRun> selectedRuns = new HashSet<IRun>();
     52    private readonly Random random = new Random();
    4953    private double xJitterFactor = 0.0;
    5054    private double yJitterFactor = 0.0;
    51     private Random random;
    5255    private bool isSelecting = false;
    5356    private bool suppressUpdates = false;
     57
    5458
    5559    public RunCollectionBubbleChartView() {
     
    6165      chart.ContextMenuStrip.Opening += new System.ComponentModel.CancelEventHandler(ContextMenuStrip_Opening);
    6266
    63       runToDataPointMapping = new Dictionary<IRun, List<DataPoint>>();
    64       categoricalMapping = new Dictionary<int, Dictionary<object, double>>();
    65       xJitter = new Dictionary<IRun, double>();
    66       yJitter = new Dictionary<IRun, double>();
    67       random = new Random();
    68 
    6967      colorDialog.Color = Color.Black;
    70       colorButton.Image = this.GenerateImage(16, 16, this.colorDialog.Color);
     68      colorDialogButton.Image = this.GenerateImage(16, 16, this.colorDialog.Color);
    7169      isSelecting = false;
    7270
     
    8482    public IStringConvertibleMatrix Matrix {
    8583      get { return this.Content; }
     84    }
     85    public IEnumerable<IRun> SelectedRuns {
     86      get { return selectedRuns; }
    8687    }
    8788
     
    9394      Content.ItemsRemoved += new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_ItemsRemoved);
    9495      Content.CollectionReset += new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_CollectionReset);
     96      Content.OptimizerNameChanged += new EventHandler(Content_AlgorithmNameChanged);
    9597      Content.UpdateOfRunsInProgressChanged += new EventHandler(Content_UpdateOfRunsInProgressChanged);
    96       Content.OptimizerNameChanged += new EventHandler(Content_AlgorithmNameChanged);
    9798      RegisterRunEvents(Content);
    9899    }
     
    104105      Content.ItemsRemoved -= new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_ItemsRemoved);
    105106      Content.CollectionReset -= new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_CollectionReset);
     107      Content.OptimizerNameChanged -= new EventHandler(Content_AlgorithmNameChanged);
    106108      Content.UpdateOfRunsInProgressChanged -= new EventHandler(Content_UpdateOfRunsInProgressChanged);
    107       Content.OptimizerNameChanged -= new EventHandler(Content_AlgorithmNameChanged);
    108109      DeregisterRunEvents(Content);
    109110    }
     
    128129    }
    129130    private void run_Changed(object sender, EventArgs e) {
     131      if (suppressUpdates) return;
    130132      if (InvokeRequired)
    131133        this.Invoke(new EventHandler(run_Changed), sender, e);
     
    133135        IRun run = (IRun)sender;
    134136        UpdateRun(run);
     137        UpdateCursorInterval();
     138        chart.ChartAreas[0].RecalculateAxesScale();
     139        UpdateAxisLabels();
     140      }
     141    }
     142
     143    private void Content_UpdateOfRunsInProgressChanged(object sender, EventArgs e) {
     144      if (InvokeRequired)
     145        this.Invoke(new EventHandler(Content_UpdateOfRunsInProgressChanged), sender, e);
     146      else {
     147        suppressUpdates = Content.UpdateOfRunsInProgress;
     148        if (suppressUpdates) return;
     149
     150        foreach (var run in Content) UpdateRun(run);
     151        UpdateMarkerSizes();
     152        UpdateCursorInterval();
     153        chart.ChartAreas[0].RecalculateAxesScale();
     154        UpdateAxisLabels();
    135155      }
    136156    }
    137157
    138158    private void UpdateRun(IRun run) {
    139       if (!suppressUpdates) {
    140         if (runToDataPointMapping.ContainsKey(run)) {
    141           foreach (DataPoint point in runToDataPointMapping[run]) {
    142             point.Color = run.Color;
    143             if (!run.Visible) {
    144               this.chart.Series[0].Points.Remove(point);
    145               UpdateCursorInterval();
    146               chart.ChartAreas[0].RecalculateAxesScale();
    147             }
     159      if (runToDataPointMapping.ContainsKey(run)) {
     160        foreach (DataPoint point in runToDataPointMapping[run]) {
     161          if (!run.Visible) {
     162            this.chart.Series[0].Points.Remove(point);
     163            continue;
    148164          }
    149           if (!run.Visible) runToDataPointMapping.Remove(run);
    150         } else {
    151           AddDataPoint(run);
    152           UpdateCursorInterval();
    153           chart.ChartAreas[0].RecalculateAxesScale();
    154         }
    155 
    156         if (this.chart.Series[0].Points.Count == 0)
    157           noRunsLabel.Visible = true;
    158         else
    159           noRunsLabel.Visible = false;
    160       }
     165          if (selectedRuns.Contains(run)) {
     166            point.Color = Color.Red;
     167            point.MarkerStyle = MarkerStyle.Cross;
     168          } else {
     169            point.Color = Color.FromArgb(255 - transparencyTrackBar.Value, ((IRun)point.Tag).Color);
     170            point.MarkerStyle = MarkerStyle.Circle;
     171          }
     172
     173        }
     174        if (!run.Visible) runToDataPointMapping.Remove(run);
     175      } else {
     176        AddDataPoint(run);
     177      }
     178
     179      if (this.chart.Series[0].Points.Count == 0)
     180        noRunsLabel.Visible = true;
     181      else
     182        noRunsLabel.Visible = false;
    161183    }
    162184
     
    167189      UpdateDataPoints();
    168190      UpdateCaption();
    169     }
     191      RebuildInverseIndex();
     192    }
     193
     194    private void RebuildInverseIndex() {
     195      if (Content != null) {
     196        runToIndexMapping.Clear();
     197        int i = 0;
     198        foreach (var run in Content) {
     199          runToIndexMapping.Add(run, i);
     200          i++;
     201        }
     202      }
     203    }
     204
    170205    private void Content_ColumnNamesChanged(object sender, EventArgs e) {
    171206      if (InvokeRequired)
     
    210245          changed = true;
    211246        }
    212         if (changed)
     247        if (changed) {
    213248          UpdateDataPoints();
    214       }
    215     }
    216 
    217 
    218     private void Content_UpdateOfRunsInProgressChanged(object sender, EventArgs e) {
    219       if (InvokeRequired)
    220         Invoke(new EventHandler(Content_UpdateOfRunsInProgressChanged), sender, e);
    221       else {
    222         suppressUpdates = Content.UpdateOfRunsInProgress;
    223         if (!suppressUpdates) UpdateDataPoints();
     249          UpdateAxisLabels();
     250        }
    224251      }
    225252    }
     
    236263      else {
    237264        this.categoricalMapping.Clear();
     265        RebuildInverseIndex();
    238266        UpdateDataPoints();
     267        UpdateAxisLabels();
    239268      }
    240269    }
     
    244273      series.Points.Clear();
    245274      runToDataPointMapping.Clear();
     275      selectedRuns.Clear();
    246276
    247277      chart.ChartAreas[0].AxisX.IsMarginVisible = xAxisValue != AxisDimension.Index.ToString();
     
    272302
    273303    private void UpdateMarkerSizes() {
    274       double[] sizeValues = this.chart.Series[0].Points.Select(p => p.YValues[1]).ToArray();
     304      var series = chart.Series[0];
     305      if (series.Points.Count <= 0) return;
     306
     307      var sizeValues = series.Points.Select(p => p.YValues[1]);
    275308      double minSizeValue = sizeValues.Min();
    276309      double maxSizeValue = sizeValues.Max();
    277 
    278       for (int i = 0; i < sizeValues.Length; i++) {
    279         DataPoint point = this.chart.Series[0].Points[i];
    280         double sizeRange = maxSizeValue - minSizeValue;
     310      double sizeRange = maxSizeValue - minSizeValue;
     311
     312      const int smallestBubbleSize = 5;
     313
     314      foreach (DataPoint point in series.Points) {
     315        //calculates the relative size of the data point  0 <= relativeSize <= 1
    281316        double relativeSize = (point.YValues[1] - minSizeValue);
    282 
    283         if (sizeRange > double.Epsilon) relativeSize /= sizeRange;
    284         else relativeSize = 1;
    285 
    286         point.MarkerSize = (int)Math.Round((sizeTrackBar.Value - sizeTrackBar.Minimum) * relativeSize + sizeTrackBar.Minimum);
     317        if (sizeRange > double.Epsilon) {
     318          relativeSize /= sizeRange;
     319
     320          //invert bubble sizes if the value of the trackbar is negative
     321          if (sizeTrackBar.Value < 0) relativeSize = Math.Abs(relativeSize - 1);
     322        } else relativeSize = 1;
     323
     324        double sizeChange = Math.Abs(sizeTrackBar.Value) * relativeSize;
     325        point.MarkerSize = (int)Math.Round(sizeChange + smallestBubbleSize);
    287326      }
    288327    }
     
    344383      if (xValue.HasValue && yValue.HasValue && sizeValue.HasValue) {
    345384        xValue = xValue.Value;
    346 
    347385        yValue = yValue.Value;
    348386
     
    350388          DataPoint point = new DataPoint(xValue.Value, new double[] { yValue.Value, sizeValue.Value });
    351389          point.Tag = run;
    352           point.Color = run.Color;
    353390          series.Points.Add(point);
    354391          if (!runToDataPointMapping.ContainsKey(run)) runToDataPointMapping.Add(run, new List<DataPoint>());
    355392          runToDataPointMapping[run].Add(point);
     393          UpdateRun(run);
    356394        }
    357395      }
     
    391429    }
    392430    private double GetCategoricalValue(int dimension, string value) {
    393       if (!this.categoricalMapping.ContainsKey(dimension))
     431      if (!this.categoricalMapping.ContainsKey(dimension)) {
    394432        this.categoricalMapping[dimension] = new Dictionary<object, double>();
    395       if (!this.categoricalMapping[dimension].ContainsKey(value)) {
    396         if (this.categoricalMapping[dimension].Values.Count == 0)
    397           this.categoricalMapping[dimension][value] = 1.0;
    398         else
    399           this.categoricalMapping[dimension][value] = this.categoricalMapping[dimension].Values.Max() + 1.0;
     433        var orderedCategories = Content.Where(r => r.Visible).Select(r => Content.GetValue(r, dimension).ToString())
     434                                    .Distinct()
     435                                    .OrderBy(x => x, new NaturalStringComparer());
     436        int count = 1;
     437        foreach (var category in orderedCategories) {
     438          this.categoricalMapping[dimension].Add(category, count);
     439          count++;
     440        }
    400441      }
    401442      return this.categoricalMapping[dimension][value];
    402443    }
     444
    403445    private double GetValue(IRun run, AxisDimension axisDimension) {
    404446      double value = double.NaN;
    405447      switch (axisDimension) {
    406448        case AxisDimension.Index: {
    407             value = Content.ToList().IndexOf(run);
     449            value = runToIndexMapping[run];
    408450            break;
    409451          }
     
    428470    }
    429471    private void UpdateCursorInterval() {
    430       Series series = chart.Series[0];
    431       double[] xValues = (from point in series.Points
    432                           where !point.IsEmpty
    433                           select point.XValue)
    434                     .DefaultIfEmpty(1.0)
    435                     .ToArray();
    436       double[] yValues = (from point in series.Points
    437                           where !point.IsEmpty
    438                           select point.YValues[0])
    439                     .DefaultIfEmpty(1.0)
    440                     .ToArray();
    441 
    442       double xRange = xValues.Max() - xValues.Min();
    443       double yRange = yValues.Max() - yValues.Min();
     472      double xMin = double.MaxValue;
     473      double xMax = double.MinValue;
     474      double yMin = double.MaxValue;
     475      double yMax = double.MinValue;
     476
     477      foreach (var point in chart.Series[0].Points) {
     478        if (point.IsEmpty) continue;
     479        if (point.XValue < xMin) xMin = point.XValue;
     480        if (point.XValue > xMax) xMax = point.XValue;
     481        if (point.YValues[0] < yMin) yMin = point.YValues[0];
     482        if (point.YValues[0] > yMax) yMax = point.YValues[0];
     483      }
     484
     485      double xRange = 0.0;
     486      double yRange = 0.0;
     487      if (xMin != double.MaxValue && xMax != double.MinValue) xRange = xMax - xMin;
     488      if (yMin != double.MaxValue && yMax != double.MinValue) yRange = yMax - yMin;
     489
    444490      if (xRange.IsAlmost(0.0)) xRange = 1.0;
    445491      if (yRange.IsAlmost(0.0)) yRange = 1.0;
     
    479525
    480526    private void chart_MouseUp(object sender, MouseEventArgs e) {
    481       if (isSelecting) {
    482         System.Windows.Forms.DataVisualization.Charting.Cursor xCursor = chart.ChartAreas[0].CursorX;
    483         System.Windows.Forms.DataVisualization.Charting.Cursor yCursor = chart.ChartAreas[0].CursorY;
    484 
    485         double minX = Math.Min(xCursor.SelectionStart, xCursor.SelectionEnd);
    486         double maxX = Math.Max(xCursor.SelectionStart, xCursor.SelectionEnd);
    487         double minY = Math.Min(yCursor.SelectionStart, yCursor.SelectionEnd);
    488         double maxY = Math.Max(yCursor.SelectionStart, yCursor.SelectionEnd);
    489 
    490         //check for click to select model
    491         if (minX == maxX && minY == maxY) {
    492           HitTestResult hitTest = chart.HitTest(e.X, e.Y);
    493           if (hitTest.ChartElementType == ChartElementType.DataPoint) {
    494             int pointIndex = hitTest.PointIndex;
    495             IRun run = (IRun)this.chart.Series[0].Points[pointIndex].Tag;
    496             run.Color = colorDialog.Color;
    497           }
    498         } else {
    499           List<DataPoint> selectedPoints = new List<DataPoint>();
    500           foreach (DataPoint p in this.chart.Series[0].Points) {
    501             if (p.XValue >= minX && p.XValue < maxX &&
    502               p.YValues[0] >= minY && p.YValues[0] < maxY) {
    503               selectedPoints.Add(p);
    504             }
    505           }
    506           foreach (DataPoint p in selectedPoints) {
    507             IRun run = (IRun)p.Tag;
    508             run.Color = colorDialog.Color;
    509           }
    510         }
    511         this.chart.ChartAreas[0].CursorX.SelectionStart = this.chart.ChartAreas[0].CursorX.SelectionEnd;
    512         this.chart.ChartAreas[0].CursorY.SelectionStart = this.chart.ChartAreas[0].CursorY.SelectionEnd;
    513       }
     527      if (!isSelecting) return;
     528
     529      System.Windows.Forms.DataVisualization.Charting.Cursor xCursor = chart.ChartAreas[0].CursorX;
     530      System.Windows.Forms.DataVisualization.Charting.Cursor yCursor = chart.ChartAreas[0].CursorY;
     531
     532      double minX = Math.Min(xCursor.SelectionStart, xCursor.SelectionEnd);
     533      double maxX = Math.Max(xCursor.SelectionStart, xCursor.SelectionEnd);
     534      double minY = Math.Min(yCursor.SelectionStart, yCursor.SelectionEnd);
     535      double maxY = Math.Max(yCursor.SelectionStart, yCursor.SelectionEnd);
     536
     537      //check for click to select a single model
     538      if (minX == maxX && minY == maxY) {
     539        HitTestResult hitTest = chart.HitTest(e.X, e.Y, ChartElementType.DataPoint);
     540        if (hitTest.ChartElementType == ChartElementType.DataPoint) {
     541          int pointIndex = hitTest.PointIndex;
     542          var point = chart.Series[0].Points[pointIndex];
     543          IRun run = (IRun)point.Tag;
     544          point.Color = Color.Red;
     545          point.MarkerStyle = MarkerStyle.Cross;
     546          selectedRuns.Add(run);
     547
     548        } else ClearSelectedRuns();
     549      } else {
     550        foreach (DataPoint point in this.chart.Series[0].Points) {
     551          if (point.XValue < minX || point.XValue >= maxX) continue;
     552          if (point.YValues[0] < minY || point.YValues[0] >= maxY) continue;
     553          point.MarkerStyle = MarkerStyle.Cross;
     554          point.Color = Color.Red;
     555          IRun run = (IRun)point.Tag;
     556          selectedRuns.Add(run);
     557        }
     558      }
     559
     560      this.chart.ChartAreas[0].CursorX.SelectionStart = this.chart.ChartAreas[0].CursorX.SelectionEnd;
     561      this.chart.ChartAreas[0].CursorY.SelectionStart = this.chart.ChartAreas[0].CursorY.SelectionEnd;
     562      this.OnChanged();
    514563    }
    515564
    516565    private void chart_MouseMove(object sender, MouseEventArgs e) {
     566      if (Control.MouseButtons != MouseButtons.None) return;
    517567      HitTestResult h = this.chart.HitTest(e.X, e.Y);
    518568      string newTooltipText = string.Empty;
     
    638688    private void zoomButton_CheckedChanged(object sender, EventArgs e) {
    639689      this.isSelecting = selectButton.Checked;
    640       this.colorButton.Enabled = this.isSelecting;
     690      this.colorDialogButton.Enabled = this.isSelecting;
     691      this.colorRunsButton.Enabled = this.isSelecting;
     692      this.hideRunsButton.Enabled = this.isSelecting;
    641693      this.chart.ChartAreas[0].AxisX.ScaleView.Zoomable = !isSelecting;
    642694      this.chart.ChartAreas[0].AxisY.ScaleView.Zoomable = !isSelecting;
    643     }
    644     private void colorButton_Click(object sender, EventArgs e) {
    645       if (colorDialog.ShowDialog(this) == DialogResult.OK) {
    646         this.colorButton.Image = this.GenerateImage(16, 16, this.colorDialog.Color);
    647       }
    648     }
    649     private Image GenerateImage(int width, int height, Color fillColor) {
    650       Image colorImage = new Bitmap(width, height);
    651       using (Graphics gfx = Graphics.FromImage(colorImage)) {
    652         using (SolidBrush brush = new SolidBrush(fillColor)) {
    653           gfx.FillRectangle(brush, 0, 0, width, height);
    654         }
    655       }
    656       return colorImage;
     695      ClearSelectedRuns();
    657696    }
    658697
     
    673712    }
    674713    private void hideRunToolStripMenuItem_Click(object sender, EventArgs e) {
    675       var constraint = Content.Constraints.OfType<RunCollectionContentConstraint>().Where(c => c.Active).FirstOrDefault();
     714      var constraint = Content.Constraints.OfType<RunCollectionContentConstraint>().FirstOrDefault(c => c.Active);
    676715      if (constraint == null) {
    677716        constraint = new RunCollectionContentConstraint();
     
    680719      }
    681720      constraint.ConstraintData.Add(runToHide);
     721    }
     722    private void hideRunsButton_Click(object sender, EventArgs e) {
     723      if (!selectedRuns.Any()) return;
     724      var constraint = new RunCollectionContentConstraint();
     725      constraint.ConstraintData = new ItemSet<IRun>(selectedRuns);
     726      Content.Constraints.Add(constraint);
     727      ClearSelectedRuns();
     728      constraint.Active = true;
     729    }
     730
     731    private void ClearSelectedRuns() {
     732      foreach (var run in selectedRuns) {
     733        foreach (var point in runToDataPointMapping[run]) {
     734          point.MarkerStyle = MarkerStyle.Circle;
     735          point.Color = Color.FromArgb(255 - LogTransform(transparencyTrackBar.Value), run.Color);
     736        }
     737      }
     738      selectedRuns.Clear();
     739    }
     740
     741    // returns a value in [0..255]
     742    private int LogTransform(int x) {
     743      double min = transparencyTrackBar.Minimum;
     744      double max = transparencyTrackBar.Maximum;
     745      double r = (x - min) / (max - min);  // r \in [0..1]
     746      double l = Math.Log(r + 1) / Math.Log(2.0); // l \in [0..1]
     747      return (int)Math.Round(255.0 * l);
    682748    }
    683749
     
    692758    private void getDataAsMatrixToolStripMenuItem_Click(object sender, EventArgs e) {
    693759      int xCol = Matrix.ColumnNames.ToList().IndexOf(xAxisValue);
     760      int yCol = Matrix.ColumnNames.ToList().IndexOf(yAxisValue);
     761
    694762      var grouped = new Dictionary<string, List<string>>();
    695763      Dictionary<double, string> reverseMapping = null;
     
    698766      foreach (var run in Content.Where(r => r.Visible)) {
    699767        var x = GetValue(run, xAxisValue);
    700         var y = GetValue(run, yAxisValue);
    701         if (!(x.HasValue && y.HasValue)) continue;
     768        object y;
     769        if (categoricalMapping.ContainsKey(yCol))
     770          y = Content.GetValue(run, yAxisValue);
     771        else y = GetValue(run, yAxisValue);
     772        if (!(x.HasValue && y != null)) continue;
    702773
    703774        var category = reverseMapping == null ? x.Value.ToString() : reverseMapping[x.Value];
    704775        if (!grouped.ContainsKey(category)) grouped[category] = new List<string>();
    705         grouped[category].Add(y.Value.ToString());
     776        grouped[category].Add(y.ToString());
    706777      }
    707778
     
    717788        i++;
    718789      }
    719 
    720       MainFormManager.MainForm.ShowContent(matrix);
     790      matrix.SortableView = false;
     791      var view = MainFormManager.MainForm.ShowContent(matrix);
     792      view.ReadOnly = true;
     793    }
     794
     795    private void transparencyTrackBar_ValueChanged(object sender, EventArgs e) {
     796      foreach (var run in Content)
     797        UpdateRun(run);
    721798    }
    722799    #endregion
    723800
    724     #region Automatic coloring
     801    #region coloring
     802    private void colorDialogButton_Click(object sender, EventArgs e) {
     803      if (colorDialog.ShowDialog(this) == DialogResult.OK) {
     804        this.colorDialogButton.Image = this.GenerateImage(16, 16, this.colorDialog.Color);
     805      }
     806    }
     807    private Image GenerateImage(int width, int height, Color fillColor) {
     808      Image colorImage = new Bitmap(width, height);
     809      using (Graphics gfx = Graphics.FromImage(colorImage)) {
     810        using (SolidBrush brush = new SolidBrush(fillColor)) {
     811          gfx.FillRectangle(brush, 0, 0, width, height);
     812        }
     813      }
     814      return colorImage;
     815    }
     816
     817    private void colorRunsButton_Click(object sender, EventArgs e) {
     818      if (!selectedRuns.Any()) return;
     819      Content.UpdateOfRunsInProgress = true;
     820      foreach (var run in selectedRuns)
     821        run.Color = colorDialog.Color;
     822
     823      ClearSelectedRuns();
     824      Content.UpdateOfRunsInProgress = false;
     825    }
     826
    725827    private void colorXAxisButton_Click(object sender, EventArgs e) {
    726828      ColorRuns(xAxisValue);
    727829    }
    728 
    729830    private void colorYAxisButton_Click(object sender, EventArgs e) {
    730831      ColorRuns(yAxisValue);
    731832    }
    732 
    733833    private void ColorRuns(string axisValue) {
    734       var runs = Content.Where(r => r.Visible).Select(r => new { Run = r, Value = GetValue(r, axisValue) }).Where(r => r.Value.HasValue);
     834      var runs = Content.Where(r => r.Visible).Select(r => new { Run = r, Value = GetValue(r, axisValue) }).Where(r => r.Value.HasValue).ToList();
    735835      double minValue = runs.Min(r => r.Value.Value);
    736836      double maxValue = runs.Max(r => r.Value.Value);
    737837      double range = maxValue - minValue;
    738 
    739       foreach (var r in runs) {
    740         int colorIndex = 0;
    741         if (!range.IsAlmost(0)) colorIndex = (int)((ColorGradient.Colors.Count - 1) * (r.Value.Value - minValue) / (range));
    742         r.Run.Color = ColorGradient.Colors[colorIndex];
    743       }
     838      // UpdateOfRunsInProgress has to be set to true, otherwise run_Changed is called all the time (also in other views)
     839      Content.UpdateOfRunsInProgress = true;
     840      if (range.IsAlmost(0)) {
     841        Color c = ColorGradient.Colors[0];
     842        runs.ForEach(r => r.Run.Color = c);
     843      } else {
     844        int maxColorIndex = ColorGradient.Colors.Count - 1;
     845        foreach (var r in runs) {
     846          int colorIndex = (int)(maxColorIndex * (r.Value - minValue) / (range));
     847          r.Run.Color = ColorGradient.Colors[colorIndex];
     848        }
     849      }
     850      Content.UpdateOfRunsInProgress = false;
    744851    }
    745852    #endregion
Note: See TracChangeset for help on using the changeset viewer.