C#.NET结合SQL Server跨窗体给控件赋值报类型转换错误如何解决
问题描述
在使用.NET平台C#语言结合SQL Server开发WinForm应用时,需要实现以下功能:选中dgvTransactionTable数据表格的某一行后,查询对应id的交易记录,打开TransactionView窗体并将查询到的数据传递给该窗体的对应输入控件。初始实现代码如下:
private void UpdateTransaction(object sender, EventArgs e) { if (this.dgvTransactionTable.SelectedRows.Count != 0) { DataGridViewRow row = this.dgvTransactionTable.SelectedRows[0]; int id = Convert.ToInt32(row.Cells["id"].Value); SqlConnection con = new SqlConnection(connectionString); if (con.State == ConnectionState.Closed) { con.Open(); } SqlCommand cmd = new SqlCommand("SELECT * FROM transactions where id = '" + id + "'", con); SqlDataReader dt= cmd.ExecuteReader(); if (dt.Read()) { TransactionView transactionView = new TransactionView(); transactionView.Show(); transactionView.txtTransactionName = dt.GetValue(0).ToString(); transactionView.cmbTransactionCategory = dt.GetValue(1).ToString(); transactionView.dateTransaction = dt.GetValue(2).ToString(); transactionView.txtTransactionName = dt.GetValue(3).ToString(); } } else { MessageBox.Show("Please Choose a Row", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } }
运行代码时,窗体控件赋值段抛出类型转换错误,对应报错代码如下:
transactionView.txtTransactionName = dt.GetValue(0).ToString(); transactionView.cmbTransactionCategory = dt.GetValue(1).ToString(); transactionView.dateTransaction = dt.GetValue(2).ToString(); transactionView.txtTransactionName = dt.GetValue(3).ToString();
错误提示:"cannot convert type string to system.windows.forms.textbox"(无法将string类型转换为System.Windows.Forms.TextBox类型)
问题原因
报错核心是赋值逻辑错误:WinForm中的TextBox、ComboBox、DateTimePicker都属于控件类对象,不能直接将字符串赋值给控件变量本身,需要给控件对应的值属性赋值才能正常显示内容。
现有代码还存在以下隐患:
- 字符串拼接SQL存在SQL注入风险
- 数据库连接、
SqlCommand、SqlDataReader对象未做资源释放,长期运行会引发数据库连接泄漏 - 重复给
txtTransactionName赋值,存在字段索引对应错误 - 直接跨窗体访问控件不符合封装规范,WinForm默认生成的控件访问修饰符为
private,正常场景下外部窗体无法直接访问 - 给日期选择控件直接传递字符串,容易因系统区域格式差异导致转换失败
- 用
SELECT *配合序号索引取值,一旦表结构字段顺序调整,就会出现赋值错位问题
修复方案
- 修正控件赋值逻辑:
- 文本框(
TextBox)给Text属性赋字符串值 - 下拉框(
ComboBox)可通过Text属性设置显示文本,若绑定了数据源则优先通过SelectedValue匹配对应选项 - 日期选择框(
DateTimePicker)给Value属性赋DateTime类型值,不要直接传字符串
- 文本框(
- 优化窗体传值方式:给
TransactionView添加带参数的构造函数,在窗体内部完成控件赋值,避免跨窗体直接操作控件 - 替换字符串拼接SQL为参数化查询,用
using语句自动释放数据库相关资源,改用字段名读取查询结果,避免索引错位
修复后代码
首先在TransactionView窗体类中添加带参数的构造函数:
// TransactionView窗体类内添加 public TransactionView(string transName, string category, DateTime transDate, decimal transAmount) { InitializeComponent(); // 本窗体内直接给控件属性赋值 txtTransactionName.Text = transName; cmbTransactionCategory.Text = category; dateTransaction.Value = transDate; // 原代码重复给txtTransactionName赋值,第四个字段请根据实际业务替换为对应控件,示例为金额输入框 txtTransAmount.Text = transAmount.ToString("0.00"); }
再修改调用方的按钮事件逻辑:
private void UpdateTransaction(object sender, EventArgs e) { if (this.dgvTransactionTable.SelectedRows.Count == 0) { MessageBox.Show("Please Choose a Row", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } DataGridViewRow row = this.dgvTransactionTable.SelectedRows[0]; int id = Convert.ToInt32(row.Cells["id"].Value); // using语句会在代码块结束时自动释放数据库资源,无需手动判断开关状态 using (SqlConnection con = new SqlConnection(connectionString)) { // 参数化查询避免SQL注入 const string sql = "SELECT transaction_name, category, trans_date, amount FROM transactions WHERE id = @TransId"; using (SqlCommand cmd = new SqlCommand(sql, con)) { cmd.Parameters.AddWithValue("@TransId", id); con.Open(); using (SqlDataReader dr = cmd.ExecuteReader()) { if (dr.Read()) { // 按字段名读取值,做对应类型转换 string transName = dr["transaction_name"].ToString(); string category = dr["category"].ToString(); DateTime transDate = Convert.ToDateTime(dr["trans_date"]); decimal amount = Convert.ToDecimal(dr["amount"]); // 通过构造函数传值后打开窗体 TransactionView transactionView = new TransactionView(transName, category, transDate, amount); transactionView.Show(); } } } } }
注意事项
- 代码中的数据库字段名、交易金额对应的控件名请根据实际项目命名替换
- 若
cmbTransactionCategory绑定了枚举或数据源,不要直接给Text赋值,需通过SelectedValue匹配对应选项,避免选项选中失效 - 读取数据时优先使用字段名而非序号索引,降低后续表结构调整引发的错误概率
内容的提问来源于stack exchange,提问作者Lochana Liyange
相关产品推荐
相关产品推荐

