Design of colour fading for WinForms controls effect












4












$begingroup$


I wrote this little piece of code to linearly interpolate between a winforms control's background colour and any arbirtrarly chosen colour.



I don't like the way I wrote this piece and I was wondering if any of you would be interested in suggesting improvements.



Some improvements/thoughts from the top of my head:




  1. If the user calls the method while a previous iteration is still ongoing, the background colour will not be predictable. This is not intended. I would require some form of if(!colourChanging.IsActive)... and so on. Any ideas on how to implement that?

  2. Do I need to dispose the task? No, right? The task will take a threadpool thread, release all of its resource at its end and return the thread back to the pool?

  3. Is this a stupid approach to begin with?

  4. How do I get the complementary colour of Color _eventColour? I think it might make sense to change the foreground color property to maintain readability.

  5. Is this approach scalable to lets say 100 controls - to be used in an event driven environment?




private void ChangeControlColour(Control _activeControl,  Color _eventColour) 
{
int _rgbEventColours = new int[3] { _eventColour.R, _eventColour.G, _eventColour.B };
int _rgbOriginalColours = new int[3] { _activeControl.BackColor.R, _activeControl.BackColor.G, _activeControl.BackColor.B };
int _rgbIntervals = new int[3];
int _rgbCurrentValues = _rgbEventColours;
int _intervals = 20;

/* linear steps between each fading interval */
for (int i = 0; i < 3; i++)
_rgbIntervals[i] = (_rgbOriginalColours[i] - _rgbEventColours[i]) / _intervals;

/* Start with EventColour */
if (_activeControl.InvokeRequired)
_activeControl.Invoke((MethodInvoker)delegate { _activeControl.BackColor = _eventColour; });
else
_activeControl.BackColor = _eventColour;

/* LinearFading Process isolated in a seperate Task to avoid blocking UI */
Task t = Task.Factory.StartNew(() =>
{
System.Threading.Thread.Sleep(500);

Color _fadingColour;
for (int i = 0; i <= _intervals; i++)
{
_fadingColour = Color.FromArgb(_rgbCurrentValues[0], _rgbCurrentValues[1], _rgbCurrentValues[2]);

if (_activeControl.InvokeRequired)
_activeControl.Invoke((MethodInvoker)delegate {_activeControl.BackColor = _fadingColour;});
else
_activeControl.BackColor = _fadingColour;

for (int n = 0; n < 3; n++) _rgbCurrentValues[n] += _rgbIntervals[n];

System.Threading.Thread.Sleep(50);
}

/* More important for non linear interpolation, but for completeness - set Control's Colour back
* to its original backgroundcolour */
Color _startingColour = Color.FromArgb(_rgbOriginalColours[0], _rgbOriginalColours[1], _rgbOriginalColours[2]);

if (_activeControl.InvokeRequired)
_activeControl.Invoke((MethodInvoker)delegate { _activeControl.BackColor = _startingColour; });
else
_activeControl.BackColor = _startingColour;
});
}


Edit / Update on Sept. 20th 2014.



ChrisWue has answered the original question in a very helpful and detailed way. Thanks!



I do have a follow up:



Regarding "making sure that only one fading effect is running at a time" is the following theoretical solution a feasible approach or would there be a more efficient, pragmatic approach ? :



