C# 简易计算器(二)

C# 简易计算器(一)前面展示了一下用C#winform做的简单的计算器,也是课程的小作业,现在分析一下代码是怎么实现的。
首先窗体的加载将一些事件绑定到对应的函数,以及一些初始化

private void Mainfrm_Load(object sender, EventArgs e)
        {

            this.KeyPreview = true;//获取或设置一个值,该值指示在将键事件传递到具有焦点的控件前,窗体是否将接收此键事件
            btnNum0.BackgroundImage = new Bitmap(Resource1._0);
            btnNum1.BackgroundImage = new Bitmap(Resource1._1);
            btnNum2.BackgroundImage = new Bitmap(Resource1._2);
            btnNum3.BackgroundImage = new Bitmap(Resource1._3);
            btnNum4.BackgroundImage = new Bitmap(Resource1._4);
            btnNum5.BackgroundImage = new Bitmap(Resource1._5);
            btnNum6.BackgroundImage = new Bitmap(Resource1._6);
            btnNum7.BackgroundImage = new Bitmap(Resource1._7);
            btnNum8.BackgroundImage = new Bitmap(Resource1._8);
            btnNum9.BackgroundImage = new Bitmap(Resource1._9);
            btnAdd.BackgroundImage = new Bitmap(Resource1.add);
            btnDecrease.BackgroundImage = new Bitmap(Resource1.decrease);
            btnMul.BackgroundImage = new Bitmap(Resource1.mul);
            btnDiv.BackgroundImage = new Bitmap(Resource1.div);
            btnClear.BackgroundImage = new Bitmap(Resource1.clear);
            btnEqual.BackgroundImage = new Bitmap(Resource1.equal);
            btnDel.BackgroundImage = new Bitmap(Resource1.del);
            btnSquareRoot.BackgroundImage = new Bitmap(Resource1.squareRoot);
            btnPoint.BackgroundImage = new Bitmap(Resource1.point);
            btnPosAndNeg.BackgroundImage = new Bitmap(Resource1.posAndNeg);
            this.btnNum0.Click += new System.EventHandler(this.btnNum_Click);
            this.btnNum1.Click += new System.EventHandler(this.btnNum_Click);
            this.btnNum2.Click += new System.EventHandler(this.btnNum_Click);
            this.btnNum3.Click += new System.EventHandler(this.btnNum_Click);
            this.btnNum4.Click += new System.EventHandler(this.btnNum_Click);
            this.btnNum5.Click += new System.EventHandler(this.btnNum_Click);
            this.btnNum6.Click += new System.EventHandler(this.btnNum_Click);
            this.btnNum7.Click += new System.EventHandler(this.btnNum_Click);
            this.btnNum8.Click += new System.EventHandler(this.btnNum_Click);
            this.btnNum9.Click += new System.EventHandler(this.btnNum_Click);
            setBtnEnableMethon(false);//防止一开始点击运算符按钮导致出错
        }

一、数字键
将所有数字键的事件都绑定到btnNum_Click,

this.btnNum0.Click += new System.EventHandler(this.btnNum_Click);
            this.btnNum1.Click += new System.EventHandler(this.btnNum_Click);
            this.btnNum2.Click += new System.EventHandler(this.btnNum_Click);
            this.btnNum3.Click += new System.EventHandler(this.btnNum_Click);
            this.btnNum4.Click += new System.EventHandler(this.btnNum_Click);
            this.btnNum5.Click += new System.EventHandler(this.btnNum_Click);
            this.btnNum6.Click += new System.EventHandler(this.btnNum_Click);
            this.btnNum7.Click += new System.EventHandler(this.btnNum_Click);
            this.btnNum8.Click += new System.EventHandler(this.btnNum_Click);
            this.btnNum9.Click += new System.EventHandler(this.btnNum_Click);

接下来点啥计算器屏幕上应该就加上啥,我是用一个字符串calshow来实现的,当点击数字键后,在calshow后面加上对应的数字

#region 数字按钮
        private void btnNum_Click(object sender, EventArgs e)
        {
            bool res = Regex.IsMatch(calShow, @"^0+$");//匹配全都是0的情况
            bool res1 = Regex.IsMatch(calShow, @"^(\-)?\d+(\.\d+)?(\+|\-|\*|\/)0+$");//匹配第二个操作数是否全都是0的情况
            if (res || res1)//满足其中一个则把前面的0删掉
            {
                btnDel.PerformClick();
            }
            if (equalStatus)//等于号按过之后点数字则直接清除原来的结果
            {
                equalStatus = false;
                calShow = "";
                resultDouble = 0.0;
                txtResual.Text = "";
            }
            Button btn = (Button)sender;
            calShow += btn.TabIndex.ToString();
            txtResual.Text = calShow;
            setBtnEnableMethon(true);
            btnEqual.Focus();
        }
        #endregion

