我一直在学习C#,使用书#34;编程在C#的键中...",这本书非常善于帮助我理解语言,但只处理Console程序。我准备继续开发我过去编码项目的版本作为Windows表单应用程序,但特别是一个程序让我很沮丧。我开发了一个简单的电影琐事程序,利用数组来保存问题,回答选择和正确的答案。它通过在控制台上显示问题,可能的答案并等待用户通过使用Console.Readline()来分配响应来提供响应(基本上是A,B,C或D)。
现在,我希望能够让用户通过选择4个按钮中的一个(A到D)来输入答案。基于我的旧代码,我不确定如何让程序等待用户单击其中一个按钮。我假设我需要改变循环的性质,但我无法弄清楚如何。任何帮助将不胜感激。
以下是我的控制台代码片段:
while (iAsked < 5)
{
iLocation = rand.Next(0, astrQuestions.GetLength(0));
if (list.Contains(iLocation))
rand.Next(0, astrQuestions.GetLength(0));
else
{
iAsked++;
list.Add(iLocation);
Console.WriteLine("Question {0}", iAsked);
Console.WriteLine("------------");
Console.WriteLine(astrQuestions[(iLocation)]);
Console.WriteLine(astrChoices[(iLocation)]);
Console.Write("Answer:");
iResponse = Console.ReadLine();
if (iResponse == astrAnswers[(iLocation)])
{
Console.WriteLine("Correct\n");
iPoints += 5;
iCorrect++;
}
else
{
Console.WriteLine("Incorrect\n");
}
}
答案 0 :(得分:0)
从一个以提示为中心的环境(如控制台程序)转移到像Winforms这样的事件驱动环境,是的......这肯定至少需要对“循环的性质”进行一些更改。 :)
也就是说,最新版本的C#提供了一种基于async
/ await
的方法,可以最大限度地减少从控制台转移到GUI所带来的一些文化冲击。编写和使用async
方法本身并不重要,但恕我直言,更简单的方案并不难理解。更重要的是,因为它允许您以更直接命令的方式构造代码,类似于在控制台程序中使用的代码,所以非常值得与Winforms一起学习。
在您的特定情况下,您需要处理两件事:提示用户并接收用户的输入。
由于事件驱动系统的工作方式,您需要将这些任务分开。但.NET有一个类TaskCompletionSource
,我们可以使用它来保持两个粘在一起,即使它们在不同的地方结束。
首先,当用户启动流程时会发生什么?据推测,你将有一个表单,在那个表单上是一个按钮(或可能是一个菜单项),当点击/选择时,它启动整个事情。这可能看起来像这样:
private TaskCompletionSource<bool> _completionSource;
private async void button1_Click(object sender, EventArgs e)
{
int[] questionIndexes = ShuffleQuestions();
for (int iAsked = 0; iAsked < 5; iAsked++)
{
textBoxQuestionNumber.Text = string.Format("Question {0}", iAsked);
textBoxQuestion.Text = astrQuestions[questionIndexes[iAsked]];
textBoxChoices.Text = astrChoices[questionIndexes[iAsked]];
_completionSource =
new TaskCompletionSource<bool>(astrAnswers[questionIndexes[iAsked]]);
button2.Enabled = true;
bool result = await _completionSource.Task;
MessageBox.Show(result ? "Correct" : "Incorrect");
if (result)
{
iPoints += 5;
iCorrect++;
}
button2.Enabled = false;
_completionSource = null;
}
}
private void button2_Click(object sender, EventArgs e)
{
if (_completionSource != null)
{
_completionSource.SetResult(
textBoxUserAnswer.Text == (string)_completionsSource.Task.AsyncState);
}
}
(我已假设您使用ShuffleQuestions()
方法,将上面的问题选择逻辑更改为更有效的方法。有关如何实现该方法的详细信息,请参阅Is using Random and OrderBy a good shuffle algorithm?。
上述代码的作用是,响应用户单击button1
按钮(可能包含“Start”等文本),执行的循环与您在控制台中的循环非常相似程序。两个主要区别是:
Console.WriteLine()
向用户显示文本,此处我已在您的表单中显示使用TextBox
控件,这些控件用于显示相同的文本。Console.ReadLine()
接收来自用户的输入,此循环创建一个TaskCompletionSource
对象,以使用完全不同的方法。使用button2
按钮(可能包含“检查答案”等文本)执行的方法将读取用户在文本框中输入的文本(此处,我已将其命名为{ {1}}),将其与问题的正确答案进行比较(通过textBoxUserAnswer
创建的AsyncState
Task
属性通过其他方法提供给此方法我创建的对象),并将TaskCompletionSource
的结果设置为Task
或true
,具体取决于用户是否得到了正确答案。上面棘手的部分是“幕后”,第一个方法实际上返回,只要填写完第一个问题的文本并到达false
语句在循环。编译器会重写整个方法以促进这一点。
当推送await
并设置button2
的结果时,框架会知道继续执行第一个方法,它在Task
语句处停止,继续循环
此序列一直持续到用户回答完所有问题为止。
关于用户界面的最后说明:
await
来进行用户输入和输出。当然,还有其他方式来显示文本。此外,TextBox
的默认状态是单行读/写文本。但是,为了向用户显示,您可能会发现将TextBox
的{{1}}属性设置为true更好(即防止用户意外更改文本),和/或您更喜欢设置ReadOnly
属性为TextBox
(即显示多行文本)。Multiline
按钮的true
属性的初始状态为button2
。即在上面的第一个方法在适当的时间明确启用按钮之前,无法单击该按钮。