好的!我给你写一份超详细、每一行都有注释 的完整代码,你直接覆盖原来的 Login_Form.cs 就行。
带超详细注释的 Login_Form.cs 完整代码
csharp
// ============================================================
// 引入命名空间(相当于"导入工具箱")
// 每一行都是引入一个功能模块,后面代码才能用里面的类和方法
// ============================================================
using System; // 基础功能(字符串、异常、时间等)
using System.Collections.Generic; // 集合(列表、字典等)
using System.ComponentModel; // 组件模型(控件相关)
using System.Drawing; // 绘图、颜色、字体
using System.Linq; // LINQ查询
using System.Text; // 字符串处理
using System.Windows.Forms; // WinForms窗体(按钮、文本框、窗口等)
using System.Data; // 数据相关(数据表、数据集)
using System.Data.OleDb; // Access数据库操作
using System.Data.Sql; // SQL Server相关
using System.Data.SqlClient; // SQL Server客户端
using System.Threading; // 多线程、延时(Sleep)
using System.Diagnostics; // 进程、命令行(启动cmd.exe)
using System.Net.NetworkInformation; // 网络相关(Ping命令)
using FibertopTest_Common; // 项目自己的公共类库(OTP12驱动、加热台驱动等)
// ============================================================
// 命名空间:SFP模块终测检查软件
// 这个项目的所有代码都在这个命名空间里
// ============================================================
namespace SFP模块终测检查软件
{
/// <summary>
/// 登录窗体类
/// 作用:程序启动后第一个显示的窗口,用户在这里设置参数、连接设备,然后进入主界面
///
/// 继承关系:Login_Form 继承自 Form(窗体基类)
/// partial 表示这个类的代码分成了几个文件,另一个文件是 Login_Form.Designer.cs(设计器自动生成的界面代码)
/// </summary>
public partial class Login_Form : Form
{
// ============================================================
// 成员变量(这个类的"全局变量",整个类里的函数都能用)
// ============================================================
// 本地数据库文件存放路径
// 原来用的是网络路径 X:\Fibertop\,现在改成本地路径
//string filePath = "X:\\Fibertop\\"; // 网络数据库镜像(旧的,注释掉了)
string filePath = "C:\\Fibertop\\"; // 本地数据库镜像(现在用这个)
// 模块类型对应的数据库文件名数组
// 数组大小是20,表示最多支持20种不同的光模块类型
// 每种模块类型对应一个Access数据库文件,里面存着该模块的测试参数
string[] moduleTypeDBName = new string[20];
// ============================================================
// 构造函数
// 作用:创建 Login_Form 对象的时候自动调用,初始化窗体
// ============================================================
public Login_Form()
{
// 初始化组件(就是界面上的所有控件:按钮、文本框、下拉框等)
// 这个方法是设计器自动生成的,在 Login_Form.Designer.cs 里
InitializeComponent();
}
// ============================================================
// 窗体加载事件
// 触发时机:窗体刚显示出来的时候自动执行
// 作用:做一些初始化工作,比如填默认值、创建对象等
// ============================================================
private void Login_Form_Load(object sender, EventArgs e)
{
// ============================================================
// 1. 设置 SQL 服务器下拉框默认选中第1项
// ============================================================
// SelectedIndex = 0 表示选中第1项(索引从0开始,0是第1个,1是第2个...)
sqlserver_comboBox.SelectedIndex = 0;
// ============================================================
// 2. 初始化两个设备对象(OTP12仪表 和 加热台)
// ============================================================
// GlobalVarFun 是全局变量类,在 Common.cs 里定义
// 这里创建 OTP12 驱动对象,存到全局变量里,后面整个程序都能用
GlobalVarFun.otp12 = new OTP12Driver();
// 创建加热台驱动对象,存到全局变量里
GlobalVarFun.heater = new SFP_EVB_Heater();
// ============================================================
// 3. 给两个IP文本框填上默认值
// ============================================================
// OTP12 的默认 IP 地址
textBox_otp12Ip.Text = "192.168.100.156";
// 加热台的默认 IP 地址(注意是129开头,不是192!)
textBox_heaterIp.Text = "129.168.1.133";
}
// ============================================================
// SQL服务器下拉框 选项改变事件
// 触发时机:用户切换下拉框选项的时候
// 作用:根据用户选的服务器IP,创建数据库连接对象
// ============================================================
private void sqlserver_comboBox_SelectedIndexChanged(object sender, EventArgs e)
{
// ============================================================
// 创建 SQL Server 数据库连接对象
// ============================================================
// 连接字符串格式:server=服务器地址;uid=用户名;pwd=密码;database=数据库名
// sqlserver_comboBox.Text 是用户选的服务器IP地址
// 用户名:tester
// 密码:fibertop2020
// 数据库名:SFP
//GlobalVarFun.sqlconnection = new SqlConnection("server=" + sqlserver_comboBox.Text + ";uid=sa;pwd=fiber123;database=SFP");
GlobalVarFun.sqlconnection = new SqlConnection(
"server=" + sqlserver_comboBox.Text +
";uid=tester;pwd=fibertop2020;database=SFP");
}
// ============================================================
// 测试 OTP12 连接 按钮点击事件
// 触发时机:用户点击"测试连接"按钮(OTP12旁边的那个)
// 作用:测试能不能连上 OTP12 设备
// ============================================================
private void button_testOtp12_Click(object sender, EventArgs e)
{
// ============================================================
// 第1步:从文本框获取IP地址,去掉前后的空格
// ============================================================
// .Trim() 去掉字符串前后的空格,防止用户不小心输了空格
string ip = textBox_otp12Ip.Text.Trim();
// ============================================================
// 第2步:检查IP地址是不是空的
// ============================================================
// 如果IP是空的(用户没输入),弹出提示,然后直接返回,不往下执行了
if (string.IsNullOrEmpty(ip))
{
// 弹出提示框
// 参数1:提示内容
// 参数2:标题
// 参数3:按钮类型(只有确定按钮)
// 参数4:图标(警告图标)
MessageBox.Show(
"请输入OTP12的IP地址!",
"提示",
MessageBoxButtons.OK,
MessageBoxIcon.Warning);
return; // 直接退出函数,后面的代码不执行了
}
// ============================================================
// 第3步:尝试连接 OTP12 设备
// ============================================================
try // try-catch:异常处理,防止程序崩溃
{
// 调用 OTP12 驱动的 Connect 方法连接设备
// 返回 true = 连接成功,返回 false = 连接失败
if (GlobalVarFun.otp12.Connect(ip))
{
// 连接成功,弹出成功提示
MessageBox.Show(
"OTP12连接成功!",
"测试结果",
MessageBoxButtons.OK,
MessageBoxIcon.Information); // 信息图标(蓝色i)
}
else
{
// 连接失败,弹出失败提示
MessageBox.Show(
"OTP12连接失败,请检查IP地址和网络!",
"测试结果",
MessageBoxButtons.OK,
MessageBoxIcon.Error); // 错误图标(红色叉)
}
}
catch (Exception ex) // 如果上面的代码抛出异常,就走到这里
{
// 弹出错误信息,ex.Message 是异常的具体描述
MessageBox.Show(
"OTP12连接出错:" + ex.Message,
"错误",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
// ============================================================
// 测试加热台连接 按钮点击事件
// 触发时机:用户点击"测试连接"按钮(加热台旁边的那个)
// 作用:测试能不能连上加热台设备
// 逻辑和上面 OTP12 的几乎一样,只是调用的方法不同
// ============================================================
private void button_testHeater_Click(object sender, EventArgs e)
{
// 第1步:获取IP地址
string ip = textBox_heaterIp.Text.Trim();
// 第2步:检查IP是否为空
if (string.IsNullOrEmpty(ip))
{
MessageBox.Show(
"请输入加热台的IP地址!",
"提示",
MessageBoxButtons.OK,
MessageBoxIcon.Warning);
return;
}
// 第3步:尝试连接加热台
try
{
// 加热台用的是 Open 方法,不是 Connect(和OTP12不一样)
if (GlobalVarFun.heater.Open(ip))
{
MessageBox.Show(
"加热台连接成功!",
"测试结果",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
else
{
MessageBox.Show(
"加热台连接失败,请检查IP地址和网络!",
"测试结果",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
catch (Exception ex)
{
MessageBox.Show(
"加热台连接出错:" + ex.Message,
"错误",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
// ============================================================
// 测试 SQL 数据库连接 按钮点击事件
// 触发时机:用户点击"测试连接"按钮(服务器IP旁边的那个)
// 作用:测试能不能连上 SQL Server 数据库
// ============================================================
private void testSQL_button_Click(object sender, EventArgs e)
{
// ============================================================
// 第1步:检查有没有选择服务器
// ============================================================
// 如果下拉框是空的,或者选的是"null",就提示用户
if (string.IsNullOrEmpty(sqlserver_comboBox.Text) ||
sqlserver_comboBox.Text == "null")
{
MessageBox.Show(
"请选择SQL数据库服务器!\r\n", // \r\n 是换行符
"Warning",
MessageBoxButtons.OK,
MessageBoxIcon.Warning);
return;
}
// ============================================================
// 第2步:先 Ping 一下服务器,看网络通不通
// ============================================================
// 调用下面写的 TestServerIPonline() 函数
// 返回 false = Ping不通,网络有问题
if (TestServerIPonline() == false)
{
MessageBox.Show(
"服务器IP地址ping不通,网络不畅通,请检查网络连接!\r\n",
"测试结果",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return; // Ping不通就直接返回,不往下试了
}
// ============================================================
// 第3步:尝试打开数据库连接
// ============================================================
try
{
// 打开数据库连接
GlobalVarFun.sqlconnection.Open();
// 能打开说明连接成功,马上关掉(只是测试一下)
GlobalVarFun.sqlconnection.Close();
MessageBox.Show(
"测试连接成功",
"测试结果",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
catch (Exception exception)
{
// 连接失败,弹出错误信息
MessageBox.Show(
"测试连接失败,请确认SQL数据库IP地址正确!!\r\n" + exception.Message,
"测试结果",
MessageBoxButtons.OK,
MessageBoxIcon.Warning);
}
}
// ============================================================
// 测试服务器IP是否通畅(Ping 命令)
// 这是一个辅助函数,被上面的 testSQL_button_Click 调用
// 返回值:true = Ping通了,false = Ping不通
// ============================================================
private bool TestServerIPonline()
{
try // 异常处理
{
// 创建 Ping 对象
Ping ping = new Ping();
// 发送 Ping 命令到服务器
// sqlserver_comboBox.Text 是服务器IP地址
// 返回 PingReply 对象,包含Ping的结果
PingReply pingReply = ping.Send(sqlserver_comboBox.Text);
// 释放 Ping 对象的资源
ping.Dispose();
// 检查 Ping 是否成功
// IPStatus.Success 表示成功
if (pingReply.Status != IPStatus.Success)
{
return false; // Ping失败,返回 false
}
}
catch // 如果出错(比如IP格式不对、网络断开等)
{
//catch (Exception exp) // 原来的代码注释掉了异常变量
return false; // 出错了也返回 false
}
//
return true; // Ping成功,返回 true
}
// ============================================================
// 从服务器复制 Access 数据库文件到本地
// 这是一个辅助函数,被下面的 update_button_Click 调用
// 返回值:true = 复制成功,false = 复制失败
//
// 原理:通过命令行(cmd.exe)执行 net use 和 xcopy 命令
// 把服务器共享文件夹里的Access数据库复制到本地
// ============================================================
private bool CopyShareDBFileToLocal()
{
// ============================================================
// 变量定义
// ============================================================
Process proc = new Process(); // 进程对象,用来启动 cmd.exe
string dosLine; // 存命令行内容
bool Flag = false; // 成功标志:true=成功,false=失败
try // 异常处理
{
// ============================================================
// 第1步:配置并启动 cmd.exe
// ============================================================
proc.StartInfo.FileName = "cmd.exe"; // 要启动的程序:命令行
proc.StartInfo.UseShellExecute = false; // 不使用系统shell启动
proc.StartInfo.RedirectStandardInput = true; // 重定向输入(可以往里面写命令)
proc.StartInfo.RedirectStandardOutput = true;// 重定向输出(可以读取输出)
proc.StartInfo.RedirectStandardError = true; // 重定向错误输出
proc.StartInfo.CreateNoWindow = true; // 不显示黑色的cmd窗口
proc.Start(); // 启动 cmd.exe
// ============================================================
// 第2步:连接服务器的共享文件夹
// ============================================================
// net use 命令:连接网络共享文件夹
// 格式:net use \\服务器IP\共享名 密码 /user:用户名
// 服务器IP:sqlserver_comboBox.Text
// 共享名:Fibertop
// 密码:test2016
// 用户名:fibertop
dosLine = @"net use \\" + sqlserver_comboBox.Text +
@"\Fibertop ""test2016"" /user:""fibertop""";
// 把命令写入 cmd 的标准输入(相当于在cmd窗口里输入命令然后回车)
proc.StandardInput.WriteLine(dosLine);
// 延时1300毫秒(1.3秒),等命令执行完
Thread.Sleep(1300);
// ============================================================
// 第3步:复制共享文件夹里的文件到本地 C:\
// ============================================================
// xcopy 命令:复制文件和文件夹
// 格式:xcopy 源路径 目标路径 /s/e/y
// /s:复制子目录(除了空的)
// /e:复制子目录(包括空的)
// /y:覆盖时不提示,直接覆盖
dosLine = @"xcopy \\" + sqlserver_comboBox.Text +
@"\Fibertop C:\ /s/e/y";
proc.StandardInput.WriteLine(dosLine);
// 延时300毫秒(0.3秒)
Thread.Sleep(300);
// ============================================================
// 第4步:断开共享文件夹
// ============================================================
// net use /del 命令:断开网络共享
dosLine = @"net use \\" + sqlserver_comboBox.Text +
@"\飞思卓共享文件 /del";
proc.StandardInput.WriteLine(dosLine);
// 输入 exit 命令,退出 cmd
proc.StandardInput.WriteLine("exit");
//proc.StandardInput.Close(); // 原来的代码注释掉了
// 等待 cmd 进程退出
proc.WaitForExit();
// ============================================================
// 第5步:读取命令行输出,判断复制是否成功
// ============================================================
// 读取所有输出内容
string str = proc.StandardOutput.ReadToEnd();
// 如果输出里包含"复制了 0 个文件",说明复制失败了(一个文件都没复制到)
if (str.Contains("复制了 0 个文件"))
{
Flag = false; // 复制失败
}
else
{
Flag = true; // 复制成功
}
// 弹出消息框,显示复制结果(复制了多少个文件等信息)
MessageBox.Show(str);
}
catch (Exception ex) // 如果出错
{
Flag = false; // 标记为失败
throw ex; // 把异常抛出去,让调用者处理
}
finally // finally:不管成功还是失败,都会执行这里的代码
{
// 关闭进程
proc.Close();
// 释放进程占用的资源
proc.Dispose();
}
// 返回结果
return Flag;
}
// ============================================================
// 从服务器更新 按钮点击事件
// 触发时机:用户点击"从服务器更新"按钮
// 作用:
// 1. 从服务器复制最新的Access数据库到本地
// 2. 从本地数据库读取模块类型列表,填充到下拉框
// ============================================================
private void update_button_Click(object sender, EventArgs e)
{
// ============================================================
// 变量定义
// ============================================================
int i = 0; // 计数器,记录读到了多少种模块类型
// ============================================================
// 第1部分:处理数据库更新
// ============================================================
// 检查有没有选择服务器
if (string.IsNullOrEmpty(sqlserver_comboBox.Text) ||
sqlserver_comboBox.Text == "null")
{
// 没选服务器,就不更新,用本地现有的数据库
GlobalVarFun.access_updated_status = false;
// 弹出提示,告诉用户没选服务器,不更新
MessageBox.Show(
"未选择服务器IP,系统将不更新本机Access文件,请确认!",
"Warning",
MessageBoxButtons.OK,
MessageBoxIcon.Warning);
}
else // 选了服务器,就尝试更新
{
// 先 Ping 一下服务器,看网络通不通
if (TestServerIPonline() == false)
{
MessageBox.Show(
"服务器IP地址ping不通,网络不畅通,请检查网络连接!\r\n",
"测试结果",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return; // Ping不通就直接返回
}
//
// 从服务器复制 Access 数据库文件到本地
if (CopyShareDBFileToLocal())
{
// 复制成功
GlobalVarFun.access_updated_status = true;
}
else
{
// 复制失败
GlobalVarFun.access_updated_status = false;
// 弹出确认框,问用户要不要继续
// 如果用户点了"否",就直接返回,不往下执行了
if (MessageBox.Show(
"操作失败:从服务器更新Access文件到本地失败,请确认是否继续???",
"提醒",
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning) == DialogResult.No)
{
return;
}
}
}
// ============================================================
// 第2部分:从本地 Access 数据库读取模块类型列表
// ============================================================
// 先清空模块类型下拉框里的所有项
type_comboBox.Items.Clear();
try // 异常处理
{
// ============================================================
// 定义数据库操作相关的对象
// ============================================================
OleDbConnection dbconnect; // 数据库连接对象
OleDbCommand dbcommand; // 数据库命令对象(执行SQL语句)
OleDbDataAdapter dbadapter; // 数据适配器(填充数据集)
DataSet dbset; // 数据集(存放查询结果)
string dbconnectionstr = ""; // 数据库连接字符串
// ============================================================
// 连接 Access 数据库
// ============================================================
// 数据库文件路径:filePath + "SupportedInfo.mdb"
// filePath 是 C:\Fibertop\
// 所以完整路径是 C:\Fibertop\SupportedInfo.mdb
dbconnect = new OleDbConnection(
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source= " +
filePath + "SupportedInfo.mdb");
// ============================================================
// 写 SQL 查询语句
// ============================================================
// 从 TypeInfo 表中查询 ModuleType(模块类型)和 AccessFilePath(数据库文件路径)
// [TypeInfo] 是表名,加中括号是因为可能和关键字重名
dbconnectionstr = string.Format(
"select ModuleType,AccessFilePath from [TypeInfo]");
// 创建命令对象
dbcommand = new OleDbCommand(dbconnectionstr, dbconnect);
// 创建数据适配器
dbadapter = new OleDbDataAdapter(dbcommand);
// 创建数据集
dbset = new DataSet();
// 执行查询,把结果填充到数据集的 TypeInfo 表里
dbadapter.Fill(dbset, "TypeInfo");
// ============================================================
// 遍历查询结果,填充到下拉框
// ============================================================
// 遍历 TypeInfo 表的每一行
foreach (DataRow dataRow in dbset.Tables["TypeInfo"].Rows)
{
// 如果模块类型名称不为空
if (dataRow["ModuleType"].ToString() != "")
{
// 把模块类型名称加到下拉框里
type_comboBox.Items.Add(dataRow["ModuleType"]);
// 把对应的数据库文件路径存到数组里
// filePath + AccessFilePath = 完整的文件路径
moduleTypeDBName[i] = filePath +
Convert.ToString(dataRow["AccessFilePath"]);
// 计数器加1
i++;
}
}
// ============================================================
// 释放数据库资源
// ============================================================
dbconnect.Close(); // 关闭连接
dbcommand.Dispose(); // 释放命令对象
dbadapter.Dispose(); // 释放适配器
dbset.Dispose(); // 释放数据集
}
catch (Exception exp) // 如果读数据库出错
{
// "确定"按钮变灰,不能点击(因为没读到模块类型,没法登录)
ok_button.Enabled = false;
// 弹出错误信息
MessageBox.Show(exp.Message);
return; // 直接返回
}
// ============================================================
// 设置下拉框默认选中项
// ============================================================
// 如果模块类型数量大于等于4个,默认选中第5个(索引是4,因为从0开始)
// 为什么是4?可能是因为QSFP在第5个位置,这是常用的
if (i >= 4)
{
type_comboBox.SelectedIndex = 4;
}
}
// ============================================================
// 确定按钮 点击事件 ⭐ 最重要的函数
// 触发时机:用户点击"确定"按钮
// 作用:
// 1. 连接所有设备
// 2. 初始化各种对象
// 3. 全部成功后,关闭登录窗体,进入主界面
//
// 【重要】登录窗体怎么进入主界面?
// 1. 在 Program.cs 里,调用了 login_Form.ShowDialog()
// 2. ShowDialog() 会阻塞,直到窗体关闭
// 3. 当用户点击"确定"按钮时:
// - 这个函数里的代码执行完
// - 因为 ok_button 的 DialogResult 属性在设计器里设为了 OK
// - 所以窗体会自动关闭,并且 ShowDialog() 返回 DialogResult.OK
// 4. Program.cs 里判断返回值是 OK,就调用 Application.Run(main_Form) 显示主界面
// ============================================================
private void ok_button_Click(object sender, EventArgs e)
{
// ============================================================
// 第1步:连接 OTP12 设备
// ============================================================
string otp12Ip = textBox_otp12Ip.Text.Trim(); // 获取IP
// 调用 Connect 方法连接,返回 false 表示失败
if (!GlobalVarFun.otp12.Connect(otp12Ip))
{
// 连接失败,弹出提示
MessageBox.Show(
"OTP12连接失败!\r\n请检查IP地址和网络连接",
"警告",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return; // 直接返回,不往下执行了
}
// ============================================================
// 第2步:连接加热台
// ============================================================
string heaterIp = textBox_heaterIp.Text.Trim(); // 获取IP
// 调用 Open 方法连接,返回 false 表示失败
if (!GlobalVarFun.heater.Open(heaterIp))
{
MessageBox.Show(
"加热台连接失败!\r\n请检查IP地址和网络连接",
"警告",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return;
}
// ============================================================
// 第3步:初始化 I2C 通信对象
// ============================================================
// 原来的 I2C 是通过并口或USB转I2C的
// 现在改成通过加热台转发 I2C 命令
// I2C_Heater 就是我们写的加热台I2C实现类
// 参数1:加热台对象
// 参数2:当前槽位号(GlobalVarFun.cutrrentSlot,注意单词拼错了,应该是currentSlot)
GlobalVarFun.iic = new I2C_Heater(
GlobalVarFun.heater,
GlobalVarFun.cutrrentSlot);
/////////////////////////////////////////////////////////////
// 第4步:判断测试工序(初测 还是 终测)
///////////////////////////////////////////////////////////
// 先清空测试类型
GlobalVarFun.testType = "";
// 如果选中了"初测"单选框(radioButton3)
if (radioButton3.Checked)
{
GlobalVarFun.testType = "firstTest"; // 测试类型设为初测
}
// 如果选中了"终测"单选框(radioButton4)
if (radioButton4.Checked)
{
GlobalVarFun.testType = "finalTest"; // 测试类型设为终测
}
///////////////////////////////////////////////////////////
// ============================================================
// 第5步:测试 I2C 通信是否正常
// ============================================================
try
{
// 尝试打开 I2C
if (GlobalVarFun.iic.TWI_Open() == false)
{
throw new Exception(); // 打开失败,抛出异常
}
// 打开成功,马上关掉(只是测试一下能不能打开)
GlobalVarFun.iic.TWI_Close();
}
catch
{
// I2C 初始化失败,弹出提示
MessageBox.Show(
"I2C通信初始化失败!\r\n程序退出",
"警告",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
Application.Exit(); // 直接退出整个程序
}
// ============================================================
// 第6步:初始化模块测试对象
// ============================================================
// 根据用户选的模块类型,创建对应的测试对象
// 目前只支持 QSFP
if (type_comboBox.Text == "QSFP")
{
// 创建 QSFP 对象,转成 ModuleTest 基类类型,存到全局变量
// ModuleTest 是基类,QSFP 是子类(继承自 ModuleTest)
GlobalVarFun.mTest = new QSFP() as ModuleTest;
}
else
{
// 不支持的模块类型,弹出提示
MessageBox.Show(
"模块类型初始化失败!\r\n程序退出",
"警告",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
Application.Exit(); // 退出程序
}
// ============================================================
// 第7步:设置 Access 数据库路径
// ============================================================
// 根据用户选的模块类型,从数组里取出对应的数据库文件路径
// type_comboBox.SelectedIndex 是选中项的索引(0,1,2...)
GlobalVarFun.moduleLutDBFilePath = moduleTypeDBName[type_comboBox.SelectedIndex];
// 标记 Access 数据库连接状态为已连接
GlobalVarFun.access_connect_status = true;
// ============================================================
// 第8步:判断 SQL 数据库要不要用
// ============================================================
// 如果没选服务器,或者选的是"null",就不用 SQL 数据库
if (sqlserver_comboBox.Text.Trim() == "" ||
sqlserver_comboBox.Text == "null")
{
GlobalVarFun.sqlconnection = null; // 设为 null,表示不用
}
// ============================================================
// 第9步:保存生产单号
// ============================================================
// 把用户输入的生产单号存到 TestResult 类的静态变量里
// TestResult 类用来存测试结果相关的信息
TestResult.fibertop_bn = bn_textBox.Text;
// ============================================================
// 【重要】函数执行完之后,会发生什么?
// ============================================================
// 因为 ok_button 按钮的 DialogResult 属性在设计器里设置成了 OK
// 所以点击按钮后:
// 1. 这个函数里的代码执行完
// 2. 登录窗体会自动关闭
// 3. ShowDialog() 方法返回 DialogResult.OK
// 4. 回到 Program.cs 里,判断返回值是 OK,就打开主界面
}
// ============================================================
// 模块类型下拉框 选项改变事件
// 触发时机:用户切换模块类型下拉框的时候
// 作用:
// 1. 把选中的模块类型存到全局变量
// 2. 把"确定"按钮设为可用(没选模块类型的时候,确定按钮是灰的)
// ============================================================
private void type_comboBox_SelectedIndexChanged(object sender, EventArgs e)
{
// 如果选中了某一项(索引 >= 0)
if (type_comboBox.SelectedIndex >= 0)
{
// 把模块类型存到全局变量
GlobalVarFun.moduleType = type_comboBox.Text;
// "确定"按钮变成可用(可以点击了)
ok_button.Enabled = true;
}
}
// ============================================================
// 读取飞思卓单号 按钮点击事件
// 触发时机:用户点击这个按钮
// 作用:目前是空的,还没实现功能
// ============================================================
private void readFibertopbn_button1_Click(object sender, EventArgs e)
{
//
// 这个函数目前是空的,还没写代码
// 可能是用来自动读取生产单号的,后面再实现
}
}
}
特别说明:登录界面怎么进入主界面?
很多人会困惑这个问题,我单独拿出来说一下:
流程是这样的:
Program.cs 里
↓
创建 Login_Form 和 Main_Form 对象
↓
调用 login_Form.ShowDialog() ← 程序停在这里,等登录窗体关闭
↓
用户在登录界面点"确定"按钮
↓
ok_button_Click 里的代码执行(连接设备、初始化等)
↓
因为 ok_button 的 DialogResult 属性在设计器里设为了 OK
所以窗体会自动关闭
↓
ShowDialog() 返回 DialogResult.OK
↓
Program.cs 里判断 if (login_Form.DialogResult == DialogResult.OK)
↓
调用 Application.Run(main_Form)
↓
主界面显示出来 ✅
关键点:
ShowDialog()是模态对话框,会阻塞程序,直到窗体关闭- 按钮的
DialogResult属性可以在设计器里设置,设为 OK 的话,点击按钮窗体会自动关闭 - 关闭后
ShowDialog()会返回对应的结果(OK、Cancel 等)