【问题标题】:C# - Make UI function during like a infinite loop until button pressedC# - 在按下按钮之前像无限循环一样使 UI 功能
【发布时间】:2017-08-07 12:56:13
【问题描述】:

我在 C# 中编写了一个代码,我从 Access 数据库中提取了一些记录,但是我需要在进行下一次迭代时依赖于单击按钮。我尝试了一些线程或任务,但它没有工作,因为它阻止了我需要它被看到和点击的 UI。

代码如下:

    bool nextClick = false ;

                while (readerSelect.Read())
                {

                    // show the correct panel
                    if (string.Compare(readerSelect[2].ToString(), "P1") == 0)
                    {
                        // panel with type 1
                        textBoxP1Text1.Text = readerSelect[3].ToString();
                        textBoxP1Text2.Text = readerSelect[4].ToString();
                        pictureBoxP1Image.ImageLocation = readerSelect[6].ToString();

                    }
                    else
                    {
                        // panel with type 2
                        textBoxP1Text2.Text = readerSelect[5].ToString();
                    }

    //this while need to be kind of infinite so the interation can't be processed and 
   //so when i need to change iteration i click the buttonNext 
                    while (!nextClick) {
                    startWhile:; 
                       MethodInvoker mi = delegate () {
                           if (nextClick)
                           {
                               Application.DoEvents(); 
                              // System.Windows.Forms.Application.Run();
                           }

                        };
                        this.Invoke(mi);
                        //break;
                        goto startWhile; 
                    }

 private void buttonNext_Click(object sender, EventArgs e)
    {
        // click on the next button
        nextClick = true; 
    }

【问题讨论】:

  • 如果您只需要在单击后转到下一个项目,则不要使用 while 循环...只需在每次单击时执行一个...
  • 这将对我们的连接产生重大影响,这有点糟糕,但感谢您的回答
  • “对您的连接产生重大影响”是什么意思?那是不正确的。你现在这样做的方式和这种方式只会读取有多少条记录。你误会了。我建议您阅读 DataReader 文档,直到您更好地理解它作为第一点
  • 非常最简单的做法是用while(!nextClick) {Application.DoEvents();} nextClick = false; 替换整个块,它会起作用,但也会导致高 CPU 无所事事的紧密循环。

标签: c# winforms oledb


【解决方案1】:

您可以在异步任务中使用信号量,在每次单击时使用按钮 Release,并让 while 循环每次都等待它。这是一个简单的示例,使用添加了button1label1 的表单:

public partial class Form1 : Form
{
    private readonly SemaphoreSlim signal = new SemaphoreSlim(0, int.MaxValue);

    public Form1()
    {
        this.InitializeComponent();
        this.RunLoop();
    }

    private async void RunLoop()
    {
        var i = 0;
        while (true)
        {
            this.label2.Text = $"Enqueued: {this.signal.CurrentCount}";
            await this.signal.WaitAsync(); // Wait button click async
            await Task.Delay(1000); // Simulate work
            this.label1.Text = $"Completed: {++i}";
        }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        this.signal.Release();
        this.label2.Text = $"Enqueued: {this.signal.CurrentCount + 1}";
        // Or if you want to limit the # people can queue up, then put this whole
        // thing in an `if (signal.CurrentCount < myLimit)` block, and optionally
        // disable the button once limit has been reached, and re-enable it right
        // before the `WaitAsync` call above.
    }
}

【讨论】:

  • 如果用户在运行循环完成时单击多次,这不会导致 SemaphoreFullException 吗?我原以为 EventWaitHandle 类之一会更合适。
  • @MartinBrown 是的,刚刚更新了答案。
  • @matteomaggioni FWIW 我刚刚注意到龙虾的回答,并且同意那里表达的观点。虽然该答案不​​能回答您的具体问题,但我认为这可能是一种更好的方法。
【解决方案2】:

虽然 Dax Fohl 的回答有效,但您的设计似乎存在问题。我认为您违反了Single Responsibility Principle,因为Form 类中有太多业务逻辑。

我建议将业务逻辑分解到它自己的类中。然后,您只需让按钮单击事件处理下一条记录并显示结果,而不是在循环中运行所有内容。这是我的意思的一个例子:

public partial class Form1 : Form
{
    private readonly DataProcessor dataProcessor = new DataProcessor();

    public Form1()
    {
        this.InitializeComponent();
    }

    private void button1Next_Click(object sender, EventArgs e)
    {
        this.buttonNext.Enabled = false;
        this.ProcessNext();
    }

    private async void ProcessNext()
    {
        string s = await this.dataProcessor.ProcessNext();
        this.textBoxP1Text1.Text = s;
        this.buttonNext.Enabled = true;
    }
}

public class DataProcessor
{
    private readonly Random r = new Random(); // Or reader or whatever.

    public async Task<string> ProcessNext() // Just using `string` as an example.
    {
        await Task.Delay(1000);
        return this.r.Next().ToString();
    }
}

我认为这在未来会更容易理解和维护。当一个新的团队成员看到信号量的东西(或你未来的自己)时,很难理解/记住这一切的意义。在这里,您只有一个本地函数,它只做一件事并且易于遵循。

【讨论】:

    猜你喜欢
    • 2020-06-01
    • 2023-01-30
    • 2010-11-03
    • 2022-01-24
    • 1970-01-01
    • 2019-07-18
    • 1970-01-01
    • 2021-12-11
    • 1970-01-01
    相关资源
    最近更新 更多