【问题标题】:C# C0246 While Filtering DataGridView with Listbox whose items come from SQL ServerC# C0246 使用来自 SQL Server 的项目的列表框过滤 DataGridView
【发布时间】:2020-10-01 15:50:52
【问题描述】:

我与您分享一段代码,除了我试图在我的列表框项目中循环的部分之外,它可以工作。这就是为什么我在这里向你寻求帮助的原因。 最近,我从 VBA 切换到 C#,所以我还是新手,还不了解所有内容。 因此,下面的代码连接到我的 SQL 服务器数据库并在我的列表框和 DataGridView 中获取数据。我也可以使用两个文本框进行过滤。 所以现在我的列表框中有项目,DataGridview 中有我的数据库视图。我想用我的列表框项目过滤我的 DataGridview (由 datatable 填充)。我想我只错过了一个愚蠢的部分。为什么我会收到此 CS0246“找不到列表项”

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();
                    }
                }
            }
        }
    }
 
  }
}

【问题讨论】:

  • 如果您确保所有代码行开头至少有 4 个空格,那么它们在 stackoverflow 上的格式会很好
  • @CaiusJard 谢谢,现在应该更好了;)

标签: c# sql-server listbox windows-forms-designer .net-4.7.2


【解决方案1】:

这行有问题:

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 注入黑客攻击。

处理问题的更好方法是通过参数化。你会这样做:

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 和一个字符串连接字符串。你不需要为它创建一个 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";
}

【讨论】:

  • 非常感谢您的所有建议,这是金子!不错的法语加 :) 但是,我有一些问题。首先,您如何知道它是 WebForms 还是 WinForms 对象?你的提议绝对比我的更合乎逻辑,所以我做出了改变,但奇怪的是,一旦我运行应用程序,它就直接在最后一行“sda.Fill(dt) 崩溃,并警告错误放置了括号,但它是就像代码将所有项目直接放在条件下,甚至让我点击或选择其中一个。我希望它足够清楚......
  • 对于 webforms/winforms,这些天我认识其中的大多数,但如果我不认识,那么快速的谷歌“ListItem 类”就会给出它 - 认为谷歌很久以前就意识到我是一名软件开发人员,因为第一个命中是 MSDN :),页面顶部是命名空间 System.Web.UI.. 我不太明白另一个问题。您是说您的程序在运行时崩溃并显示错误消息?或者您在代码下看到一条红色波浪线有效?确切的流星信息是什么(在任何一种情况下)?
  • @CaiuJard,感谢您的回答,我显然仍然需要与微软的帮助相处!我会仔细检查我的错误信息,下班后给你回复。为我迟到的回复道歉。
  • 嘿@Caius Jard 我终于可以连接和测试了。这是我的错误消息,所以我猜它是在字符串条件下有一些东西:System.Data.SqlClient.SqlException:'字符串'Underperform)'之后的未闭合引号。 “表现不佳)”附近的语法不正确。但问题是,为什么它在我启动应用程序时运行过滤器,而不是在我检查框时运行过滤器
  • 可能是因为您连接的事件不符合您的需要。在列表框上使用不同的事件?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多