2

I have made a Form that moves across the screen to the left, but all of the components on the Form are blank.

I put the code for the movement in comments and everything was fine, so the problem is in my movement code, but I don't know what the problem is.

System.Threading.Thread thread;

private void Form1_Load(object sender, EventArgs e)
{
    thread = new System.Threading.Thread(loop);
    thread.Start();
}

void loop()
{
    this.BeginInvoke((Action)delegate () {
        int x = 0;
        int y = 0;
        int MoveRate = 1;
        Point TopLeft = this.Location;
        Point TopRight = new Point (this.Location.X + this.Width, this.Location.Y);
        while (true)
        {
            x = x + MoveRate;
            this.Location = new Point(x, 150);
            System.Threading.Thread.Sleep(10);
        }
    });     
}

This should make the form move to the left, however the components on the Form are blank.

Jimi
  • 29,621
  • 8
  • 43
  • 61
Hamez
  • 33
  • 4

2 Answers2

3

Let's look at the loop() method:

void loop()
{
    this.BeginInvoke((Action)delegate () {
        int x = 0;
        int y = 0;
        int MoveRate = 1;
        Point TopLeft = this.Location;
        Point TopRight = new Point (this.Location.X + this.Width, this.Location.Y);
        while (true)
        {
            x = x + MoveRate;
            this.Location = new Point(x, 150);
            System.Threading.Thread.Sleep(10);
        }
    });     
}

This code immediately invokes a delegate back on the main UI thread. The delegate runs a while(true) loop that never exits. As soon as the loop begins to execute, the UI thread is completely hosed with no hope of ever responding to other event messages, including paint events.

Try this instead:

void loop()
{
    int x = 0;
    int MoveRate = 1;

    while(true)
    {
        x += MoveRate;
        this.BeginInvoke((Action)delegate () { this.Location = new Point(x, 150); });
        System.Threading.Thread.Sleep(16);
    } 
}

It's all the same code (except for the stuff that wasn't doing anything), but now arranged so the delegate is invoked inside the loop. The UI thread is only blocked for a brief time, and then control returns back to the loop thread, which will Sleep for a little while before bothering the UI again. The invoke even simple enough I was able to rewrite it as single line.

Notice I also increased the sleep time, because that still gives you 60 frames per second.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
2

An Async variation of the same process, to test something different.
(The main reason why your thread wasn't working as expected has already been explained. If you use a thread and then invoke the UI thread in a close loop, it's more or less like not having your code run in a different thread at all: a Form doesn't have time to update itself or its controls).

This method add a termination to the scrolling procedure, when the Form is scrolled outside the current Screen bounds. When this condition is met, the while loop is exited and the Task ends, moving the Form in the center of the screen.

The Task is started in the Shown event. I think it's more appropriate than the Load event (the Form is ready to be presented, here).

Note that neither this Task or the Thread add any check on the Form.FormClosing event, to cancel the asynchronous proc: if the Form is closed while the scrolling is performed, you will most likely have a exception (the Form has been disposed, thus no more handle).

private async Task Scroller(int ScreenWidth)
{
    int x = 0;
    int MoveRate = 2;

    while (true)
    {
        x += MoveRate;
        this.BeginInvoke(new MethodInvoker(() => { this.Location = new Point(x, 150);}));
        await Task.Delay(10);
        if (x > ScreenWidth) break;
    };
}

private async void Form_Shown(object sender, EventArgs e)
{
    int ScreenWidth = Screen.FromHandle(this.Handle).Bounds.Width;
    await this.Scroller(ScreenWidth);
    this.Location = new Point((ScreenWidth - this.Width) / 2 , 150);
}
Jimi
  • 29,621
  • 8
  • 43
  • 61