Question

I'm doing an 8 Puzzle solver that ultimately stores each node (int[] of elements 0-8) in the path to put the blocks in order in a stack. I have a WPF GUI that displays an int[,]

foreach (var node in stack)
        {
            int[,] unstrung = node.unstringNode(node); // turns node of int[] into board of  int[,]
            blocks.setBoard(unstrung); // sets the board to pass in to the GUI
            DrawBoard(); // Takes the board (int[,]) and sets the squares on the GUI to match it.
            Thread.Sleep(500);
        }

The GUI displays the initial board, and then after I click solve, the final (in order) board is displayed correctly. What I want to do is display each node on the board for some amount of time, ultimately arriving at the in-order board. With Thread.Sleep, the GUI will simply pause for the set amount of time before displaying the final node. Any ideas as to why it this code wouldn't display the board at each node every 500ms?

For reference, here's an example output from Console.Write for the nodes:
4,2,3,6,1,0,7,5,8
4,2,0,6,1,3,7,5,8
4,0,2,6,1,3,7,5,8
4,1,2,6,0,3,7,5,8
4,1,2,0,6,3,7,5,8
0,1,2,4,6,3,7,5,8
1,0,2,4,6,3,7,5,8
1,2,0,4,6,3,7,5,8
1,2,3,4,6,0,7,5,8
1,2,3,4,0,6,7,5,8
1,2,3,4,5,6,7,0,8
1,2,3,4,5,6,7,8,0

Was it helpful?

Solution

Edit:

Since my original answer was downvoted for using a Thread instead of a Timer, here is an example using a timer.

The code for using a Thread was just shorter and I wanted to give him a solution quickly.

Also, using a Thread instead of a timer meant he didn't need to pass parameters differently or restructure his loop.

This is why it is a good idea to discuss pros/cons of alternate solutions instead of simply insisting that there is only one right way.

Use the timer_Tick function to update the position.

You might notice that this complicates the original code since you will have to pass parameters differently and restructure your loop.

public partial class Form1 : Form
{
    private Point pos = new Point(1,1);
    private float[] vel = new float[2];
    private Size bounds = new Size(20,20);
    private Timer ticky = new Timer();      //System.Windows.Forms.Timer
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        ticky.Interval = 20;
        ticky.Tick += ticky_Tick;
        vel[0] = 4; vel[1] = 0;
        ticky.Start();
    }
    void ticky_Tick(object sender, EventArgs e)
    {
        updatePosition();
        //This tells our form to repaint itself (and call the OnPaint method)
        this.Invalidate();
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        e.Graphics.FillEllipse(new SolidBrush(Color.LightBlue), new Rectangle(pos, bounds));
    }
    private void updatePosition()
    {
        pos = new Point(pos.X + (int)vel[0], pos.Y + (int)vel[1]);
        vel[1] += .5f; //Apply some gravity
        if (pos.X + bounds.Width > this.ClientSize.Width)
        {
            vel[0] *= -1;
            pos.X = this.ClientSize.Width - bounds.Width;
        }
        else if (pos.X < 0)
        {
            vel[0] *= -1;
            pos.X = 0;
        }
        if (pos.Y + bounds.Height > this.ClientSize.Height)
        {
            vel[1] *= -.90f; //Lose some velocity when bouncing off the ground
            pos.Y = this.ClientSize.Height - bounds.Height;
        }
        else if (pos.Y < 0)
        {
            vel[1] *= -1;
            pos.Y = 0;
        }
    }
}

Results:

Results

You can use timers to do all sorts of delayed form drawing:

More

Original Solution:

//Create a separate thread so that the GUI thread doesn't sleep through updates:
using System.Threading;

new Thread(() => {
    foreach (var node in stack)
    {
        //The invoke only needs to be used when updating GUI Elements
        this.Invoke((MethodInvoker)delegate() {
            //Everything inside of this Invoke runs on the GUI Thread
            int[,] unstrung = node.unstringNode(node); // turns node of int[] into board of  int[,]
            blocks.setBoard(unstrung); // sets the board to pass in to the GUI
            DrawBoard(); // Takes the board (int[,]) and sets the squares on the GUI to match it.
        });
        Thread.Sleep(500);
    }
}).Start();

Solution in 2022:

await Task.Delay(500);

Things really are better these days.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top