我正在编写一架飞机的随机座椅发电机,我正在使用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循环。
答案 0 :(得分:2)
首先,必读:The Windows Message Loop
@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();
}
}
答案 1 :(得分:1)
基本上,您需要生成后台线程来进行处理,然后每次使用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
中格式错误的数字文本时,控件将始终存在。
以'可爱'回答更新
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();
}
答案 2 :(得分:0)
不是获取一个随机数,检查它是否被采用然后分配它,我会取一个列表1 thru numberOfSeats
并将其随机播放并开始以此随机洗牌顺序分配席位。这样可以避免恶性while (Seats.Contains(randomNumber)){...}
循环。
答案 3 :(得分:0)
您所编写的代码仅从最多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();
这对你有用吗?
答案 4 :(得分:-1)
事情是,占用的座位只在一切都完成时显示。
预计在for循环中查看while循环:
while (Seats.Contains(randomNumber))
{
randomNumber = RandomNumber(1, numberofSeats);
}
Seats.Add(randomNumber);
您可能忘记在while循环中添加“Add”方法。还要检查退出条件 - 我想你想在Contains返回true时退出while循环。