(let's assume I create a new instance of the ColorFader on some random event X for a control Y)



I store all ColorFader instances in a List and give them a field to remember their WinForms control Y. so every time the event is fired for a control Y I would check if there already is a ColorFader in the list referencing this particular Control Y.



In the ColorFader Class I would need to implement IDisposable . And as soon as the the object is disposed I would need to get rid of it in the List .



Got a better idea anyone?



The "problem" I see with this is the additional management of objects. It is not nicely encapsulated, because every project I would like to use this in I would have to create a new List just to keep track of the Objects + work to add and remove from said list.



(I will have around 20-40 labels which could fire a ColorFading event at any given time)










share|improve this question











$endgroup$

















    4












    $begingroup$


    I wrote this little piece of code to linearly interpolate between a winforms control's background colour and any arbirtrarly chosen colour.



    I don't like the way I wrote this piece and I was wondering if any of you would be interested in suggesting improvements.



    Some improvements/thoughts from the top of my head:




    1. If the user calls the method while a previous iteration is still ongoing, the background colour will not be predictable. This is not intended. I would require some form of if(!colourChanging.IsActive)... and so on. Any ideas on how to implement that?

    2. Do I need to dispose the task? No, right? The task will take a threadpool thread, release all of its resource at its end and return the thread back to the pool?

    3. Is this a stupid approach to begin with?

    4. How do I get the complementary colour of Color _eventColour? I think it might make sense to change the foreground color property to maintain readability.

    5. Is this approach scalable to lets say 100 controls - to be used in an event driven environment?




    private void ChangeControlColour(Control _activeControl,  Color _eventColour) 
    {
    int _rgbEventColours = new int[3] { _eventColour.R, _eventColour.G, _eventColour.B };
    int _rgbOriginalColours = new int[3] { _activeControl.BackColor.R, _activeControl.BackColor.G, _activeControl.BackColor.B };
    int _rgbIntervals = new int[3];
    int _rgbCurrentValues = _rgbEventColours;
    int _intervals = 20;

    /* linear steps between each fading interval */
    for (int i = 0; i < 3; i++)
    _rgbIntervals[i] = (_rgbOriginalColours[i] - _rgbEventColours[i]) / _intervals;

    /* Start with EventColour */
    if (_activeControl.InvokeRequired)
    _activeControl.Invoke((MethodInvoker)delegate { _activeControl.BackColor = _eventColour; });
    else
    _activeControl.BackColor = _eventColour;

    /* LinearFading Process isolated in a seperate Task to avoid blocking UI */
    Task t = Task.Factory.StartNew(() =>
    {
    System.Threading.Thread.Sleep(500);

    Color _fadingColour;
    for (int i = 0; i <= _intervals; i++)
    {
    _fadingColour = Color.FromArgb(_rgbCurrentValues[0], _rgbCurrentValues[1], _rgbCurrentValues[2]);

    if (_activeControl.InvokeRequired)
    _activeControl.Invoke((MethodInvoker)delegate {_activeControl.BackColor = _fadingColour;});
    else
    _activeControl.BackColor = _fadingColour;

    for (int n = 0; n < 3; n++) _rgbCurrentValues[n] += _rgbIntervals[n];

    System.Threading.Thread.Sleep(50);
    }

    /* More important for non linear interpolation, but for completeness - set Control's Colour back
    * to its original backgroundcolour */
    Color _startingColour = Color.FromArgb(_rgbOriginalColours[0], _rgbOriginalColours[1], _rgbOriginalColours[2]);

    if (_activeControl.InvokeRequired)
    _activeControl.Invoke((MethodInvoker)delegate { _activeControl.BackColor = _startingColour; });
    else
    _activeControl.BackColor = _startingColour;
    });
    }


    Edit / Update on Sept. 20th 2014.



    ChrisWue has answered the original question in a very helpful and detailed way. Thanks!



    I do have a follow up:



    Regarding "making sure that only one fading effect is running at a time" is the following theoretical solution a feasible approach or would there be a more efficient, pragmatic approach ? :



    (let's assume I create a new instance of the ColorFader on some random event X for a control Y)



    I store all ColorFader instances in a List and give them a field to remember their WinForms control Y. so every time the event is fired for a control Y I would check if there already is a ColorFader in the list referencing this particular Control Y.



    In the ColorFader Class I would need to implement IDisposable . And as soon as the the object is disposed I would need to get rid of it in the List .



    Got a better idea anyone?



    The "problem" I see with this is the additional management of objects. It is not nicely encapsulated, because every project I would like to use this in I would have to create a new List just to keep track of the Objects + work to add and remove from said list.



    (I will have around 20-40 labels which could fire a ColorFading event at any given time)










    share|improve this question











    $endgroup$















      4












      4








      4


      2



      $begingroup$


      I wrote this little piece of code to linearly interpolate between a winforms control's background colour and any arbirtrarly chosen colour.



      I don't like the way I wrote this piece and I was wondering if any of you would be interested in suggesting improvements.



      Some improvements/thoughts from the top of my head:




      1. If the user calls the method while a previous iteration is still ongoing, the background colour will not be predictable. This is not intended. I would require some form of if(!colourChanging.IsActive)... and so on. Any ideas on how to implement that?

      2. Do I need to dispose the task? No, right? The task will take a threadpool thread, release all of its resource at its end and return the thread back to the pool?

      3. Is this a stupid approach to begin with?

      4. How do I get the complementary colour of Color _eventColour? I think it might make sense to change the foreground color property to maintain readability.

      5. Is this approach scalable to lets say 100 controls - to be used in an event driven environment?




      private void ChangeControlColour(Control _activeControl,  Color _eventColour) 
      {
      int _rgbEventColours = new int[3] { _eventColour.R, _eventColour.G, _eventColour.B };
      int _rgbOriginalColours = new int[3] { _activeControl.BackColor.R, _activeControl.BackColor.G, _activeControl.BackColor.B };
      int _rgbIntervals = new int[3];
      int _rgbCurrentValues = _rgbEventColours;
      int _intervals = 20;

      /* linear steps between each fading interval */
      for (int i = 0; i < 3; i++)
      _rgbIntervals[i] = (_rgbOriginalColours[i] - _rgbEventColours[i]) / _intervals;

      /* Start with EventColour */
      if (_activeControl.InvokeRequired)
      _activeControl.Invoke((MethodInvoker)delegate { _activeControl.BackColor = _eventColour; });
      else
      _activeControl.BackColor = _eventColour;

      /* LinearFading Process isolated in a seperate Task to avoid blocking UI */
      Task t = Task.Factory.StartNew(() =>
      {
      System.Threading.Thread.Sleep(500);

      Color _fadingColour;
      for (int i = 0; i <= _intervals; i++)
      {
      _fadingColour = Color.FromArgb(_rgbCurrentValues[0], _rgbCurrentValues[1], _rgbCurrentValues[2]);

      if (_activeControl.InvokeRequired)
      _activeControl.Invoke((MethodInvoker)delegate {_activeControl.BackColor = _fadingColour;});
      else
      _activeControl.BackColor = _fadingColour;

      for (int n = 0; n < 3; n++) _rgbCurrentValues[n] += _rgbIntervals[n];

      System.Threading.Thread.Sleep(50);
      }

      /* More important for non linear interpolation, but for completeness - set Control's Colour back
      * to its original backgroundcolour */
      Color _startingColour = Color.FromArgb(_rgbOriginalColours[0], _rgbOriginalColours[1], _rgbOriginalColours[2]);

      if (_activeControl.InvokeRequired)
      _activeControl.Invoke((MethodInvoker)delegate { _activeControl.BackColor = _startingColour; });
      else
      _activeControl.BackColor = _startingColour;
      });
      }


      Edit / Update on Sept. 20th 2014.



      ChrisWue has answered the original question in a very helpful and detailed way. Thanks!



      I do have a follow up:



      Regarding "making sure that only one fading effect is running at a time" is the following theoretical solution a feasible approach or would there be a more efficient, pragmatic approach ? :



      (let's assume I create a new instance of the ColorFader on some random event X for a control Y)



      I store all ColorFader instances in a List and give them a field to remember their WinForms control Y. so every time the event is fired for a control Y I would check if there already is a ColorFader in the list referencing this particular Control Y.



      In the ColorFader Class I would need to implement IDisposable . And as soon as the the object is disposed I would need to get rid of it in the List .



      Got a better idea anyone?



      The "problem" I see with this is the additional management of objects. It is not nicely encapsulated, because every project I would like to use this in I would have to create a new List just to keep track of the Objects + work to add and remove from said list.



      (I will have around 20-40 labels which could fire a ColorFading event at any given time)










      share|improve this question











      $endgroup$




      I wrote this little piece of code to linearly interpolate between a winforms control's background colour and any arbirtrarly chosen colour.



      I don't like the way I wrote this piece and I was wondering if any of you would be interested in suggesting improvements.



      Some improvements/thoughts from the top of my head:




      1. If the user calls the method while a previous iteration is still ongoing, the background colour will not be predictable. This is not intended. I would require some form of if(!colourChanging.IsActive)... and so on. Any ideas on how to implement that?

      2. Do I need to dispose the task? No, right? The task will take a threadpool thread, release all of its resource at its end and return the thread back to the pool?

      3. Is this a stupid approach to begin with?

      4. How do I get the complementary colour of Color _eventColour? I think it might make sense to change the foreground color property to maintain readability.

      5. Is this approach scalable to lets say 100 controls - to be used in an event driven environment?




      private void ChangeControlColour(Control _activeControl,  Color _eventColour) 
      {
      int _rgbEventColours = new int[3] { _eventColour.R, _eventColour.G, _eventColour.B };
      int _rgbOriginalColours = new int[3] { _activeControl.BackColor.R, _activeControl.BackColor.G, _activeControl.BackColor.B };
      int _rgbIntervals = new int[3];
      int _rgbCurrentValues = _rgbEventColours;
      int _intervals = 20;

      /* linear steps between each fading interval */
      for (int i = 0; i < 3; i++)
      _rgbIntervals[i] = (_rgbOriginalColours[i] - _rgbEventColours[i]) / _intervals;

      /* Start with EventColour */
      if (_activeControl.InvokeRequired)
      _activeControl.Invoke((MethodInvoker)delegate { _activeControl.BackColor = _eventColour; });
      else
      _activeControl.BackColor = _eventColour;

      /* LinearFading Process isolated in a seperate Task to avoid blocking UI */
      Task t = Task.Factory.StartNew(() =>
      {
      System.Threading.Thread.Sleep(500);

      Color _fadingColour;
      for (int i = 0; i <= _intervals; i++)
      {
      _fadingColour = Color.FromArgb(_rgbCurrentValues[0], _rgbCurrentValues[1], _rgbCurrentValues[2]);

      if (_activeControl.InvokeRequired)
      _activeControl.Invoke((MethodInvoker)delegate {_activeControl.BackColor = _fadingColour;});
      else
      _activeControl.BackColor = _fadingColour;

      for (int n = 0; n < 3; n++) _rgbCurrentValues[n] += _rgbIntervals[n];

      System.Threading.Thread.Sleep(50);
      }

      /* More important for non linear interpolation, but for completeness - set Control's Colour back
      * to its original backgroundcolour */
      Color _startingColour = Color.FromArgb(_rgbOriginalColours[0], _rgbOriginalColours[1], _rgbOriginalColours[2]);

      if (_activeControl.InvokeRequired)
      _activeControl.Invoke((MethodInvoker)delegate { _activeControl.BackColor = _startingColour; });
      else
      _activeControl.BackColor = _startingColour;
      });
      }


      Edit / Update on Sept. 20th 2014.



      ChrisWue has answered the original question in a very helpful and detailed way. Thanks!



      I do have a follow up:



      Regarding "making sure that only one fading effect is running at a time" is the following theoretical solution a feasible approach or would there be a more efficient, pragmatic approach ? :



      (let's assume I create a new instance of the ColorFader on some random event X for a control Y)



      I store all ColorFader instances in a List and give them a field to remember their WinForms control Y. so every time the event is fired for a control Y I would check if there already is a ColorFader in the list referencing this particular Control Y.



      In the ColorFader Class I would need to implement IDisposable . And as soon as the the object is disposed I would need to get rid of it in the List .



      Got a better idea anyone?



      The "problem" I see with this is the additional management of objects. It is not nicely encapsulated, because every project I would like to use this in I would have to create a new List just to keep track of the Objects + work to add and remove from said list.



      (I will have around 20-40 labels which could fire a ColorFading event at any given time)







      c# design-patterns winforms






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Sep 20 '14 at 14:59







      Aeolus

















      asked Sep 13 '14 at 18:01









      AeolusAeolus

      85127




      85127






















          1 Answer
          1






          active

          oldest

          votes


















          5












          $begingroup$


          1. The standard naming convention in C# for local variables and parameters is camelCase. An underscore prefix is usually used for private class fields.


          2. You repeat the code so the the background color three times - this should be extracted into a separate method.



          3. This will perform an integer division:



            _rgbIntervals[i] = (_rgbOriginalColours[i] - _rgbEventColours[i]) / _intervals;


            which means if the difference between two color part is less than _intervals the calculated step is 0. Also you will get quite big accumulated errors due to the rounding. For example if you start with the color (30, 30, 30) and want it to fade to (60, 60, 60) then you will end up only fading to (50, 50, 50) because 60 - 30 = 30 and 30 / 20 = 1.5 but the 1.5 will be truncated to 1 due to integer division.



            You do set the final color to the target color but it would be smoother to calculate the step sizes as floats.



          4. You keep track of the various part and intermediate values in a bunch of arrays which do not have any direct relationship. It's also not very re-usable this way. I would encapsulate it in it's own class.



          So the refactored code could look like this:



          Class to encapsulate the fading between two colors:



          public class ColorFader
          {
          private readonly Color _From;
          private readonly Color _To;

          private readonly double _StepR;
          private readonly double _StepG;
          private readonly double _StepB;

          private readonly uint _Steps;

          public ColorFader(Color from, Color to, uint steps)
          {
          if (steps == 0)
          throw new ArgumentException("steps must be a positive number");

          _From = from;
          _To = to;
          _Steps = steps;

          _StepR = (double)(_To.R - _From.R) / _Steps;
          _StepG = (double)(_To.G - _From.G) / _Steps;
          _StepB = (double)(_To.B - _From.B) / _Steps;
          }

          public IEnumerable<Color> Fade()
          {
          for (uint i = 0; i < _Steps; ++i)
          {
          yield return Color.FromArgb((int)(_From.R + i * _StepR), (int)(_From.G + i * _StepG), (int)(_From.B + i * _StepB));
          }
          yield return _To; // make sure we always return the exact target color last
          }
          }


          And the refactored method in your form:



          private void ChangeControlColour(Control activeControl,  Color eventColour) 
          {
          int intervals = 20;

          var colorFader = new ColorFader(eventColour, activeControl.BackColor, intervals);

          SetControlBackColor(eventColor);

          /* LinearFading Process isolated in a seperate Task to avoid blocking UI */
          Task t = Task.Factory.StartNew(() =>
          {
          System.Threading.Thread.Sleep(500);
          foreach (var color in colorFader.Fade())
          {
          SetControlBackColor(color);
          System.Threading.Thread.Sleep(50);
          }
          });
          }

          private void SetControlBackColor(Color color)
          {
          if (_activeControl.InvokeRequired)
          _activeControl.Invoke((MethodInvoker)delegate { _activeControl.BackColor = color; });
          else
          _activeControl.BackColor = color;
          }





          share|improve this answer











          $endgroup$













          • $begingroup$
            ChrisWue, that was a very helpful and enlightening answer to my question.
            $endgroup$
            – Aeolus
            Sep 20 '14 at 14:36











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f62840%2fdesign-of-colour-fading-for-winforms-controls-effect%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          5












          $begingroup$


          1. The standard naming convention in C# for local variables and parameters is camelCase. An underscore prefix is usually used for private class fields.


          2. You repeat the code so the the background color three times - this should be extracted into a separate method.



          3. This will perform an integer division:



            _rgbIntervals[i] = (_rgbOriginalColours[i] - _rgbEventColours[i]) / _intervals;


            which means if the difference between two color part is less than _intervals the calculated step is 0. Also you will get quite big accumulated errors due to the rounding. For example if you start with the color (30, 30, 30) and want it to fade to (60, 60, 60) then you will end up only fading to (50, 50, 50) because 60 - 30 = 30 and 30 / 20 = 1.5 but the 1.5 will be truncated to 1 due to integer division.



            You do set the final color to the target color but it would be smoother to calculate the step sizes as floats.



          4. You keep track of the various part and intermediate values in a bunch of arrays which do not have any direct relationship. It's also not very re-usable this way. I would encapsulate it in it's own class.



          So the refactored code could look like this:



          Class to encapsulate the fading between two colors:



          public class ColorFader
          {
          private readonly Color _From;
          private readonly Color _To;

          private readonly double _StepR;
          private readonly double _StepG;
          private readonly double _StepB;

          private readonly uint _Steps;

          public ColorFader(Color from, Color to, uint steps)
          {
          if (steps == 0)
          throw new ArgumentException("steps must be a positive number");

          _From = from;
          _To = to;
          _Steps = steps;

          _StepR = (double)(_To.R - _From.R) / _Steps;
          _StepG = (double)(_To.G - _From.G) / _Steps;
          _StepB = (double)(_To.B - _From.B) / _Steps;
          }

          public IEnumerable<Color> Fade()
          {
          for (uint i = 0; i < _Steps; ++i)
          {
          yield return Color.FromArgb((int)(_From.R + i * _StepR), (int)(_From.G + i * _StepG), (int)(_From.B + i * _StepB));
          }
          yield return _To; // make sure we always return the exact target color last
          }
          }


          And the refactored method in your form:



          private void ChangeControlColour(Control activeControl,  Color eventColour) 
          {
          int intervals = 20;

          var colorFader = new ColorFader(eventColour, activeControl.BackColor, intervals);

          SetControlBackColor(eventColor);

          /* LinearFading Process isolated in a seperate Task to avoid blocking UI */
          Task t = Task.Factory.StartNew(() =>
          {
          System.Threading.Thread.Sleep(500);
          foreach (var color in colorFader.Fade())
          {
          SetControlBackColor(color);
          System.Threading.Thread.Sleep(50);
          }
          });
          }

          private void SetControlBackColor(Color color)
          {
          if (_activeControl.InvokeRequired)
          _activeControl.Invoke((MethodInvoker)delegate { _activeControl.BackColor = color; });
          else
          _activeControl.BackColor = color;
          }





          share|improve this answer











          $endgroup$













          • $begingroup$
            ChrisWue, that was a very helpful and enlightening answer to my question.
            $endgroup$
            – Aeolus
            Sep 20 '14 at 14:36
















          5












          $begingroup$


          1. The standard naming convention in C# for local variables and parameters is camelCase. An underscore prefix is usually used for private class fields.


          2. You repeat the code so the the background color three times - this should be extracted into a separate method.



          3. This will perform an integer division:



            _rgbIntervals[i] = (_rgbOriginalColours[i] - _rgbEventColours[i]) / _intervals;


            which means if the difference between two color part is less than _intervals the calculated step is 0. Also you will get quite big accumulated errors due to the rounding. For example if you start with the color (30, 30, 30) and want it to fade to (60, 60, 60) then you will end up only fading to (50, 50, 50) because 60 - 30 = 30 and 30 / 20 = 1.5 but the 1.5 will be truncated to 1 due to integer division.



            You do set the final color to the target color but it would be smoother to calculate the step sizes as floats.



          4. You keep track of the various part and intermediate values in a bunch of arrays which do not have any direct relationship. It's also not very re-usable this way. I would encapsulate it in it's own class.



          So the refactored code could look like this:



          Class to encapsulate the fading between two colors:



          public class ColorFader
          {
          private readonly Color _From;
          private readonly Color _To;

          private readonly double _StepR;
          private readonly double _StepG;
          private readonly double _StepB;

          private readonly uint _Steps;

          public ColorFader(Color from, Color to, uint steps)
          {
          if (steps == 0)
          throw new ArgumentException("steps must be a positive number");

          _From = from;
          _To = to;
          _Steps = steps;

          _StepR = (double)(_To.R - _From.R) / _Steps;
          _StepG = (double)(_To.G - _From.G) / _Steps;
          _StepB = (double)(_To.B - _From.B) / _Steps;
          }

          public IEnumerable<Color> Fade()
          {
          for (uint i = 0; i < _Steps; ++i)
          {
          yield return Color.FromArgb((int)(_From.R + i * _StepR), (int)(_From.G + i * _StepG), (int)(_From.B + i * _StepB));
          }
          yield return _To; // make sure we always return the exact target color last
          }
          }


          And the refactored method in your form:



          private void ChangeControlColour(Control activeControl,  Color eventColour) 
          {
          int intervals = 20;

          var colorFader = new ColorFader(eventColour, activeControl.BackColor, intervals);

          SetControlBackColor(eventColor);

          /* LinearFading Process isolated in a seperate Task to avoid blocking UI */
          Task t = Task.Factory.StartNew(() =>
          {
          System.Threading.Thread.Sleep(500);
          foreach (var color in colorFader.Fade())
          {
          SetControlBackColor(color);
          System.Threading.Thread.Sleep(50);
          }
          });
          }

          private void SetControlBackColor(Color color)
          {
          if (_activeControl.InvokeRequired)
          _activeControl.Invoke((MethodInvoker)delegate { _activeControl.BackColor = color; });
          else
          _activeControl.BackColor = color;
          }





          share|improve this answer











          $endgroup$













          • $begingroup$
            ChrisWue, that was a very helpful and enlightening answer to my question.
            $endgroup$
            – Aeolus
            Sep 20 '14 at 14:36














          5












          5








          5





          $begingroup$


          1. The standard naming convention in C# for local variables and parameters is camelCase. An underscore prefix is usually used for private class fields.


          2. You repeat the code so the the background color three times - this should be extracted into a separate method.



          3. This will perform an integer division:



            _rgbIntervals[i] = (_rgbOriginalColours[i] - _rgbEventColours[i]) / _intervals;


            which means if the difference between two color part is less than _intervals the calculated step is 0. Also you will get quite big accumulated errors due to the rounding. For example if you start with the color (30, 30, 30) and want it to fade to (60, 60, 60) then you will end up only fading to (50, 50, 50) because 60 - 30 = 30 and 30 / 20 = 1.5 but the 1.5 will be truncated to 1 due to integer division.



            You do set the final color to the target color but it would be smoother to calculate the step sizes as floats.



          4. You keep track of the various part and intermediate values in a bunch of arrays which do not have any direct relationship. It's also not very re-usable this way. I would encapsulate it in it's own class.



          So the refactored code could look like this:



          Class to encapsulate the fading between two colors:



          public class ColorFader
          {
          private readonly Color _From;
          private readonly Color _To;

          private readonly double _StepR;
          private readonly double _StepG;
          private readonly double _StepB;

          private readonly uint _Steps;

          public ColorFader(Color from, Color to, uint steps)
          {
          if (steps == 0)
          throw new ArgumentException("steps must be a positive number");

          _From = from;
          _To = to;
          _Steps = steps;

          _StepR = (double)(_To.R - _From.R) / _Steps;
          _StepG = (double)(_To.G - _From.G) / _Steps;
          _StepB = (double)(_To.B - _From.B) / _Steps;
          }

          public IEnumerable<Color> Fade()
          {
          for (uint i = 0; i < _Steps; ++i)
          {
          yield return Color.FromArgb((int)(_From.R + i * _StepR), (int)(_From.G + i * _StepG), (int)(_From.B + i * _StepB));
          }
          yield return _To; // make sure we always return the exact target color last
          }
          }


          And the refactored method in your form:



          private void ChangeControlColour(Control activeControl,  Color eventColour) 
          {
          int intervals = 20;

          var colorFader = new ColorFader(eventColour, activeControl.BackColor, intervals);

          SetControlBackColor(eventColor);

          /* LinearFading Process isolated in a seperate Task to avoid blocking UI */
          Task t = Task.Factory.StartNew(() =>
          {
          System.Threading.Thread.Sleep(500);
          foreach (var color in colorFader.Fade())
          {
          SetControlBackColor(color);
          System.Threading.Thread.Sleep(50);
          }
          });
          }

          private void SetControlBackColor(Color color)
          {
          if (_activeControl.InvokeRequired)
          _activeControl.Invoke((MethodInvoker)delegate { _activeControl.BackColor = color; });
          else
          _activeControl.BackColor = color;
          }





          share|improve this answer











          $endgroup$




          1. The standard naming convention in C# for local variables and parameters is camelCase. An underscore prefix is usually used for private class fields.


          2. You repeat the code so the the background color three times - this should be extracted into a separate method.



          3. This will perform an integer division:



            _rgbIntervals[i] = (_rgbOriginalColours[i] - _rgbEventColours[i]) / _intervals;


            which means if the difference between two color part is less than _intervals the calculated step is 0. Also you will get quite big accumulated errors due to the rounding. For example if you start with the color (30, 30, 30) and want it to fade to (60, 60, 60) then you will end up only fading to (50, 50, 50) because 60 - 30 = 30 and 30 / 20 = 1.5 but the 1.5 will be truncated to 1 due to integer division.



            You do set the final color to the target color but it would be smoother to calculate the step sizes as floats.



          4. You keep track of the various part and intermediate values in a bunch of arrays which do not have any direct relationship. It's also not very re-usable this way. I would encapsulate it in it's own class.



          So the refactored code could look like this:



          Class to encapsulate the fading between two colors:



          public class ColorFader
          {
          private readonly Color _From;
          private readonly Color _To;

          private readonly double _StepR;
          private readonly double _StepG;
          private readonly double _StepB;

          private readonly uint _Steps;

          public ColorFader(Color from, Color to, uint steps)
          {
          if (steps == 0)
          throw new ArgumentException("steps must be a positive number");

          _From = from;
          _To = to;
          _Steps = steps;

          _StepR = (double)(_To.R - _From.R) / _Steps;
          _StepG = (double)(_To.G - _From.G) / _Steps;
          _StepB = (double)(_To.B - _From.B) / _Steps;
          }

          public IEnumerable<Color> Fade()
          {
          for (uint i = 0; i < _Steps; ++i)
          {
          yield return Color.FromArgb((int)(_From.R + i * _StepR), (int)(_From.G + i * _StepG), (int)(_From.B + i * _StepB));
          }
          yield return _To; // make sure we always return the exact target color last
          }
          }


          And the refactored method in your form:



          private void ChangeControlColour(Control activeControl,  Color eventColour) 
          {
          int intervals = 20;

          var colorFader = new ColorFader(eventColour, activeControl.BackColor, intervals);

          SetControlBackColor(eventColor);

          /* LinearFading Process isolated in a seperate Task to avoid blocking UI */
          Task t = Task.Factory.StartNew(() =>
          {
          System.Threading.Thread.Sleep(500);
          foreach (var color in colorFader.Fade())
          {
          SetControlBackColor(color);
          System.Threading.Thread.Sleep(50);
          }
          });
          }

          private void SetControlBackColor(Color color)
          {
          if (_activeControl.InvokeRequired)
          _activeControl.Invoke((MethodInvoker)delegate { _activeControl.BackColor = color; });
          else
          _activeControl.BackColor = color;
          }






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited 51 mins ago









          Hannobo

          1032




          1032










          answered Sep 15 '14 at 21:37









          ChrisWueChrisWue

          19.1k333100




          19.1k333100












          • $begingroup$
            ChrisWue, that was a very helpful and enlightening answer to my question.
            $endgroup$
            – Aeolus
            Sep 20 '14 at 14:36


















          • $begingroup$
            ChrisWue, that was a very helpful and enlightening answer to my question.
            $endgroup$
            – Aeolus
            Sep 20 '14 at 14:36
















          $begingroup$
          ChrisWue, that was a very helpful and enlightening answer to my question.
          $endgroup$
          – Aeolus
          Sep 20 '14 at 14:36




          $begingroup$
          ChrisWue, that was a very helpful and enlightening answer to my question.
          $endgroup$
          – Aeolus
          Sep 20 '14 at 14:36


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f62840%2fdesign-of-colour-fading-for-winforms-controls-effect%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          How to make a Squid Proxy server?

          第一次世界大戦

          Touch on Surface Book