将C#代码从Console迁移到Windows Form Application

时间:2015-01-23 16:16:54

标签: c# user-interface buttonclick

我一直在学习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");
            }
        }

1 个答案:

答案 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的结果设置为Tasktrue,具体取决于用户是否得到了正确答案。

上面棘手的部分是“幕后”,第一个方法实际上返回,只要填写完第一个问题的文本并到达false语句在循环。编译器会重写整个方法以促进这一点。

当推送await并设置button2的结果时,框架会知道继续执行第一个方法,它在Task语句处停止,继续循环

此序列一直持续到用户回答完所有问题为止。

关于用户界面的最后说明:

  • 我到处都使用await来进行用户输入和输出。当然,还有其他方式来显示文本。此外,TextBox的默认状态是单行读/写文本。但是,为了向用户显示,您可能会发现将TextBox的{​​{1}}属性设置为true更好(即防止用户意外更改文本),和/或您更喜欢设置ReadOnly属性为TextBox(即显示多行文本)。
  • 以上还假设Multiline按钮的true属性的初始状态为button2。即在上面的第一个方法在适当的时间明确启用按钮之前,无法单击该按钮。