1

I am trying to make a User Control to blink a label.

I have found this example:

using System.Diagnostics;
using System.Threading.Tasks;

private async void SoftBlink(Control ctrl, Color c1, Color c2, short CycleTime_ms, bool BkClr)
        {
            var sw = new Stopwatch(); sw.Start();
            short halfCycle = (short)Math.Round(CycleTime_ms * 0.5);
            while (true)
            {
                await Task.Delay(1);
                var n = sw.ElapsedMilliseconds % CycleTime_ms;  // ERROR #1
                var per = (double)Math.Abs(n - halfCycle) / halfCycle; 
                var red = (short)Math.Round((c2.R - c1.R) * per) + c1.R; // ERROR #2
                var grn = (short)Math.Round((c2.G - c1.G) * per) + c1.G; // ERROR #3
                var blw = (short)Math.Round((c2.B - c1.B) * per) + c1.B; // ERROR #4
                var clr = Color.FromArgb(red, grn, blw);
                if (BkClr) ctrl.BackColor = clr; else ctrl.ForeColor = clr;
            }
        }

Since I want to implement it in a legacy project NET 1.1 I need to convert above piece of code to make it work so I have implemented below user control:

public class UCSoftBlink : System.Windows.Forms.Label
{
    /// <summary> 
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.Container components = null;
    private bool exit = false;
    private DateTime startTime = new DateTime();
    private TimeSpan elapsedTime = new TimeSpan();      
    private Timer timer = new Timer();
    private delegate void blinkingDelegate(Label lbl, Color clr1, Color clr2, short CycleTime_ms, bool bkClr);
    public static UCSoftBlink _ucSoftBlink;

    private UCSoftBlink()
    {
        // This call is required by the Windows.Forms Form Designer.
        InitializeComponent();

        timer.Tick += new EventHandler( timer_Tick );
        timer.Interval = (1000) * (1); 
        timer.Enabled = true; 
    }

    public static void StartBlinking(Label lbl, Color clr1, Color clr2, short CycleTime_ms, bool bkClr)
    {
        _ucSoftBlink = new UCSoftBlink();

        blinkingDelegate bd = new blinkingDelegate(_ucSoftBlink.Blink);
        bd.BeginInvoke(lbl, clr1, clr2, CycleTime_ms, bkClr, null, null);
    }

    public static void StopBlinking()
    {
        _ucSoftBlink.timer.Stop();
        _ucSoftBlink.exit = true;
    }

    private void Blink(Label lbl, Color clr1, Color clr2, short CycleTime_ms, bool bkClr)
    {
        _ucSoftBlink.timer.Start();
        _ucSoftBlink.startTime = DateTime.Now;          

        short halfCycle = (short)Math.Round(CycleTime_ms * 0.5);
        while (!exit)
        {
            System.Threading.Thread.Sleep(1000);
            short n = _ucSoftBlink.elapsedTime.TotalMilliseconds % CycleTime_ms; // ERROR #1
            double per = (double)Math.Abs(n - halfCycle) / halfCycle;
            short red = (short)Math.Round((clr2.R - clr1.R) * per) + clr1.R; ERROR #2
            short grn = (short)Math.Round((clr2.G - clr1.G) * per) + clr1.G; ERROR #3
            short blw = (short)Math.Round((clr2.B - clr1.B) * per) + clr1.B; ERROR #4
            Color clr = Color.FromArgb(red, grn, blw);

            if (bkClr) 
            {
                lbl.BackColor = clr; 
            }
            else
            {
                lbl.ForeColor = clr;
            }
        }

        Dispose(true);
    }

    private void timer_Tick(object sender, EventArgs e)
    { 
        _ucSoftBlink.elapsedTime = DateTime.Now - _ucSoftBlink.startTime;
    }

