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