如何从按钮点击事件中获取返回值?

3

我刚开始学习C#。我看到了一个旧问题,有人试图制作可口可乐机,这似乎是个不错的练习。

但我卡在了钱的按钮上。我想不出如何将按钮代表的金额存储在一个变量中,并让ColaMachine方法可以访问。

我的代码如下:

using System;
using System.Windows.Forms;
using System.Drawing;

namespace QuickSharp
{
    public class ColaMachine : Form
    {
        public ColaMachine()
        {
            this.Text = "Cola Machine";
            this.Size = new Size(450 , 500);

            //Money & Money Buttons   

            Label Money;
            Money = new Label();
            Money.Text = "Insert Coins Here:";
            Money.Location = new Point(20, 100);
            this.Controls.Add(Money);

            Button MoneyButton1;
            MoneyButton1 = new Button();
            MoneyButton1.Text = "€0,05";
            MoneyButton1.Location = new Point(28,125);
            MoneyButton1.Click += new System.EventHandler(this.MoneyButton1_Click);
            this.Controls.Add(MoneyButton1);

            Button MoneyButton2;
            MoneyButton2 = new Button();
            MoneyButton2.Text = "€0,10";
            MoneyButton2.Location = new Point(28,165);
            MoneyButton2.Click += new System.EventHandler(this.MoneyButton2_Click);
            this.Controls.Add(MoneyButton2);

            Button MoneyButton3;
            MoneyButton3 = new Button();
            MoneyButton3.Text = "€0,20";
            MoneyButton3.Location = new Point(28,205);
            MoneyButton3.Click += new System.EventHandler(this.MoneyButton3_Click);
            this.Controls.Add(MoneyButton3);

            Button MoneyButton4;
            MoneyButton4 = new Button();
            MoneyButton4.Text = "€0,50";
            MoneyButton4.Location = new Point(28,245);
            MoneyButton4.Click += new System.EventHandler(this.MoneyButton4_Click);
            this.Controls.Add(MoneyButton4);

            Button MoneyButton5;
            MoneyButton5 = new Button();
            MoneyButton5.Text = "€1,00";
            MoneyButton5.Location = new Point(28,285);
            MoneyButton5.Click += new System.EventHandler(this.MoneyButton5_Click);
            this.Controls.Add(MoneyButton5);

            Button MoneyButton6;
            MoneyButton6 = new Button();
            MoneyButton6.Text = "€2,00";
            MoneyButton6.Location = new Point(28,325);
            MoneyButton6.Click += new System.EventHandler(this.MoneyButton6_Click);
            this.Controls.Add(MoneyButton6);

            // Drinks & Drink Buttons

            Label Drinks;
            Drinks = new Label();
            Drinks.Text = "Choose Your Drink:";
            Drinks.Location = new Point(315 , 100);
            Drinks.AutoSize = true;
            this.Controls.Add(Drinks);

            Button DrinkButton1;
            DrinkButton1 = new Button();
            DrinkButton1.Text = "Coca-Cola";
            DrinkButton1.Location = new Point(328,125);
            this.Controls.Add(DrinkButton1);

                        Button DrinkButton2;
            DrinkButton2 = new Button();
            DrinkButton2.Text = "Coca-Cola Light";
            DrinkButton2.Location = new Point(328,165);
            this.Controls.Add(DrinkButton2);

                        Button DrinkButton3;
            DrinkButton3 = new Button();
            DrinkButton3.Text = "Fanta";
            DrinkButton3.Location = new Point(328,205);
            this.Controls.Add(DrinkButton3);

                        Button DrinkButton4;
            DrinkButton4 = new Button();
            DrinkButton4.Text = "Sprite";
            DrinkButton4.Location = new Point(328,245);
            this.Controls.Add(DrinkButton4);

                        Button DrinkButton5;
            DrinkButton5 = new Button();
            DrinkButton5.Text = "Spa Blauw";
            DrinkButton5.Location = new Point(328,285);
            this.Controls.Add(DrinkButton5);

                        Button DrinkButton6;
            DrinkButton6 = new Button();
            DrinkButton6.Text = "Red Bull";
            DrinkButton6.Location = new Point(328,325);
            this.Controls.Add(DrinkButton6);

            //Header & Machine Display

            Label Header;
            Header = new Label();
            Header.Text = "Coca-Cola Machine";
            Header.Font = new Font("Arial" , Header.Font.Size +5);
            Header.ForeColor = Color.DarkRed;
            Header.Location = new Point(132, 20);
            Header.AutoSize = true;
            this.Controls.Add(Header);



            TextBox TextBox1 ;
            TextBox1 = new TextBox();

            if(InsertedCoins == 0.00)
                TextBox1.Text = "Buy Your Ice Cold Drinks Here!";
            else
                TextBox1.Text = "Inserted Coins: €" + InsertedCoins;

            TextBox1.BackColor = Color.Black;
            TextBox1.ForeColor = Color.Red;
            TextBox1.Font = new Font("Arial" , TextBox1.Font.Size +3);
            TextBox1.ReadOnly = true;
            TextBox1.Size = new Size(210,300);
            TextBox1.Location = new Point(112,50);

            // I tried to get the text scrolling here... :)
            TextBox1.SelectionStart = TextBox1.Text.Length;
            TextBox1.ScrollToCaret();
            TextBox1.Refresh();

            this.Controls.Add(TextBox1);
        }


