Search code examples
c#.netvisual-studioposition

Changing position of a Form Item Dynamically


I created a form where a 'Rectangle' item's location needs to be changed dynamically such that a 'constant movement' effect is obtained.

enter image description here

I want the black rectangle to keep moving within the boundaries of the form. I tried this:

//globally decalred variables
Random locationRandomizer = new Random();
int count = 0;

private void Form1_Load(object sender, EventArgs e)
{
     Thread movementThread = new Thread(new ThreadStart(movementFunction));
     movementThread.Start();
}


public void movementFunction()
{
     try
     {
          int x;
          int y;

          int windowHeight = this.Height;
          int windowWidth = this.Width;

          do
          {
              x = locationRandomizer.Next(1, windowWidth);
              y = locationRandomizer.Next(1, windowWidth);
              animalDot.Location = new Point(x, y);
          }
          while (count == 0);
          }
     catch (Exception ex)
     {

     }
}

But the rectangle moves ONCE when the form is loaded and does not change after that. What am I doing wrong?

When I removed the empty try catch block, this is the error I got:

Cross-thread operation not valid: Control 'shapeContainer1' accessed from a thread other than the thread it was created on.


Solution

  • You're getting that error because you're accessing a UI element from a non-UI thread. You can get around it by calling the Invoke method on the control, to marshal the call back to the UI thread:

    textBox1.Invoke((MethodInvoker)(() => textBox1.Location = new Point(x, y)));
    

    A better way to execute some code that affects the UI at regular intervals is to use a System.Windows.Forms.Timer, which is the Timer control you can just drop in the designer.

    Subscribe to the Tick event, which runs on the UI thread and avoids the above error.

    private void timer_Tick(object sender, EventArgs e)
    {
        var x = locationRandomizer.Next(1, Width - animalDot.Width);
        var y = locationRandomizer.Next(1, Height - animalDot.Height);
        animalDot.Location = new Point(x, y);
    }
    

    Then in the constructor, you can set the interval to something relatively small and enable the timer.

    timer.Interval = 100;
    timer.Enabled = true;