在每个FOR循环中在屏幕上显示结果
本文关键字:显示 结果 屏幕 循环 FOR | 更新日期: 2023-09-27 18:08:43
我正在为飞机编写一个随机座位生成器,并在其中使用for循环。问题是,只有当一切都完成后,才会显示已占用的座位。我想让它在每次迭代中,显示随机选择的座位。怎么做呢?
这是我正在使用的代码。飞机有118个座位,每个座位都有一个名为"img_Seat_X"的图片框。我知道有更好的方法,但这是我能在一个小时内想到的。提前感谢!
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();
}
}
}
明白了!每次迭代中的一个简单的Refresh()就完成了这项工作!我还用if语句替换了while循环。
必读:Windows消息循环
@rice指出了我明显的错误,抱歉把你引到错误的道路上,谢谢rice。无论如何,你可以在一个单独的线程中执行工作,并使用BackgroundWorker类向UI trhead发布更新。下面是一个简单的示例,它在响应按钮点击时更新表单上的标签100次:
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();
}
}
本质上,您需要生成一个后台线程来进行处理,然后每次使用BeginInvoke
来更新用户界面元素。在这个解决方案中有一些问题需要解决。用户现在可以继续点击按钮,它将产生额外的后台线程。最常见的机制是弹出一个进度对话框(我讨厌模态对话框,所以不要这样做),或者通过禁用按钮来防止用户执行两次操作,直到工作完成。
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;
}
}
还有其他问题需要解决。例如,当调用FindControl
而不处理txt_WeightBalance_Passengers
中的畸形数字文本时,假设控件将始终存在。
更新'cute'答案
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();
}
不是获得一个随机数,检查它是否被占用,然后分配它,而是获取一个列表1 thru numberOfSeats
并对其进行洗牌,并开始按这种随机洗牌的顺序分配座位。这样可以避免while (Seats.Contains(randomNumber)){...}
的恶性循环。
您正在编写的代码仅从最多119个座位中随机选择座位。即使您使用的是旧硬件,它也应该运行速度非常快,所以我不明白为什么您需要显示每个座位的分配情况。在我看来,你应该把代码拆分。计算座位分配,然后按你喜欢的方式显示。
下面是随机选择座位的代码:
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();
现在要将座椅控制设置为可见,只需执行以下操作:
foreach (var c in controls)
{
c.Visible = true;
}
如果你有一个特定的需要在后台运行这个并更新UI,你可以这样做:
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();
这个对你有用吗?
问题是,只有当所有的事情都完成了,才会显示出有人坐。
这是在for循环中看到的while循环:
while (Seats.Contains(randomNumber))
{
randomNumber = RandomNumber(1, numberofSeats);
}
Seats.Add(randomNumber);
你可能忘记在while循环中添加"Add"方法。还要检查退出条件-我猜您想要在Contains返回true时退出while循环。