问题描述
我与您分享了一段有效的代码,除了我试图在列表框项目中循环的那一部分。这就是为什么我在这里向您寻求帮助。 最近,我从VBA切换到了C#,因此我对此仍然很陌生,并且不了解所有内容。 因此,以下代码连接到我的SQL Server数据库,并在列表框和DataGridView中获取数据。我也可以使用两个文本框进行过滤。 所以现在我在列表框中有项目,在DataGridview中有数据库视图。我想用Listbox的项目过滤DataGridview(由datatable填充)。我只想念一个愚蠢的部分。为什么我得到此CS0246“找不到ListItem”
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Configuration;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsAppTest
{
public partial class Form1 : Form
{
//Initialize the component and display the items within my listbox CS_Bonds_listBox
public Form1()
{
InitializeComponent();
string connetionString = @"Data Source=my_server;Initial Catalog=my_db;Integrated Security=SSPI";
SqlConnection conn = new SqlConnection(connetionString);
conn.Open();
DataSet ds = new DataSet();
SqlDataAdapter adapter = new SqlDataAdapter(
"SELECT DISTINCT RatingProvider FROM Bonds",conn);
adapter.Fill(ds);
this.CS_Bonds_listBox.DataSource = ds.Tables[0];
this.CS_Bonds_listBox.DisplayMember = "RatingProvider";
}
private void Form1_Load(object sender,EventArgs e)
{
}
DataTable dtTEST = new DataTable();
// Next,when clicking on my button Connect,I retrieve my db into a Datatable that is displayed within //the Datagridview1
private void buttonConnect_Click(object sender,EventArgs e)
{
string connetionString = @"Data Source=my_server;Initial Catalog=my_db;Integrated Security=SSPI";
SqlConnection cnn= new SqlConnection(connetionString);
cnn.Open();
MessageBox.Show("Connection Open !");
String sql = "Select * from Bonds";
SqlCommand command = new SqlCommand(sql,cnn);
SqlDataAdapter sqlDA = new SqlDataAdapter();
sqlDA.SelectCommand = command;
sqlDA.Fill(dtTEST);
dataGridView1.DataSource = dtTEST;
cnn.Close();
}
private void ISIN_Bonds_textBox_TextChanged(object sender,EventArgs e)
{
DataView dv = dtTEST.DefaultView;
dv.RowFilter = "ISIN LIKE '" + ISIN_Bonds_textBox.Text + "%'";
dataGridView1.DataSource = dv;
}
private void Ticker_Bonds_textBox_TextChanged(object sender,EventArgs e)
{
DataView dv1 = dtTEST.DefaultView;
dv1.RowFilter = "Ticker LIKE '" + Ticker_Bonds_textBox.Text + "%'";
dataGridView1.DataSource = dv1;
}
private void CS_Bonds_listBox_SelectedIndexChanged(object sender,EventArgs e)
{
string conString = @"Data Source=my_server;Initial Catalog=my_db;Integrated Security=SSPI";
string query = "SELECT ISIN,Ticker,CrediSight,FROM Bonds";
string condition = string.Empty;
foreach (ListItem item in CS_Bonds_listBox.Items)
{
condition += item.Selected ? string.Format("'{0}',",item.Value) : "";
}
if (!string.IsNullOrEmpty(condition))
{
condition = string.Format(" WHERE Country IN ({0})",condition.Substring(0,condition.Length - 1));
}
using (SqlConnection con = new SqlConnection(conString))
{
using (SqlCommand cmd = new SqlCommand(query + condition))
{
using (SqlDataAdapter sda = new SqlDataAdapter(cmd))
{
cmd.Connection = con;
using (DataTable dt = new DataTable())
{
sda.Fill(dt);
dataGridView1.DataSource = dt;
//dataGridView1.DataBind();
}
}
}
}
}
}
}
解决方法
此行有问题:
foreach (ListItem item in CS_Bonds_listBox.Items)
ListItem是WebForms的东西,而您的应用程序是WinForms的东西;您的列表框不包含ListItem对象的列表,因此即使导入了相关的Web名称空间,此行代码也无法实现。
因为已将列表框绑定到数据表,所以显示的列表中充满了DataRowView对象,因此需要进行处理。 DataRowView具有Row属性,该属性为您提供基础行,而基础行又可以通过列名进行访问。
另外,为了使您的生活更轻松,列表框具有SelectedItems属性,因此您无需检查每个项目是否被选中:
foreach (DataRowView drv in CS_Bonds_listBox.SelectedItems)
{
var dr = drv.Row as DataRow;
var rp = dr["RatingProvider"];
condition += $"'{rp}',"
}
由于这种情况,您的条件将最终以逗号结尾,因此在使用它构建IN子句之前,请先对其进行修剪:
condition = condition.TrimEnd(',');
如果用户设法更改列表项中显示的文本,则此技术可能会受到SQL Injection黑客的攻击。
处理该问题的更好方法是通过参数化。您可以这样:
var cmd = new SqlCommand("SELECT * FROM table WHERE Country IN(",connStr);
int i = 0;
foreach (DataRowView drv in CS_Bonds_listBox.SelectedItems)
{
var dr = drv.Row as DataRow;
var rp = dr["RatingProvider"];
cmd.CommandText += $"@p{i},";
cmd.Parameters.Add($"@p{i}",SqlDbType.VarChar).Value = rp;
i++;
}
cmd.CommandText = cmd.CommandText.TrimEnd(',') + ")";
using(var da = new SqlDataAdapter(cmd))
{
var dt = new DataTable();
da.Fill(dt);
someGridView.DataSource = dt;
}
这将构建一个类似于SELECT * FROM table WHERE Country IN(@p0,@p1,@p2....
的sql,即我们在其中串联了参数占位符,而不是在其中串联了值。与此同时,我们用参数值填充了参数集合
这还意味着我们的数据库不能为hacked via our program,并且当用户选择名称为Cote d'Ivoire
需要注意一些其他事情来整理代码:
SqlDataAdapter可以采用字符串SQL和字符串connection-string。您无需为此编写SqlCommand。您无需为此打开和关闭连接;它知道如何自己做所有这一切。我仅使用SqlCommand,因为我在进行过程中正在构建参数集合。通常我会做using(var da = SqlDataAdapter("SELECT...","Server=..")
,因为它会使事情变得井井有条。
例如您的构造函数可以很简单:
//put this here once
private string _connStr = @"Data Source=my_server;Initial Catalog=my_db;Integrated Security=SSPI";
public Form1()
{
InitializeComponent();
var dt = new DataTable();
using(var da = new SqlDataAdapter("SELECT DISTINCT RatingProvider FROM Bonds",_connStr))
adapter.Fill(dt);
this.CS_Bonds_listBox.DataSource = dt;
this.CS_Bonds_listBox.DisplayMember = "RatingProvider";
}