        public double InsertedCoins;

        // Money Button Click Events

        private void MoneyButton1_Click(object sender, EventArgs e)
        {
            InsertedCoins = InsertedCoins + 0.05;
        }

        private void MoneyButton2_Click(object sender, EventArgs e)
        {
            InsertedCoins = InsertedCoins + 0.10;
        }

        private void MoneyButton3_Click(object sender, EventArgs e)
        {
            InsertedCoins = InsertedCoins + 0.20;
        }

        private void MoneyButton4_Click(object sender, EventArgs e)
        {
            InsertedCoins = InsertedCoins + 0.50;
        }

        private void MoneyButton5_Click(object sender, EventArgs e)
        {
            InsertedCoins = InsertedCoins + 1.00;
        }

        private void MoneyButton6_Click(object sender, EventArgs e)
        {
            InsertedCoins = InsertedCoins + 2.00;
        }

        private static void Main()
        {

            ColaMachine Scherm;
            Scherm = new ColaMachine();
            Application.Run(Scherm);
        }
    }
}

此外,如果您对我的一般编程有任何建议(例如使代码更易于阅读),请告诉我!

在您的情况下,这并不是真正重要的:处理货币金额时,您应该使用decimal类型而不是double。这将防止舍入误差,因为十进制类型是为精确表示十进制数而开发的。 - ChrisWue
3个回答

7
当我想到自动售货机时,我看到的是机器上每种饮料都有一个按钮,但没有不同面额的按钮。也许你是指可乐的价格是50美分,所以按下可乐按钮后需要收取50美分。
按下屏幕上的按钮会生成一个“点击事件”。您需要编写一种方法来响应该点击事件。通常用于响应事件的任何方法都称为“事件处理程序”。您必须告诉您的程序哪些按钮与哪些事件处理程序相关联。我们将此称为“注册事件处理程序”。
按照惯例,如果您的按钮名为'CokeButton',则与该特定按钮关联的事件处理程序将被命名为'CokeButton_ClickHandler'。或者类似于这样的名称。
一般建议是,考虑您正在建模的事物,并定义代码以反映现实世界中的情况。您模型中的“事物”通常最终将成为类、类属性和类字段。这些事物的功能通常会成为适当类中的方法。然后您思考这些事物如何相互作用。
在开始编写代码之前,您无需弄清自动售货机的所有细节。您应该一点一点地编写代码,测试它们,然后建立在已测试内容的基础上。不要编写大量复杂的交互式代码,然后再进行测试。这样会让您陷入无休止的循环中。稍微编写一点,测试一下,然后重复。现在听我说并相信我;稍微编写一点,测试一下,然后重复。永远要遵守这个建议。
所以这就是我可能会如何考虑自动售货机的方式。首先有一个自动售货机本身。
public class CokeMachine {}

一个可乐机有一个投币口、一个退币口和喝料按钮。我不能真的把钱放进去,所以我想我会在一个文本框中输入。然后我会点击一个按钮,可乐就会出来。我觉得我已经定义了足够的模型来开始了。一个可乐机有很多其他的东西,但现在我不会担心它们。
但是,我需要知道每种饮料的价格。
好吧,那么必须有“CokeCost”、“7UpCost”等字段。因此,请定义它们!我们将随着进展弄清楚如何使用它们以及在哪里使用它们。
   public class CokeMachine {
     Button Coke;
     Button 7Up;
     Button RootBeer;
     TextBox MoneySlot;

     double CokeCost = .75;
     double 7UpCost = .65;
}

我说按钮需要处理程序,所以我们至少可以编写一些代码模板。我预计它们都将以相同的方式工作,所以现在我会重点关注其中一个。请注意,当我编写代码时,我会意识到必须处理其他事情。我会添加注释、调用尚不存在的方法等。

   public class CokeMachine {
     Button Coke;
     Button 7Up;
     Button RootBeer;
     TextBox MoneySlot;

     double CokeCost = .75;
     double 7UpCost = .65;

     // "wiring up" the coke button click event to it's handler.
     // We do this in C# by declaring an new EventHandler object (a .NET framework supplied class)
     // and we pass in the name of our method as a parameter.
     // This new EventHandler is *added* to the button's click event.
     // An event can have multiple handlers, that's why we do "+="
     // instead of just "=". Otherwise we would have accidentally "unhooked" any
     // previously registered handlers.
     Coke.Click += new EventHandler(Coke_ClickHandler);

     // this is the .NET event handler method signature.
     Public void Coke_ClickHandler (object sender, EventArgs args){
          if (MoneySlot.Value >= CokeCost) {
             DispenseDrink();
             // How do I handle returning change? Maybe DispenseDrink() can do that.
          }else {
             // tell customer to put in more money
          }
     }

     private void DispenseDrink() {
       // An empty method is enough to get it to compile so for now that's fine.
       // I need to test the Coke_EventHandler logic that I've written so far.
     }

  }

