开发者

Show result on screen in each FOR loop?

开发者 https://www.devze.com 2023-04-04 12:15 出处:网络
I\'m coding a random seat generator for an airplane and I\'m using a FOR loop in it. The thing is, the occupied seats only show when everything is done. What I would like it to do was in each iteratio

I'm coding a random seat generator for an airplane and I'm using a FOR loop in it. The thing is, the occupied seats only show when everything is done. What I would like it to do was in each iteration, show the random seat selected. How would one do that?

Here's the code I'm using. The plane has 118 seats and I have a picturebox named "img_Seat_X" for each one of them. I know there's a better way to this, but that's I could think in a quick hour. Thanks in advance!

private void btn_WeightBalance_Populate_Click(object sender, EventArgs e)
{
                int passengers = Convert.ToInt32(txt_WeightBalance_Passengers.Text);
                List<int> seats = new List<int> { }; numberofSeats = 119;

                if (rdb_WeightBalance_190.Checked == true)
                    numberofSeats = 107;

                for (int x = 0; x < Passengers; x++)
                {
                    int randomNumber = RandomNumber(1, numberofSeats);

                    if (seats.Contains(randomNumber))
                        x--;

                    else
                    {

                        seats.Add(randomNumber);

                        Control[] seat = this.panWeightBalance.Controls.Find("img_Seat_" + randomNumber, true);
                        seat[0].Visible开发者_运维问答 = true;
                        seat[0].Refresh();
                    }
                }
}

Figured it out! A simple Refresh() in each iteration did the job! I also replaced the while loop with an if statement.


First, required reading: The Windows Message Loop

@rice pointed out my obvious error, sorry for leading you down the wrong path, thanks rice.

Anyway, you can perform the work in a separate thread and post updates to the UI trhead using the BackgroundWorker class. Here is a simple example which updates a label on a form 100 times in response to a button click:

public partial class Form1 : Form
{
    BackgroundWorker _worker;
    public Form1()
    {
        InitializeComponent();
        _worker = new BackgroundWorker();
        _worker.WorkerReportsProgress = true;
        _worker.DoWork += _worker_DoWork;
        _worker.ProgressChanged += _worker_ProgressChanged;
    }

    private void _worker_ProgressChanged( object sender, ProgressChangedEventArgs e )
    {
        label1.Text = e.UserState.ToString();
    }

    private void _worker_DoWork( object sender, DoWorkEventArgs e )
    {
        for( int i = 0; i < 100; ++i )
        {
            _worker.ReportProgress( i, i );
            // allow some time between each update,
            // for demonstration purposes only.
            System.Threading.Thread.Sleep( 15 );
        }
    }   

    private void button1_Click( object sender, EventArgs e )
    {
        _worker.RunWorkerAsync();
    }
}


Essentially you need to spawn a background thread to do the processing, then use BeginInvoke to update the User Interface elements each time. There are some issues you will need to work out in this solution. The user can now continue to click the button and it will spawn additional background threads. The most common mechanism is to pop a progress dialog (I hate modal dialogs , so don't do this) or prevent the user from taking the action twice by disabling the button until the work is completed.

    private void btn_WeightBalance_Populate_Click(object sender, EventArgs e)
    {
        int passengers = Convert.ToInt32(txt_WeightBalance_Passengers.Text);

        List<int> seats = new List<int> { }; numberofSeats = 119;

        if (rdb_WeightBalance_190.Checked == true)
            numberofSeats = 107;

        BackgroundWorker worker = new BackgroundWorker();
        worker.DoWork += delegate
        {
            for (int x = 0; x < passengers; x++)
            {
                int randomNumber = RandomNumber(1, numberofSeats);
                while (seats.Contains(randomNumber))
                {
                    randomNumber = RandomNumber(1, numberofSeats);
                }
                seats.Add(randomNumber);

                UpdateSeat(randomNumber);
            }
        };
        worker.RunWorkerAsync();
    }

    /// <summary>
    /// Update a seat control in the correct UI thread. If this
    /// method is invoked in a thread besides the UI thread it will use
    /// BeginInvoke to put it on the UI thread queue.
    /// </summary>
    /// <param name="seatNumber"></param>
    private void UpdateSeat (int seatNumber)
    {
        if (this.InvokeRequired)
        {
            this.BeginInvoke((Action)(() => UpdateSeat(seatNumber)));
        }
        else
        {
            Control[] seat = this.Controls.Find("img_Seat_" + seatNumber, true);
            seat[0].Visible = true;
        }
    }

There are additional issues that should be solved. Such as the assumption a Control will always exist when calling FindControl and not handling malformed numeric text in txt_WeightBalance_Passengers.

Update with 'cute' answer

    private void btn_WeightBalance_Populate_Click(object sender, EventArgs e)
    {
        var passengers = Convert.ToInt32(txt_WeightBalance_Passengers.Text);
        numberofSeats = rdb_WeightBalance_190.Checked ? 107 : 119;
        var worker = new BackgroundWorker();
        worker.DoWork += delegate
        {
            var random = new Random();
            foreach (var seatNumber in Enumerable.Range(1, Int32.MaxValue).Select(r => random.Next(numberofSeats)).Distinct())
            {
                var randomSeat = seatNumber;
                BeginInvoke((Action)(() =>
                {
                    var seat = this.Controls.Find("img_Seat_" + randomSeat, true);
                    seat[0].Visible = true;
                }));                    
                if (--passengers <= 0) break;
            }
        };
        worker.RunWorkerAsync();
    }


Instead of getting a random number, checking to see if it's taken and then assigning it, I would take a list 1 thru numberOfSeats and shuffle it and start assigning seats in this random shuffled order. That way can avoid the vicious while (Seats.Contains(randomNumber)){...} loop.


The code you're writing is only selecting random seats from a maximum of 119 seats. Even if you're using old hardware it should be blazzingly fast to run so I don't see why you need to show each seat as it is allocated. It seems to me that you should split the code up. calculate the seat allocations and then display them any which way you like.

Here's the code to randomly select the seats:

var passengers = Convert.ToInt32(txt_WeightBalance_Passengers.Text);
var numberofSeats = rdb_WeightBalance_190.Checked ? 107 : 119;

// Creates an array from 0 .. numberofSeats - 1
var seats = Enumerable.Range(0, numberofSeats).ToArray();

//Shuffle the first "passengers" elements of the array
for (var i = 0; i < passengers; i++)
{
    var j = RandomNumber(0, numberofSeats);
    var x = seats[i];
    seats[i] = seats[j];
    seats[j] = x;
}

//Find the first "passengers" count of seat controls
var controls = (
        from i in seats.Take(passengers)
        let c = this.Controls.Find("img_Seat_" + i, true).FirstOrDefault()
        where c != null
        select c
    ).ToArray();

Now to set the seat controls as visible just do this:

foreach (var c in controls)
{
    c.Visible = true;
}

If you have a specific need to run this in the background and update the UI as it does so, you could do this:

var t = new System.Threading.Thread(new ThreadStart(() =>
{
    foreach (var c in controls)
    {
        this.Invoke(new Action(() => c.Visible = true));
        // Thread.Sleep(100); // Slow it down if you wish...
    }
}));
t.Start();

Does this work for you?


The thing is, the occupied seats only show when everything is done.

That is expected looking at the while loop inside your for loop:

while (Seats.Contains(randomNumber))
{
    randomNumber = RandomNumber(1, numberofSeats);
}
Seats.Add(randomNumber); 

You probably forgot to put the "Add" method inside your while loop. Also check the exit condition - I guess you want to exit the while loop when Contains returns true.

0

精彩评论

暂无评论...
验证码 换一张
取 消