强制循环等待事件

时间:2015-09-29 05:51:12

标签: c# loops event-handling

我正在尝试编写一个遍历项目列表的方法 - 每个项目都将其添加到表单中,然后等待用户输入一些数据并单击按钮。但是我不知道我应该/可以使用什么来创建它。

foreach (string s in List)
{
    txtName.Text = s;
    //wait for button click...

    // When the user is ready, they click the button to continue the loop.
}

到目前为止,我发现的只是EventWaitHandle类,它似乎只适用于线程。

我怎样才能做到这一点?

3 个答案:

答案 0 :(得分:3)

如果您还使用async / await,那么使用信号发送的方式 - 您可以等待按下被点击的按钮完成的任务 - 但一般情况下您应该认为关于用户界面的更多事件驱动方式。

不要在此处使用foreach循环,而是将索引保存到正在显示项目的集合中,并在每次单击按钮时将其前进。 (当然,请记住检查是否要显示的项目。)

答案 1 :(得分:1)

虽然我一般同意 Jon Skeet ,但 Mads Torgensen 有一个有趣的演示文稿,演示如何async/await可用于简化此类场景(使用 Jon 提到的技术。毕竟,与枚举器一样 - 我们可以使用像索引等状态编写自己的枚举器类,但我们几乎从不这样做,而是使用迭代器块。

无论如何,这是我们谈论的async/await技术。

首先,可重复使用的部分:

public static class Utils
{
    public static Task WhenClicked(this Button button)
    {
        var tcs = new TaskCompletionSource<object>();
        EventHandler onClick = null;
        onClick = (sender, e) =>
        {
            button.Click -= onClick;
            tcs.TrySetResult(null);
        };
        button.Click += onClick;
        return tcs.Task;
    }
}

以及使用它的代码(请注意,您需要将方法标记为async

foreach (string s in List)
{
    txtName.Text = s;
    await yourButton.WhenClicked();
}

样本测试将所有内容放在一起:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Samples
{
    static class Test
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            var form = new Form();
            var txtName = new TextBox { Parent = form, Top = 8, Left = 8 };
            var buttonNext = new Button { Parent = form, Top = txtName.Bottom + 8, Left = 8, Text = "Next" };
            form.Load += async (sender, e) =>
            {
                var List = new List<string> { "A", "B", "C", "D " };
                foreach (string s in List)
                {
                    txtName.Text = s;
                    await buttonNext.WhenClicked();
                }
                txtName.Text = "";
                buttonNext.Enabled = false;
            };
            Application.Run(form);
        }
    }
    public static class Utils
    {
        public static Task WhenClicked(this Button button)
        {
            var tcs = new TaskCompletionSource<object>();
            EventHandler onClick = null;
            onClick = (sender, e) =>
            {
                button.Click -= onClick;
                tcs.TrySetResult(null);
            };
            button.Click += onClick;
            return tcs.Task;
        }
    }
}

答案 2 :(得分:0)

我和Jon Skeet就这一点,试图强迫UI框架成为一种不正常的工作方式,这往往会导致问题。即使它有效,你也会拥有其他人难以理解的代码,因为它会以一种不寻常的方式工作。

Lisp中的一些Web框架的工作方式使页面网页显示看起来像方法调用。我没有在.NET中看到过这样做的任何内容。

我首先想到的是在循环中调用Application.DoEvents(),检查按钮回调设置的标志。但是,您的应用程序将使用100%的CPU等待而不执行任何操作。见http://blog.codinghorror.com/is-doevents-evil/

您的应用程序是finite state machine,它响应来自用户的事件,并在所需事件到达时与当前状态的状态转换匹配时进入新状态。多年来已经有很多尝试在代码中对此进行建模,我所看到的并没有出现任何比仅使用标准事件处理更好的东西。