【问题标题】:Winforms how to wait for user to click a button? [duplicate]Winforms如何等待用户点击按钮? [复制]
【发布时间】:2021-04-13 16:33:30
【问题描述】:

我正在尝试设计一个二十一点游戏,在某些时候,我需要选择要采取的行动:击球、站立、拆分或双下。我为每个操作都有一个按钮,并且我已将它们的所有单击事件处理程序添加到表单设计器中的同一方法中,就像这样(我真的不知道描述它的确切方式,但我指的是事件处理程序和 btnRound_Click 方法之间的链接)

this.btnSplit.Click += new System.EventHandler(this.btnRound_Click);
this.btnDoubled.Click += new System.EventHandler(this.btnRound_Click);
this.btnHit.Click += new System.EventHandler(this.btnRound_Click);
this.btnStand.Click += new System.EventHandler(this.btnRound_Click);
//Inside the Form1 class

enum RoundPlay
        {
            hit,
            stand,
            doubled,
            split
        }
        RoundPlay playchoice = RoundPlay.none;

private void btnRound_Click(object sender, EventArgs e)
        {
            switch (((Button)sender).Text)
            {                
                case "Hit":
                    playchoice = RoundPlay.hit;
                    break;
                case "Stand":
                    playchoice = RoundPlay.stand;
                    break;
                case "Split":
                    playchoice = RoundPlay.split;
                    break;
                case "Double Down":
                    playchoice = RoundPlay.doubled;
                    break;
            }            
        }

在我的主要方法中:

//enabling the buttons so that I can click on one of them

//place to wait for the user to click on one of the buttons
//this is where I tried using await operator 

switch (playchoice)
                {
                    case RoundPlay.hit:
                        //one action
                        break;
                    case RoundPlay.stand:
                        //other action
                        break;
                    case RoundPlay.doubled:
                        //other action
                        break;
                    case RoundPlay.split:
                        //other action
                        break;
                }

那么,“等待”用户输入的最佳方式是什么?我已经搜索了很多,但我能找到的只是一个按钮问题的解决方案,或者主要方法是否更改无关紧要的情况(为每个事件处理程序创建一个方法)。在这种情况下,我想保持相同的方法,这样我就可以重复循环,这意味着,我想把它放在一个 Do...While() 循环中。

我已经尝试过使用 ManualResetEvent 和 async/await,但我对其中的任何一个都没有太多了解,所以如果有人有任何建议,我将不胜感激! :)

在另一个不太重要的问题上,我刚刚注意到我在 Form1_Load 方法中编写了所有与游戏进度相关的代码,这有多不正确?里面有没有更好的地方写这个?抱歉,如果有什么不清楚的地方,这是我的第二篇文章 谢谢

【问题讨论】:

  • I want to stay in the same method, so that I can repeat rounds, meaning, I want to have this inside a Do...While() loop...你的概念是错误的。到目前为止,您是否一直在编写控制台应用程序?单击按钮应触发用户选择。一旦竞争完成,您就可以开始新一轮 - 调用一个方法,该方法移动事物,直到它达到再次需要用户输入的状态,然后完成。然后代码什么也不做,直到用户按下另一个按钮来触发下一步。
  • I just noticed I'm writting all game progress related code inside the Form1_Load method, how incorrect is this...对于快速而肮脏的/初学者解决方案来说还可以,但从长远来看,或者对于更大的应用程序来说并不好。大多数应用程序通常都有逻辑排列的类来处理逻辑的不同部分/代表程序中的不同实体等。您需要了解更多有关面向对象编程的信息,这是一个很大的话题。但现在坚持研究如何处理 UI。
  • @ADyson 是的,我有......我基本上试图“复制” Console.ReadLine() 方法。所以我应该为游戏的每个状态制定方法,并将它们分为那些在没有输入的情况下运行的方法和那些仅用于输入的方法?谢谢!
  • @ADyson 所以现在把游戏相关的代码塞进 Form1_Load() 里不是那么可怕吗?我习惯于控制台,我会把所有东西都放在 Main() 中,但是这里有几个地方可以写代码,我碰巧注意到把东西放在 Form1_Load 中就可以了

标签: c# winforms async-await user-input


【解决方案1】:

Winforms 是一个框架而不是一个库。框架通常具有控制反转 (IoC),它们调用您的代码而不是您控制它们。

最简单的更改是将您的 while 循环体添加到一个新方法中,例如

private void NextAction() 
{
  switch(playchoice) 
  {
    ...
  }
  playchoice = RoundPlay.none;
}

然后在按钮事件处理程序的末尾调用这个方法

private void btnRound_Click(...) 
{
  switch (((Button)sender).Text)
  {
    ...
  }
  NextAction(); 
}

【讨论】:

    【解决方案2】:

    下面的方法创建一个Task<Control>,当点击任何提供的Controls 时完成:

    public static Task<Control> OnClickAnyAsync(params Control[] controls)
    {
        var tcs = new TaskCompletionSource<Control>();
        foreach (var control in controls) control.Click += OnClick;
        return tcs.Task;
    
        void OnClick(object sender, EventArgs e)
        {
            foreach (var control in controls) control.Click -= OnClick;
            tcs.SetResult((Control)sender);
        }
    }
    

    您可以使用此方法对await 使用四个按钮中的任何一个进行点击,并根据点击的按钮返回具有适当值的Task&lt;RoundPlay&gt;

    private async Task<RoundPlay> GetPlayChoiceAsync()
    {
        var clickedButton = await OnClickAnyAsync(btnHit, btnStand, btnDoubled, btnSplit);
        if (clickedButton == btnHit) return RoundPlay.hit;
        if (clickedButton == btnStand) return RoundPlay.stand;
        if (clickedButton == btnDoubled) return RoundPlay.doubled;
        if (clickedButton == btnSplit) return RoundPlay.split;
        throw new NotImplementedException();
    }
    

    最后你可以像这样使用GetPlayChoiceAsync 方法:

    // Enabling the buttons so that I can click on one of them
    // Wait the player to make a choice, without blocking the UI
    RoundPlay playchoice = await GetPlayChoiceAsync();
    // Do something with the playchoice
    

    【讨论】:

    • 这更符合我试图做的事情,即使我不太了解任务但我必须研究它,主要是 TaskCompletionSource 类及其任务属性谢谢!
    • @NunoCatalão TaskCompletionSource&lt;T&gt; 是一种简单的机制,允许通过在适当的时刻调用 SetResult/SetException/SetCanceled 方法之一以编程方式控制 TaskTask 属性是你给消费者观察的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-21
    • 2021-12-07
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 2019-01-11
    • 1970-01-01
    相关资源
    最近更新 更多