    /// <summary> 
    /// Clean up any resources being used.
    /// </summary>
    protected override void Dispose( bool disposing )
    {
        if( disposing )
        {
            if(components != null)
            {
                _ucSoftBlink.timer.Stop();
                _ucSoftBlink.timer.Tick -= timer_Tick; // // ERROR #5 I know this is possible in other version of NET (>=2.0).
                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()
    {
        components = new System.ComponentModel.Container();
    }
    #endregion
}

but I get some compilation errors:

ERROR #1 error CS0197: Cannot pass 'My.Controls.UCSoftBlink.elapsedTime' as ref or out, because 'My.Controls.UCSoftBlink.elapsedTime' is a marshal-by-reference class
ERROR #1 error CS0029: Cannot implicitly convert type 'double' to 'short'
ERROR #2 error CS0029: Cannot implicitly convert type 'int' to 'short'
ERROR #3 error CS0029: Cannot implicitly convert type 'int' to 'short'
ERROR #4 error CS0029: Cannot implicitly convert type 'int' to 'short'
ERROR #5 error CS0654: Method 'sdpiu.Controles.UCSoftBlink.timer_Tick(object, System.EventArgs)' referenced without parentheses

I want StartBlinking, StopBlinking to be static so I can access without the need of instantiate the user control.

Willy
  • 9,848
  • 22
  • 141
  • 284
  • why are you trying to work in an outdated legacy framework such as .ent 1.1? – MethodMan Nov 14 '17 at 16:11
  • @MethodMan It is a legacy project and I need to do some modifications. Soon it will be migrated but until now I need to do the modifications. It needs to be maintained until then. – Willy Nov 14 '17 at 16:21
  • “Soon it will be migrated.” No it won't, because you keep propping it up. – Dour High Arch Nov 14 '17 at 16:45
  • @DourHighArch Well, I know it is a very old project but my question is about making it working in net 1.1 in some way, not about migration. So I highly appreciate to provide ideas on how to solve above compilation errors. – Willy Nov 14 '17 at 16:52
  • "I want StartBlinking, StopBlinking to be static so I can access without the need of instantiate the user control." Then what would they control? I didn't see you pass any reference to a control to these methods... – Zohar Peled Nov 14 '17 at 17:54
  • 1
    https://stackoverflow.com/a/30865840/17034 – Hans Passant Nov 14 '17 at 18:29
  • @HansPassant Very good! I like this solution, creating a component that implements the IExtenderProvider interface instead of using a user control. But how to adapt your solution to make blinking progressive like in the example I provided instead of making blinking switching "ON/OFF"? – Willy Nov 14 '17 at 21:30
  • @HansPassant Your example is great! but Dictionary is not available in .NET 1.1. It is only available from .NET 2.0. I have changed into a hashtable that is available in NET 1.1. – Willy Nov 16 '17 at 12:46

1 Answers1

0

You can create a class with static methods and properties to handle this task on another thread and use a delegate in order to access safely to GUI controls, here is a basic approach, you can improve it for your specific needs.

calling the class from your user control:

public void StartBlink()
{
    BlinkMe.BlinkedLabel = this.label1;
    // start blinking
    System.Threading.Thread t1 = new System.Threading.Thread(BlinkMe.Blink);
    t1.Start();
}
private void btnStopBlinking_Click(object sender, EventArgs e)
{
    // stop blinking
    BlinkMe.IsBlinking = false;
}

the class that will handle the blink:

class BlinkMe
{
    public static Label BlinkedLabel;
    public static int milisec = 500;
    public delegate void BlinkDel();
    public static bool IsBlinking { get; set; } = true;

    public static void Blink()
    {

        while (IsBlinking)
        {
            System.Threading.Thread.Sleep(milisec);
            BlinkedLabel.Invoke(new BlinkDel(DoBLink));
        }
    }

    public static void DoBLink()
    {
        if (BlinkedLabel.BackColor != Color.Yellow)
        {
            BlinkedLabel.BackColor = Color.Yellow;
        }
        else
        {
            BlinkedLabel.BackColor = Color.Green;
        }
    }
}
Jonathan Applebaum
  • 5,738
  • 4
  • 33
  • 52