一. 实验要求
1、参照Windows提供的计算器工具,设计一个与之类似的程序;另外可参考老师给出的计算器Demo程序。
2、基本要求:实现“普通运算”功能,支持连续运算,如连续输入“2+3×5”后点击“等号”按钮进行运算其结果为25。
3、附加要求:
A. 支持“科学运算”功能,即输入表达式时遵循运算符号的优先原则,连续输入“2+3×5”后点击“等号”按钮进行运算其结果为17。
B. 支持数字键盘(小键盘)输入功能(焦点不在文本框内时也支持键盘输入)。
二. 设计思路
1、首先,设计出计算器的主要界面和主要按钮。为了达到美观的效果,本次实验重写了Button类,将按钮的轮廓由方形改为圆形,并加进了阴影效果。
核心代码:
publicclass myButtonObject : UserControl
{
protectedoverride void OnPaint(PaintEventArgs e)
{
Graphicsgraphics = e.Graphics;
Pen myPen = new Pen(Color.Transparent);//将轮廓的颜色设为透明
graphics.DrawEllipse(myPen, 0,0,this.Width,this.Height);
//轮廓的高度和宽度设为对象的高度和宽度
myPen.Dispose();
}
}
2、接着,在解决方案中,按照要求增加“ClassLibrary”文件夹,并在内新增类CalculateClass,里面包含了两个方法:计算普通运算的SimpleCalc和科学计算ComplexCalc。实际上,这个方法运算过程都是类似的。接受来自主程序的计算式后,科学计算拆分数字和字符,将数字和符号加入到栈中,而简单计算拆分后,就直接放入到队列中。科学计算计算前要判断符号的优先级,而简单计算只需根据队列的出队顺序进行计算即可。
核心代码:
publicdouble ComplexCalc(string stringBuilder)//科学计算的方法
{
inti = 0, j = 0;
Stack<double>numberStack = new Stack<double>();//存储数字的栈
Stack<char>oprStack = new Stack<char>();//存储运算符号的栈
for(; i < stringBuilder.Length; i++)//遍历计算式
{
charc = stringBuilder[i];
if (c >= '0' && c <= '9')
{
j = 10 * j + Convert.ToInt32(c) - 48;
}
else//碰到符号,先将前一个数字压入计算数字栈中
{
numberStack.Push(j);
j= 0;
if(oprStack.Count > 0)
{
charc0 = oprStack.Pop();
//判断栈的符号是否为乘、除号,如果是的话,则先计算
if(c0 == '*')
{
double jj = numberStack.Pop() * numberStack.Pop();
numberStack.Push(jj);
}
else if (c0 == '/')
{
double jj = numberStack.Pop();
jj = numberStack.Pop() /jj;
numberStack.Push(jj);
}
else
{
oprStack.Push(c0);
}
}
oprStack.Push(c);//将当前符号压入栈中
}
}
numberStack.Push(j);//将最后一个数字压入栈中。
if(oprStack.Count > 0)//判断最后一个运算符是否为乘除号,如果是则先计算
{
charc3 = oprStack.Peek();
if(c3 == '*' || c3 == '/')
{
doublei0 = numberStack.Pop();
doublei2 = numberStack.Pop();
c3= oprStack.Pop();
if(c3 == '*')
{
numberStack.Push(i0* i2);
}
else
{
numberStack.Push(i2/ i0);
}
}
}
//将栈化成列表,方便正向计算剩下的加减法
List<double>numberList = numberStack.ToList<double>();
List<char>oprList = oprStack.ToList<char>();
doubleresult = numberList[oprStack.Count];
for(int i0 = oprStack.Count - 1; i0 >= 0; i0--)
{//与压栈方向相反,正向计算剩下的加减法
switch(oprList[i0])
{
case'+':
result+= numberList[i0];
break;
case '-':
result-= numberList[i0];
break;
default:
return -1;//如果符号列表中还存在乘除号则返回错误。
}
}
returnresult;
}
publicdouble SimpleCalc(string calcString)//普通计算,从左往右计算
{
Queue<char>oprQueue = new Queue<char>();//符号队列
Queue<double>numberQueue = new Queue<double>();//数字队列
inti = 0, j = 0;
charc;
for(; i < calcString.Length; i++)//遍历计算式分别将数字和符号加入队列中
{
c = calcString[i];
if (c >= '0' && c <= '9')
{
j = 10 * j + Convert.ToInt32(c) - 48;
}
else
{
numberQueue.Enqueue(j);
j = 0;
oprQueue.Enqueue(c);
}
}
numberQueue.Enqueue(j);
doubleresult = numberQueue.Dequeue();
while(oprQueue.Count > 0)//计算得数
{
switch(oprQueue.Dequeue())
{
case'+':
result+= numberQueue.Dequeue();
break;
case'-':
result-= numberQueue.Dequeue();
break;
case '*':
result *=numberQueue.Dequeue();
break;
case '/':
result /=numberQueue.Dequeue();
break;
default:
return -1;//遇到其他符号则返回错误
break;
}
}
returnresult;//返回正确结果
}
3、根据题目要求,实现在键盘上按下相关键或鼠标点击按钮时会出现相关数字或运算符号。
核心代码:
//判断鼠标点击的键
privatevoid Keys_Click(object sender, EventArgs e)
{
myButtonObject myButton =(myButtonObject)sender;
string opr;
switch (myButton.Name.ToString())
{
case "KeyAdd":
opr = "+";
break;
case "KeyMinus":
opr = "-";
break;
case "KeyMutiply":
opr = "*";
break;
case "KeyDivision":
opr = "/";
break;
default:
opr =myButton.Name.Substring(3);
break;
}
AddToList(opr);
}
//将判断键盘输入的键
privatevoid Calc_KeyPress(object sender, KeyPressEventArgs e)
{
if(e.KeyChar >= '0' && e.KeyChar <= '9')//数字键
{
AddToList(e.KeyChar.ToString());
}
elseif (e.KeyChar == '+' || e.KeyChar == '-' || e.KeyChar == '*' || e.KeyChar =='/')//符号键
{
AddToList(e.KeyChar.ToString());
}
elseif (e.KeyChar == (char)Keys.Back)//退格键,关联退格功能
{
BackSpaceButton_Click(sender,e);
}
else if (e.KeyChar == (char)Keys.Enter)//关联等于效果
{
KeyEqual_Click(sender,e);
}
else if(e.KeyChar == (char)Keys.Escape)//关联清空功能
{
ClearButton_Click(sender,e);
}
}
4、由于本次界面,采用无边框的界面。为了让窗体能够移动,为窗体增加了3个事件。
核心代码:
privatevoid Calc_MouseDown(object sender, MouseEventArgs e)
{
if(e.Button == MouseButtons.Left)
{
mouseOff= new Point(-e.X, -e.Y);
leftFlag= true;
}
}//如果用鼠标的左键点击了界面,则做好移动标记
privatevoid Calc_MouseMove(object sender, MouseEventArgs e)
{
if(leftFlag)
{
PointmouseSet = Control.MousePosition;
mouseSet.Offset(mouseOff.X,mouseOff.Y);
Location= mouseSet;
}
}//将整个窗体移动到鼠标移动到的位置
privatevoid Calc_MouseUp(object sender, MouseEventArgs e)
{
if(leftFlag)
{
leftFlag= false;
}
}//将取消移动的标记
三. 程序运行效果图
1、主界面,默认是简单计算模式,见图1。
2、简单模式下计算2+2×5,见图2。
3、科学计算模式下计算2+3×5,见图3。
四. 实验总结
这是我第一次设计Windows界面风格的程序。通过实验,我终于发现,原来界面的设计原来是这么难的。设计这个程序,中间有太多的折中和不情愿,但是最后还是由于技术的原因,不得不放弃。相对于Web的界面设计,Windows风格窗体的设计,的确难很多,还有很多需要学习的地方。接下来学习C#的时间,我会着重于学习这些地方,力求设计出一个“摆得上台面”的界面。
另外一方面,通过这次的实验,也让我重温了大二时学习的数据结构队列和栈的知识,还好,数据结构的知识没有忘掉太多。