3

Trying to understand Timers and virtual clicks in C# Winforms. I want to have the program have an entered time value by the user (textbox1), then wait that amount of time and click the mouse, then increase the number counter (textbox2).

In the code below, the number counter immediately goes to 10, but the clicks are never ending, despite having a while loop set to stop the clicks at 10. I basically just want the program to wait a slightly random time (time entered to time entered +3), click the mouse, increase the counter, then pick a new random number and continue until 10 total clicks.

 public Form1()
    {
        InitializeComponent();
    }

    private void NumbersOnly(object sender, KeyPressEventArgs e)
    {
        char ch = e.KeyChar;
        if (!Char.IsDigit(ch) && ch != 8)
        {
            e.Handled = true;
        }
    }

    static System.Timers.Timer _timer;
    int numberofclicks = 0;
    [DllImport("user32.dll")]
    static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);
    private const int MOUSEEVENTF_MOVE = 0x0001;
    private const int MOUSEEVENTF_LEFTDOWN = 0x0002;
    private const int MOUSEEVENTF_LEFTUP = 0x0004;
    private const int MOUSEEVENTF_RIGHTDOWN = 0x0008;
    private const int MOUSEEVENTF_RIGHTUP = 0x0010;
    private const int MOUSEEVENTF_MIDDLEDOWN = 0x0020;
    private const int MOUSEEVENTF_MIDDLEUP = 0x0040;
    private const int MOUSEEVENTF_ABSOLUTE = 0x8000;


    private void StartClicked(object sender, EventArgs e)
    {
        numberofclicks = 0;
        Random rsn = new Random();

        while (numberofclicks < 10)
        {
            string startseconds = textBox1.Text;
            int timerstartseconds = Convert.ToInt32(startseconds);
            int timertime = rsn.Next(timerstartseconds * 1000, ((timerstartseconds + 3) * 1000));
            _timer = new System.Timers.Timer(timertime);

            _timer.Elapsed += _timer_Elapsed;
            _timer.Enabled = true;

            textBox2.Clear();
            numberofclicks++;
            string numbertextbox = numberofclicks.ToString();
            textBox2.Text = numbertextbox;

        }
    }

    void _timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        LeftClick();                        
    }

    public static void LeftClick()
    {
        mouse_event(MOUSEEVENTF_LEFTDOWN, Control.MousePosition.X, Control.MousePosition.Y, 0, 0);
        mouse_event(MOUSEEVENTF_LEFTUP, Control.MousePosition.X, Control.MousePosition.Y, 0, 0);
    }
ShoTo
  • 153
  • 1
  • 10

2 Answers2

2

When you say _timer.Enabled = true;, the rest of your code keeps executing. Then at some time later, when the timer ticks, the Elapsed event is triggered.

Additionally, each timer you create keeps on ticking - they don't only fire once.

Also, there are many different Timer classes in the .NET framework. For a simple winforms app like yours, I would stick with the System.Windows.Forms.Timer version. You don't have to worry about threading and such that way.

You might be better served with something like this:

private Random rsn = new Random();
private int numberofclicks = 0;
private System.Windows.Forms.Timer _timer;

// set the timer's tick event handler in form_load or similar.
// you could also just drag a timer onto the form and double-click it.

private void StartClicked(object sender, EventArgs e)
{        
    // don't allow starting again until finished
    btnStart.Enabled = false;
    numberofclicks = 0;
    _timer.Interval = /* get randomish interval */
    _timer.Start();
}

void _timer_Tick(object sender, EventArgs e)
{
    _timer.Stop();
    LeftClick();
    numberofclicks++;
    if (numberofclicks >= 10) {
        btnStart.Enabled = true;
    }                      
    else {
        _timer.Interval = /* get randomish interval */
        _timer.Start();
    }
}
Blorgbeard
  • 101,031
  • 48
  • 228
  • 272
  • 1
    Thank you so much! I got my answer, and I will experiment with this method as well! So when I create a new _timer, it picks (lets say 3 seconds) the time, counts down, triggers the event, resets, then count downs again, all non stop? So I am just creating 10 timers, each never stopping and continually clicking? – ShoTo Mar 11 '15 at 20:11
2

The problem lies in a fact that StartClicked event handler does not block itself. It is just a simple method that without any delay loops 10 times changing the method level variable and modifying(even creating new!) Timer properties.

It is possible to do what you attempted to do in such a manner(single method with simple loop), but you will have to use async event handler and will have no need for a timer. And as the another answer already discusses how to do it with classic timers, I will give you such async-based solution:

private async void StartClicked(object sender, EventArgs e)
{
    numberofclicks = 0;
    Random rsn = new Random();

    while (numberofclicks < 10)
    {
        string startseconds = textBox1.Text;
        int timerstartseconds = Convert.ToInt32(startseconds);
        int timertime = rsn.Next(timerstartseconds * 1000, ((timerstartseconds + 3) * 1000));

        await Task.Delay(timertime);
        LeftClick();

        textBox2.Clear();
        numberofclicks++;
        string numbertextbox = numberofclicks.ToString();
        textBox2.Text = numbertextbox;
    }
}

For more information on async-await you can read MSDN on async-await.

Community
  • 1
  • 1
Eugene Podskal
  • 10,270
  • 5
  • 31
  • 53
  • 1
    This works! I gave you the answer+ because it was much more simple here than the other option (however, I will still upvote and try out his answer as well!) If you don't mind, what does the async mean in general? I am still relatively new to programming and I am not entirely familiar with this process yet (I dont have money for school, so I use tons of online courses and youtubes so I kind of just patch-work my curiosities from other lectures and searches). – ShoTo Mar 11 '15 at 20:10
  • 1
    @ShoTo Yes, it is much simpler. Entire async await infrastructure has been designed to simplify asynchronous programming (futures, callbacks, partially parallelization...). It can also be used in a way that allows to emulate timers. But learning about actual timers, thread affinity, synchronization context invokes, thread sync constructs is also very important. It is a time worth spending, knowledge and experience that will greatly improve the way you see and understand more high-level things you will encounter. – Eugene Podskal Mar 11 '15 at 20:16
  • 1
    I did not know there was a difference. I was originally using a Sleep() but that was locking up the UI, I switched to timers because a tutorial showed that it prevented the lockup, which is what I wanted to avoid. I will try and find some tutorials and knowledge base on threads and how they function. Thank you again! – ShoTo Mar 11 '15 at 20:19