目录
总述
事件在某种意义上可以理解为控件交互或控件与窗体的交互,可以说成是不需要编写代码来调用的方法。这样分类可能并不严格,解释也不一定正确,但比较容易理解。按照这个思路,首先来看一下控件交互型事件。
控件交互型事件
控件间的交互很容易理解,打个比方:点击某个按钮,实现了对某个文本框内的数据的计算并返回给另一个文本框。在这里,点击按钮这个事件出发的即是后续一系列内容。
具体应该如何实现,就以单击按钮实现读取计算写入为例来说明。
类与属于类的方法
首先,应有实现此计算所使用的系列方法,在此不建议直接将方法写入事件内,而是单独建立一个类,实现方法的调用。例如,要实现角度计算(Angel Calculation),可以先创建一个角度计算类:
using System;
namespace AngCal
{
/// <summary>
/// 角度相关
/// </summary>
public class AngCal
{
/// <summary>
/// 构造方法
/// </summary>
public AngCal()
{
}
}
}
当然,也可以加入一些字段封装成属性,比如
using System;
namespace AngCal
{
/// <summary>
/// 角度相关
/// </summary>
public class AngCal
{
private double degree;
private double minute;
private double second;
private double rad;
public double Degree { get => degree; set => degree = value; }
public double Minute { get => minute; set => minute = value; }
public double Second { get => second; set => second = value; }
public double Rad { get => rad; set => rad = value; }
/// <summary>
/// 构造方法
/// </summary>
public AngCal()
{
}
}
}
属性与全局变量有一定类似之处,但又不完全相同,此处不再赘述。
类中的方法,根据是否有static关键字修饰,可分为静态与非静态两种。静态方法要以类调用,非静态方法则是以对象调用。下面举例说明:
/// <summary>
/// 非静态:角度转弧度
/// 输入格式dd(度).mm(分)ssss(秒)
/// </summary>
public double Ang2Rad(double Degrees)
{
double Degree = Math.Truncate(Degrees);
double Minute = Math.Truncate((Degrees - Degree) * 100);
double Second = ((Degrees - Degree) * 100 - Minute) * 100;
double Rad = (Degree + Minute / 60 + Second / 3600) / 180 * Math.PI;
return Rad;
}
/// <summary>
/// 静态:角度转弧度
/// 输入格式dd(度).mm(分)ssss(秒)
/// </summary>
static public double Ang2Rad(double Degrees)
{
double Degree = Math.Truncate(Degrees);
double Minute = Math.Truncate((Degrees - Degree) * 100);
double Second = ((Degrees - Degree) * 100 - Minute) * 100;
double Rad = (Degree + Minute / 60 + Second / 3600) / 180 * Math.PI;
return Rad;
}
注意这里采用的是没有添加任何字段和属性的类。两者调用时区别如下:
// 静态方法调用举例
double Angel = 30.1124;
double Rad = AngCal.AngCal.Ang2Rad(Angel);
// 非静态方法调用举例
double Angel = 30.1124;
AngCal.AngCal angcal = new AngCal.AngCal();
double Rad = angcal.Ang2Rad(Angel);
注意此处如果对namespace AngCal进行using,则可简化为
using AngCal;
......
// 静态方法调用举例
double Angel = 30.1124;
double Rad = AngCal.Ang2Rad(Angel);
// 非静态方法调用举例
double Angel = 30.1124;
AngCal angcal = new AngCal();
double Rad = angcal.Ang2Rad(Angel);
方法在事件中调用
之后可在工具箱拖动一个按钮控件进入窗体,对按钮属性进行调整,一切就绪后,双击Design界面的按钮,即可添加单击事件。假设此时需要将name为Degree的文本框内的内容计算弧度并返回给name为Rad的文本框,则可如此处理:
private void 计算_Click(object sender, EventArgs e)
{
AngCal angcal = new AngCal();
string degree = Degree.Text;
Rad.Text = Convert.ToString(Math.Round(angcal.Ang2Rad(Convert.ToDouble(degree)), 5));
}
如此即可实现以上所述功能。由此可以看出:
1.事件确实作为一种方法出现在窗体类中,但不需要程序员编写代码调用,系统已经设定了调用的方式;
2.如果调用不属于同一个命名空间的类中的方法,需要在类前缀以命名空间或对对应的命名空间进行using,如果方法所属类不仅命名空间与窗体类不同,也不属于窗体类所属的解决方案,则还需要添加引用;
3.调用类时需要注意返回值类型,灵活的进行值类型转换。在调试时建议使用Parse或ToType方法,因为TryParse可能会产生误导。
控件窗体交互型事件
此处仍然举例说明。例如要通过单击按钮隐藏窗体或关闭程序:
// 隐藏窗体
private void 隐藏_Click(object sender, EventArgs e)
{
Form1.Hide();
}
// 退出程序
private void 退出_Click(object sender, EventArgs e)
{
Application.Exit();
}
一般控件和窗体交互时很少使用其他类的方法。
方法中值的返回
对于有返回值的方法,其返回值类型即是方法前类型关键字对应的类型。例如:
/// <summary>
/// 弧度转角度
/// </summary>
public double Rad2Ang(double Rad)
{
double dd = Rad / Math.PI * 180;
double Degree = Math.Truncate(dd);
double Minute = Math.Truncate((dd - Degree) * 60);
double Second = Math.Round(((dd - Degree) * 60 - Minute) * 60, 10);//返回10位小数
double Degrees = Degree + Minute / 100 + Second / 10000;
return Degrees;
}
/// <summary>
/// dd.mmssss转换为°'"格式
/// </summary>
public string Ang2Str(double Degrees)
{
string symbol = "";
if (Degrees < 0)
{
Degrees = Math.Abs(Degrees);
symbol = "-";
}
double Degree = Math.Truncate(Degrees);
double Minute = Math.Truncate((Degrees - Degree) * 100);
double Second = ((Degrees - Degree) * 100 - Minute) * 100;
string AngStr = symbol + Degree.ToString() + "°" + Minute.ToString() + "′" +
Second.ToString() + "″";
return AngStr;
}
Rad2Ang方法返回的数值是dd.mmss格式的度分秒,可以以double,也可以以string类型返回。但是Ang2Str方法返回的是含有特殊符号的度分秒,只能以string类型返回,在调用两种方法时,要时刻注意输入参数的类型以及返回值的类型。
如果是void方法,或者需要返回多个参数值的有返回值的方法,则可以通过ref关键字来返回值。
ref并不是一种值类型,它对应参数的值类型与调用时传入的参数的值类型相同。这里以导线网近似平差为例说明:
/// <summary>
/// 导线近似平差计算,当返回值为1时,说明所给数据超限
/// <param name="a">导线类型(闭合或附合)</param>
/// <param name="b">选择观测左角或观测右角</param>
/// <param name="XY">点坐标(包括已知点及待求点)</param>
/// <param name="angle">观测角度</param>
/// <param name="distance">观测距离</param>
public int TraverseNetworkAdjust(int a, int b, ref double[,] XY, double[] angle, double[] distance)
{
if (a == 2) //闭合路线
{
AngCal.AngCal angcal = new AngCal.AngCal();
double Az = CoCal.CoCal.GetAzi(XY[0, 0], XY[0, 1], XY[1, 0], XY[1, 1]); //计算初始坐标方位角
int n1 = XY.GetLength(0); //总点数
int n2 = angle.Length; //观测角度数
double sum = 0;
// 求角度和
for (int i = 0; i < n2; i++)
{
angle[i] = angcal.Ang2Dec(angle[i]);
sum += angle[i];
}
double closure = Math.Floor(sum);
closure = sum - closure; ;
// 检查角度闭合差
if (closure < ((40 * Math.Sqrt(n1 - 1)) / 3600))
{
// 分配角度闭合差
if (closure < 0.5)
{
double dangle = -1 * closure / (n1 - 1);
for (int i = 0; i < n2; i++)
{
angle[i] = angle[i] + dangle;
}
}
else
{
double dangle = closure / (n1 - 1);
for (int i = 0; i < n2; i++)
{
angle[i] = angle[i] + dangle;
}
}
// 计算方位角
double[] az = new double[n2];
az[0] = Az * 180 / Math.PI;
if (b == 1) //左角
{
for (int i = 1; i < n2; i++)
{
az[i] = az[i - 1] + angle[i - 1] - 180;
if (az[i] < 0) { az[i] = az[i] + 360; }
if (az[i] > 360) { az[i] = az[i] - 360; }
}
}
else //右角
{
for (int i = 1; i < n2; i++)
{
az[i] = az[i - 1] - angle[i - 1] + 180;
if (az[i] < 0) { az[i] = az[i] + 360; }
if (az[i] > 360) { az[i] = az[i] - 360; }
}
}
// 计算坐标增量
double[] dx = new double[n2 - 1];
double[] dy = new double[n2 - 1];
double flag1 = 0;
double flag2 = 0;
double s = 0;
double K = 5000;
for (int i = 0; i < (n2 - 1); i++)
{
az[i + 1] = az[i + 1] * Math.PI / 180;
dx[i] = distance[i] * Math.Cos(az[i + 1]);
dy[i] = distance[i] * Math.Sin(az[i + 1]);
flag1 += dx[i];
flag2 += dy[i];
s += distance[i];
}
az[n2 - 1] = az[n2 - 1] * Math.PI / 180;
// 检查坐标闭合差
if (s / Math.Sqrt(flag1 * flag1 + flag2 * flag2) > K)
{
double dxx = -1.0 * flag1 / (n2 - 1);
double dyy = -1.0 * flag2 / (n2 - 1);
for (int i = 0; i < (n2 - 1); i++)
{
dx[i] = dx[i] + dxx;
dy[i] = dy[i] + dyy;
}
// 计算坐标xy
for (int i = 2; i < n1; i++)
{
XY[i, 0] = XY[i - 1, 0] + dx[i - 2];
XY[i, 1] = XY[i - 1, 1] + dy[i - 2];
}
return 0;
}
else
{
return 1;//即超限
}
}
else
{
return 1;//即超限
}
}
else
{
AngCal.AngCal angcal = new AngCal.AngCal();
double Az1 = CoCal.CoCal.GetAzi(XY[0, 0], XY[0, 1], XY[1, 0], XY[1, 1]); //起始端初始坐标方位角
double Az2 = CoCal.CoCal.GetAzi(XY[2, 0], XY[2, 1], XY[3, 0], XY[3, 1]); //结束端初始坐标方位角
Az1 = Az1 * 180 / Math.PI;
Az2 = Az2 * 180 / Math.PI;
int n1 = XY.GetLength(0); //总点数
int n2 = angle.Length; //观测角度数
double sum = 0;
// 计算观测角度总和
for (int i = 0; i < n2; i++)
{
angle[i] = angcal.Ang2Dec(angle[i]);
sum += angle[i];
}
// 计算闭合差
double closure = Az1 - sum + n2 * 180 - Az2;
// 检查角度闭合差
if (closure < ((40 * Math.Sqrt(n1 - 1)) / 3600))
{
//分配角度闭合差
if (closure < 0.5)
{
double dangle = -1 * closure / (n1 - 1);
for (int i = 0; i < n2; i++)
{
angle[i] = angle[i] + dangle;
}
}
else
{
double dangle = closure / (n1 - 1);
for (int i = 0; i < n2; i++)
{
angle[i] = angle[i] + dangle;
}
}
// 计算方位角
double[] az = new double[n2 + 1];
az[0] = Az1;
if (b == 1) //左角
{
for (int i = 1; i < n2 + 1; i++)
{
az[i] = az[i - 1] + angle[i - 1] - 180;
if (az[i] < 0) { az[i] = az[i] + 360; }
if (az[i] > 360) { az[i] = az[i] - 360; }
}
}
else //右角
{
for (int i = 1; i < n2 + 1; i++)
{
az[i] = az[i - 1] - angle[i - 1] + 180;
if (az[i] < 0) { az[i] = az[i] + 360; }
if (az[i] > 360) { az[i] = az[i] - 360; }
}
}
//计算坐标增量
double[] dx = new double[n2 - 1];
double[] dy = new double[n2 - 1];
double flag1 = 0;
double flag2 = 0;
double s = 0;
double K = 5000;
for (int i = 0; i < (n2 - 1); i++)
{
az[i + 1] = az[i + 1] * Math.PI / 180;
dx[i] = distance[i] * Math.Cos(az[i + 1]);
dy[i] = distance[i] * Math.Sin(az[i + 1]);
flag1 += dx[i];
flag2 += dy[i];
s += distance[i];
}
az[n2 - 1] = az[n2 - 1] * Math.PI / 180;
az[n2] = az[n2] * Math.PI / 180;
flag1 = flag1 - (XY[2, 0] - XY[1, 0]); //dx
flag2 = flag2 - (XY[2, 1] - XY[1, 1]); //dy
//检查坐标闭合差
if (s / Math.Sqrt(flag1 * flag1 + flag2 * flag2) > K)
{
double dxx = -1.0 * flag1 / (n2 - 1);
double dyy = -1.0 * flag2 / (n2 - 1);
for (int i = 0; i < (n2 - 1); i++)
{
dx[i] = dx[i] + dxx;
dy[i] = dy[i] + dyy;
}
// 计算坐标xy
for (int i = 4; i < n1; i++)
{
XY[i, 0] = XY[i - 1, 0] + dx[i - 4];
XY[i, 1] = XY[i - 1, 1] + dy[i - 4];
}
return 0;
}
else
{
return 1;
}
}
else
{
return 1;
}
}
}
}
在调用时,ref的值从外部获取,并在方法代码段运行完毕后将值返回给对应的参数:
private void 平差计算_Click(object sender, EventArgs e)
{
double[,] XY = new double[x + 2, 2];
int q = tr.TraverseNetworkAdjust(2, b, ref XY, angle, distance);
}
这样既得到了返回值q,也得到了返回值XY。
注意此处仅仅是举例说明,实际调用是应关注实际问题的需求。
本期到此为止。