上面用了两个正则表达式,分别是:匹配全都是0的情况、匹配第二个操作数是否全都是0的情况,这个的目的就是当你连续点击0的时候,如果首位开始的连0,则忽略掉,比如一开就一直点击0,那么屏幕上应该只有一个0,而不是000000…
接下来就是如果点过了等于号,接下来再点击数字时直接清除原来的结果,最后再把相对应的数字添加到后面。

二、加减乘除以及等于号
这里我将四则运算的操作和等于号直接归为一类,实际上也是可以这么做的,当你连加的时候其实前面的两个数的相加其实也就是等于号的作用。
还有一个就是除以零的错误检测,当除零时会抛出异常。

#region 运算符处理函数
        private void multiDeal()
        {
            try
            {
                //将前面的表达式的值计算出来
                Regex reg = new Regex(@"(\+|\-)?(\d+)(\.\d+)?(\+|\-|\*|\/)(\+|\-)?(\d+)(\.\d+)?");
                var res = reg.Match(calShow).Groups;
                //res[0]    整个匹配的表达式
                //res[1]    第一个操作数的符号
                //res[2]    第一个操作数的整数部分
                //res[3]    第一个操作数的小数部分
                //res[4]    操作符
                //res[5]    第二个操作数的符号
                //res[6]    第二个操作数的整数部分
                //res[7]    第而个操作数的小数部分
                switch (res[4].ToString())
                {
                    case "+":
                        resultDouble = Convert.ToDouble(res[1].ToString() + res[2].ToString() + res[3].ToString())
                                       + Convert.ToDouble(res[5].ToString() + res[6].ToString() + res[7].ToString());
                        calShow = resultDouble.ToString();
                        break;
                    case "-":
                        resultDouble = Convert.ToDouble(res[1].ToString() + res[2].ToString() + res[3].ToString())
                                       - Convert.ToDouble(res[5].ToString() + res[6].ToString() + res[7].ToString());
                        calShow = resultDouble.ToString();
                        break;
                    case "*":
                        resultDouble = Convert.ToDouble(res[1].ToString() + res[2].ToString() + res[3].ToString())
                                       * Convert.ToDouble(res[5].ToString() + res[6].ToString() + res[7].ToString());
                        calShow = resultDouble.ToString();
                        break;
                    case "/":
                        double secondNum = Convert.ToDouble(res[5].ToString() + res[6].ToString() + res[7].ToString());
                        if(secondNum == 0)
                            throw new Exception("除数不能为0!");
                        resultDouble = Convert.ToDouble(res[1].ToString() + res[2].ToString() + res[3].ToString())
                                       / secondNum;
                        calShow = resultDouble.ToString();
                        break;
                    default:
                        break;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "警告");
            }      
            txtResual.Text = calShow;
        }
        #endregion

这个函数是这五个键对应的处理函数,正则对应的是(+-)A+(+-)B这样两个数的运算,然后就对字符串进行操作,将两个操作数分离出来

三、小数点
这里有个问题就是小数点什么时候能加,什么情况不能加,比如我不能出现3.4.2这种情况吧,因此我在这里同样使用正则表达式区分这些,当正确输入时能将小数点加上去。

#region 小数点
        private void btnPoint_Click(object sender, EventArgs e)
        {
            if (equalStatus)//等于号按过之后点数字则直接清除原来的结果
            {
                equalStatus = false;
                calShow = "";
                resultDouble = 0.0;
                txtResual.Text = "";
            }
            try
            {
                bool res = Regex.IsMatch(calShow, @"^(\-)?\d+$");//匹配纯数字
                bool res1 = Regex.IsMatch(calShow, @"^(\-)?\d+(\.\d+)?(\+|\-|\*|\/)\d+$");//匹配第二个操作数是否为整数
                if (res || res1)//满足其中一个则可以添加小数点
                {
                    calShow += ".";
                }
            }
            catch (Exception)
            {
                MessageBox.Show("发生错误!", "警告");
            }
            txtResual.Text = calShow;
            setBtnEnableMethon(true);
        }
        #endregion

四、其他操作符
(1)开根号
使用的是Math的Sqrt方法,其中要注意的就是负数开根号的问题,同样这里将抛出异常。

        #region 开平方
        private void btnSquareRoot_Click(object sender, EventArgs e)
        {
            multiDeal();//先得出前面表达式的值
            try
            {
                Regex reg = new Regex(@"(\+|\-)?(\d+)(\.\d+)?");
                var res = reg.Match(calShow).Groups;

                if (res[1].ToString() == "-")
                {
                    throw new Exception("负数不能开根号!");
                }
                resultDouble = Math.Sqrt(Convert.ToDouble(calShow));
                calShow = resultDouble.ToString();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message,"警告");
            }
            txtResual.Text = calShow;
        }
        #endregion

(2)正负号
正负号的操作是先点数字再取正负,正数的时候字符串前面为空,负数的时候字符串前面为“-”。