现在我需要测试我到目前为止所写的内容。之后我需要决定下一步要关注什么。但请注意,当您编写依赖于已编写代码的新代码时,如果该现有代码尚未经过测试 - 现在您看到错误,您刚刚使自己更加困难。您可以在代码更简单的时候进行测试。现在有更多内容,更复杂,并且将更难以调试和修复。
建议,第二部分
冒着搞砸的风险,我提供了对我的原始答案的补充:
您可以看到每个饮料按钮都执行相同的操作,并且根据上述代码,我们将为每个按钮编写相同的逻辑。如果需要更改任何内容,我们必须在所有位置进行更改。
更一般的建议
面向对象编程的一个启发式是“封装不变的事物”。您应该始终注意可能是公共代码的地方。
我想强调的是,这种常见的按钮行为对我来说并不是显而易见的。只有在我编写了上述代码之后,我才开始思考我的所有饮料按钮处理程序将开始看起来相同,并且我意识到在真正的饮料机上它们实际上表现相同。我的编码感觉告诉它,当代码反映您的真实物品的可识别行为时(双关语打算!),这绝对是一件好事。
重构
实际上是一个技术术语,意思是重新设计现有代码,使其更加灵活,可重用,易读等。简而言之,是可维护的。
重构应该始终在您的思考过程中。但是,请确保您有正当理由进行任何更改。重塑代码是软件开发的正常、不可分割的部分。
让我们通过“提取方法”来“重构”。
     Public void Coke_ClickHandler (object sender, EventArgs args){
          PurchaseDrink("Coke", CokeCost);
     }

     // now we have a method that stands out and says THIS is how it works
     // and a single point of change, rather than ump-teen button handlers.
      private PurchaseDrink (string whatKind, double cost) {

         // all I did so far is move the code and change "Cokecost" to "cost"
         // Now I'm beginning to think I may need to pass "whatKind" to
         // DispenseDrink() - but first I need to test the changes I've
         // made at this level.
         // ***** and since I already tested the code when I 1st wrote it,
         // this refactoring will be easier & quicker to test.. GET IT??!! ******

         if (MoneySlot.Value >= cost) {
             DispenseDrink();
             // How do I handle returning change? Maybe DispenseDrink() can do that.
          }else {
             // tell customer to put in more money
          }
     }

     private void DispenseDrink() {
       // An empty method is enough to get it to compile so for now that's fine.
       // I need to test the Coke_EventHandler logic that I've written so far.
     }

枚举

我不喜欢像上面使用"Coke"那样使用字符串。打字错误和大小写(大写/小写)可能会导致问题,而Visual Studio无法捕获。当我有一个有限的事物列表 - 饮料种类 - 我真的很喜欢使用枚举。它们会显示在智能感知中,我可以在switch语句中使用它们(并研究“类型安全”的概念)。而我真正喜欢的是,它们绝对定义了我们程序所知道的所有饮料类型的一处地方。就像文档一样!


MoneyButtons代表你插入机器的硬币。非常感谢,非常有帮助。我可能会完全重写它。我和我的狭隘视野... - BBB

3

您可以将每个按钮的数量存储在i按钮标签属性中,并在事件处理程序中使用以下代码读取数量:

void ValueButton_Click(object sender, EventArgs e)
{
  Button button = sender as Button;
  if (button == null) return;
  if (button.Tag == null) return;
  double amount = (double)button.Tag;

  // Process the amount here....
  InsertedCoins += amount; 
}

谢谢!我还不是完全理解,但我会努力学习。 - BBB

0

首先考虑: 你应该将问题分为两个类(Test类和ColaMachine类)。

看起来像是:

public class ColaMachine : Form
{
  public ColaMachine()
  {  
     ...
  }
}

public class Test 
{
   private static void Main()
   {
      ColaMachine Scherm;
      Scherm = new ColaMachine();
      Application.Run(Scherm);
   }
}

下一个问题:如果你想要返回一个私有变量,使用属性。IC将是一个公共方法(属性)。InsertedCoins将是一个私有变量。
public double IC
{
   get
   {
      return InsertedCoins;
    }
   set
   {
      InsertedCoins = value;
   }
}

别忘了,机器有很多状态。你应该使用设计模式,确切地说是状态模式


谢谢!我还有很多基础编程规则需要学习。 - BBB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接