#region 正负号
        private void btnPosAndNeg_Click(object sender, EventArgs e)
        {
            multiDeal();//先计算结果,再来加正负号
            try
            {
                Regex reg = new Regex(@"^(\-)?");
                var res = reg.Match(calShow).Groups;
                if(res[1].ToString() == "")//正数
                {
                    calShow = "-" + calShow;
                }
                else//负数
                {
                    calShow = calShow.Substring(1,calShow.Length-1);
                }   
            }
            catch (Exception)
            {
                MessageBox.Show("发生错误!","警告");
            }
            txtResual.Text = calShow;
        }
        #endregion

(3)退格键
就是将calshow最后面的字符删除即可

#region 退格键
        private void btnDel_Click(object sender, EventArgs e)
        {
            if(calShow.Length != 0)
            {
                calShow = calShow.Substring(0,calShow.Length - 1);//删除最后一个字符
            }
            txtResual.Text = calShow;
            setBtnEnableMethon(true);
        }

(4)清除键
即清除所有数据

        #region 清除键
        //清除所有
        private void btnClear_Click(object sender, EventArgs e)
        {
            calShow = "";
            resultDouble = 0;
            txtResual.Text = "";
        }
        #endregion

五、按键操作

#region 按键输入
        private void Mainfrm_KeyDown(object sender, KeyEventArgs e)
        {

            switch(e.KeyCode)
            {
                case Keys.D0://字母上面的数字键
                    btnNum0.PerformClick();
                    break;
                case Keys.NumPad0://键盘右边的数字键
                    btnNum0.PerformClick();
                    break;
                case Keys.D1:
                    btnNum1.PerformClick();
                    break;
                case Keys.NumPad1:
                    btnNum1.PerformClick();
                    break;
                case Keys.D2:
                    btnNum2.PerformClick();
                    break;
                case Keys.NumPad2:
                    btnNum2.PerformClick();
                    break;
                case Keys.D3:
                    btnNum3.PerformClick();
                    break;
                case Keys.NumPad3:
                    btnNum3.PerformClick();
                    break;
                case Keys.D4:
                    btnNum4.PerformClick();
                    break;
                case Keys.NumPad4:
                    btnNum4.PerformClick();
                    break;
                case Keys.D5:
                    btnNum5.PerformClick();
                    break;
                case Keys.NumPad5:
                    btnNum5.PerformClick();
                    break;
                case Keys.D6:
                    btnNum6.PerformClick();
                    break;
                case Keys.NumPad6:
                    btnNum6.PerformClick();
                    break;
                case Keys.D7:
                    btnNum7.PerformClick();
                    break;
                case Keys.NumPad7:
                    btnNum7.PerformClick();
                    break;
                case Keys.D8:
                    btnNum8.PerformClick();
                    break;
                case Keys.NumPad8:
                    btnNum8.PerformClick();
                    break;
                case Keys.D9:
                    btnNum9.PerformClick();
                    break;
                case Keys.NumPad9:
                    btnNum9.PerformClick();
                    break;
                case Keys.Enter:
                    btnEqual.PerformClick();
                    break;
                case Keys.Add:
                    btnAdd.PerformClick();
                    break;
                case Keys.Subtract:
                    btnDecrease.PerformClick();
                    break;
                case Keys.Multiply:
                    btnMul.PerformClick();
                    break;
                case Keys.Divide:
                    btnDiv.PerformClick();
                    break;
                case Keys.Back:
                    btnDel.PerformClick();
                    break;
                case Keys.C:
                    btnClear.PerformClick();
                    break;
                case Keys.S:
                    btnSquareRoot.PerformClick();
                    break;
                case Keys.Z:
                    btnPosAndNeg.PerformClick();
                    break;
                case Keys.Decimal:
                    btnPoint.PerformClick();
                    break;
                default:
                    break;
            }
        }
        #endregion

这里重要的一点是窗体加载的第一句

this.KeyPreview = true;//获取或设置一个值,该值指示在将键事件传递到具有焦点的控件前,窗体是否将接收此键事件

如果没有这句的话,当你按下按键是没有反应的,其次就是enter键焦点的问题,在数字键处理的最后一句

btnEqual.Focus();

这里每次操作都将焦点转移到enter键上面去

六、其他

#region textBox字体的大小自适应
        private void txtResual_TextChanged(object sender, EventArgs e)
        {
            if (calShow.Length < 10)
            {
                txtResual.Font = new Font(txtResual.Font.Name, 25);
            }
            else if (calShow.Length < 20)
            {
                txtResual.Font = new Font(txtResual.Font.Name, 15);
            }
            else if (calShow.Length < 30)
            {
                txtResual.Font = new Font(txtResual.Font.Name, 11);
            }
            else
            {
                txtResual.Font = new Font(txtResual.Font.Name, 8);
            }
        }
        #endregion

这个是比较人性化的设计,当数据很长的时候自适应显示出来,就不是出现当数据太长的时候一行没法显示完整。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值