Фільтрування DataGridView без зміни джерела даних


95

Я розробляю управління користувачами в C # Visual Studio 2010 - своєрідне текстове поле "швидкого пошуку" для фільтрації datagridview. Він повинен працювати для 3 типів джерел даних datagridview: DataTable, DataBinding та DataSet. Моя проблема полягає у фільтруванні DataTable від об'єкта DataSet, який відображається на DataGridView.

Може бути 3 випадки (приклади для стандартного додатку WinForm з DataGridView та TextBox) - перші 2 працюють нормально, у мене проблема з 3-м:

1. datagridview.DataSource = dataTable: він працює,
тому я можу фільтрувати, встановивши: dataTable.DefaultView.RowFilter = "країна ПОДОБАЄТЬСЯ '% s%'";

DataTable dt = new DataTable();

private void Form1_Load(object sender, EventArgs e)
{
    dt.Columns.Add("id", typeof(int));
    dt.Columns.Add("country", typeof(string));

    dt.Rows.Add(new object[] { 1, "Belgium" });
    dt.Rows.Add(new object[] { 2, "France" });
    dt.Rows.Add(new object[] { 3, "Germany" });
    dt.Rows.Add(new object[] { 4, "Spain" });
    dt.Rows.Add(new object[] { 5, "Switzerland" });
    dt.Rows.Add(new object[] { 6, "United Kingdom" });

    dataGridView1.DataSource = dt;
}

private void textBox1_TextChanged(object sender, EventArgs e)
{
    MessageBox.Show("DataSource type BEFORE = " + dataGridView1.DataSource.GetType().ToString());

    dt.DefaultView.RowFilter = string.Format("country LIKE '%{0}%'", textBox1.Text);

    MessageBox.Show("DataSource type AFTER = " + dataGridView1.DataSource.GetType().ToString());
} 

2. datagridview.DataSource = bindingSource: він працює,
тому я можу фільтрувати, встановивши: bindingSource.Filter = "country LIKE '% s%'";

DataTable dt = new DataTable();
BindingSource bs = new BindingSource();

private void Form1_Load(object sender, EventArgs e)
{
    dt.Columns.Add("id", typeof(int));
    dt.Columns.Add("country", typeof(string));

    dt.Rows.Add(new object[] { 1, "Belgium" });
    dt.Rows.Add(new object[] { 2, "France" });
    dt.Rows.Add(new object[] { 3, "Germany" });
    dt.Rows.Add(new object[] { 4, "Spain" });
    dt.Rows.Add(new object[] { 5, "Switzerland" });
    dt.Rows.Add(new object[] { 6, "United Kingdom" });

    bs.DataSource = dt;
    dataGridView1.DataSource = bs;
}

private void textBox1_TextChanged(object sender, EventArgs e)
{
    MessageBox.Show("DataSource type BEFORE = " + dataGridView1.DataSource.GetType().ToString());

    bs.Filter = string.Format("country LIKE '%{0}%'", textBox1.Text);

    MessageBox.Show("DataSource type AFTER = " + dataGridView1.DataSource.GetType().ToString());
}

3. datagridview.DataSource = dataSource; datagridview.DataMember = "Ім'я
таблиці ": це не працює. Це трапляється, коли ви проектуєте таблицю за допомогою конструктора: помістіть набір даних з набору інструментів у форму, додайте до неї dataTable, а потім встановіть datagridview.DataSource = dataSource; та datagridview.DataMember = "Ім'я таблиці".
Наведений нижче код видає такі операції:

DataSet ds = new DataSet();
DataTable dt = new DataTable();

private void Form1_Load(object sender, EventArgs e)
{
    dt.Columns.Add("id", typeof(int));
    dt.Columns.Add("country", typeof(string));

    dt.Rows.Add(new object[] { 1, "Belgium" });
    dt.Rows.Add(new object[] { 2, "France" });
    dt.Rows.Add(new object[] { 3, "Germany" });
    dt.Rows.Add(new object[] { 4, "Spain" });
    dt.Rows.Add(new object[] { 5, "Switzerland" });
    dt.Rows.Add(new object[] { 6, "United Kingdom" });

    ds.Tables.Add(dt);
    dataGridView1.DataSource = ds;
    dataGridView1.DataMember = dt.TableName;
}

private void textBox1_TextChanged(object sender, EventArgs e)
{
    MessageBox.Show("DataSource type BEFORE = " + dataGridView1.DataSource.GetType().ToString());  
    //it is not working
    ds.Tables[0].DefaultView.RowFilter = string.Format("country LIKE '%{0}%'", textBox1.Text);

    MessageBox.Show("DataSource type AFTER = " + dataGridView1.DataSource.GetType().ToString());
}

Якщо ви його протестуєте - хоча таблиця даних відфільтрована (ds.Tables [0] .DefaultView.Count зміни), datagridview не оновлюється ... Я довго шукав будь-яке рішення, але проблема в тому, що DataSource не може зміна - оскільки це додатковий контроль, я не хочу, щоб він псував код програміста.

Я знаю, що можливі рішення:
- пов’язати DataTable з DataSet за допомогою DataBinding і використовувати його як приклад 2: але це залежить від програміста під час написання коду,
- змінити dataSource на BindingSource, dataGridView.DataSource = dataSet.Tables [0], або до DefaultView програмно: однак, він змінює DataSource. Отже, рішення:

private void textBox1_TextChanged(object sender, EventArgs e)
{
    MessageBox.Show("DataSource type BEFORE = " + dataGridView1.DataSource.GetType().ToString(), ds.Tables[0].DefaultView.Count.ToString());

    DataView dv = ds.Tables[0].DefaultView;
    dv.RowFilter = string.Format("country LIKE '%{0}%'", textBox1.Text);
    dataGridView1.DataSource = dv;

    MessageBox.Show("DataSource type AFTER = " + dataGridView1.DataSource.GetType().ToString(), ds.Tables[0].DefaultView.Count.ToString());
}

неприйнятно, як ви бачите в DataBox MessageBox змінюється ...

Я не хочу цього робити, тому що можливо, програміст пише код, подібний до цього:

private void textBox1_TextChanged(object sender, EventArgs e)
{
    MessageBox.Show("DataSource type BEFORE = " + dataGridView1.DataSource.GetType().ToString(), ds.Tables[0].DefaultView.Count.ToString());

    DataSet dsTmp = (DataSet)(dataGridView1.DataSource);   //<--- it is OK 

    DataView dv = ds.Tables[0].DefaultView;
    dv.RowFilter = string.Format("country LIKE '%{0}%'", textBox1.Text);
    dataGridView1.DataSource = dv;   //<--- here the source is changeing from DataSet to DataView

    MessageBox.Show("DataSource type AFTER = " + dataGridView1.DataSource.GetType().ToString(), ds.Tables[0].DefaultView.Count.ToString());

    dsTmp = (DataSet)(dataGridView1.DataSource);    //<-- throws an exception: Unable to cast object DataView to DataSet
}

Він може це зробити, оскільки розробив DataGridView за допомогою DataSet та DataMember у дизайнері. Код буде скомпільовано, однак після використання фільтра він видасть виняток ...

Отже, питання полягає в тому: як я можу фільтрувати DataTable у DataSet і показувати результати на DataGridView, не змінюючи DataSource на інший? Чому я можу відфільтрувати DataTable з прикладу 1 безпосередньо, тоді як фільтрування DataTable з DataSet не працює? Може, в такому випадку це не DataTable, прив’язаний до DataGridView?

Зверніть увагу, що моя проблема береться від проектування, тому рішення МОЖЕ РОБОТИ на прикладі 3.


1
Мої 2 центи на додаток до всіх цінних коментарів та рішень. Ось стаття, яка описує плюси і мінуси фільтрації DataGridView таким чином, пов’язаних із даними, та дає вам ідеї, як це зробити краще.
TecMan

Вибачте за повторення, але я думаю, що моя пропозиція не працює кожного разу. Справді, часом усувається виняток, що мій код малоймовірний. Спроба фільтрувати за допомогою прив'язки Джерело у вас є всі шанси створити хороший код. Подобається дата: bindingSource.Filter = string.Format .....
KOUAKEP ARNOLD

Мені подобається коментар TecMan. Ви можете делегувати роботу фільтрації до інтерфейсу IBindingListView за допомогою властивості filter (менше працює, але дійсно придатне для використання з ADO.Net Datatable) або виконувати всю роботу у вашому контролі (більше робіт, але слід працювати з чим завгодно).
Marco Guignard

Відповіді:


144

Я просто витратив годину на подібну проблему. Для мене відповідь виявилася незручно простою.

(dataGridViewFields.DataSource as DataTable).DefaultView.RowFilter = string.Format("Field = '{0}'", textBoxFilter.Text);

2
як прив’язати цю подію до текстового поля
Арун Прасад ES

7
Синтаксис фільтрування можна знайти тут: csharp-examples.net/dataview-rowfilter
Sal

Використання DataTable як джерела вирішує проблему необхідності реалізації IBindingListViewвідповідно до msdn.microsoft.com/en-us/library/…
Джеремі Томпсон

Я отримую таку помилку: Object reference not set to an instance of an object.для GridView.
Si8

Яке ваше джерело даних? Мій приклад передбачає, що ви використовуєте таблицю даних. Якщо ви використовуєте щось інше, перевірте свій кастинг. "як DataTable" у моєму прикладі.
Бред Брюс

23

Я розробив загальне твердження для застосування фільтра:

string rowFilter = string.Format("[{0}] = '{1}'", columnName, filterValue);
(myDataGridView.DataSource as DataTable).DefaultView.RowFilter = rowFilter;

У квадратних дужках передбачені пробіли в назві стовпця.

Крім того, якщо ви хочете включити у свій фільтр кілька значень, ви можете додати такий рядок для кожного додаткового значення:

rowFilter += string.Format(" OR [{0}] = '{1}'", columnName, additionalFilterValue);

12

Більш простий спосіб - поперечний перегляд даних та приховування рядків із Visibleвластивістю.

// Prevent exception when hiding rows out of view
CurrencyManager currencyManager = (CurrencyManager)BindingContext[dataGridView3.DataSource];
currencyManager.SuspendBinding();

// Show all lines
for (int u = 0; u < dataGridView3.RowCount; u++)
{
    dataGridView3.Rows[u].Visible = true;
    x++;
}

// Hide the ones that you want with the filter you want.
for (int u = 0; u < dataGridView3.RowCount; u++)
{
    if (dataGridView3.Rows[u].Cells[4].Value == "The filter string")
    {
        dataGridView3.Rows[u].Visible = true;
    }
    else
    {
        dataGridView3.Rows[u].Visible = false;
    }
}

// Resume data grid view binding
currencyManager.ResumeBinding();

Просто ідея ... вона працює у мене.


Як хтось, хто вручну заповнює a DataGridView, це спрацювало чудово. :) Хоча я використовував foreachі безпосередньо призначений row.Visible = showAll || <condition>;без жодного if. Це showAllправда, якщо рядок фільтра порожній.
Ендрю

чудова ідея, оскільки в цьому випадку ми не прив’язані до типу джерела даних. ані будь-яка таблиця даних.
Мшакуров

Працював ідеально, і для поліпшення логіки пошуку ми можемо замінити умовою if dataGridView3.Rows [u] .Cells [4] .Value.ToString (). IndexOf ("Рядок фільтра")> = 0
Алі Алі

1

Ви можете створити об’єкт DataView із джерела даних. Це дозволить вам фільтрувати та сортувати дані, не змінюючи безпосередньо джерело даних.

Також не забудьте зателефонувати dataGridView1.DataBind();після встановлення джерела даних.


2
Дякую за відповідь. Так, об'єкт DataView можна створити, проте він змінює тип DataSource, див. Останній код. Я змінив причину, чому я хочу уникати цього в попередньому дописі. метод dataGridView1.DataBind () не існує в WinForms, я вважаю, що це від ASP.
mj82

0

// "Коментувати" Фільтрувати сітку даних, не змінюючи набір даних, чудово працює.

            (dg.ItemsSource as ListCollectionView).Filter = (d) =>
            {
                DataRow myRow = ((System.Data.DataRowView)(d)).Row;
                if (myRow["FName"].ToString().ToUpper().Contains(searchText.ToString().ToUpper()) || myRow["LName"].ToString().ToUpper().Contains(searchText.ToString().ToUpper()))
                    return true; //if want to show in grid
                return false;    //if don't want to show in grid
            };         

0

У мене є більш чітка пропозиція щодо автоматичного пошуку в DataGridView

це приклад

private void searchTb_TextChanged(object sender, EventArgs e)
    {
        try
        {
            (lecteurdgview.DataSource as DataTable).DefaultView.RowFilter = String.IsNullOrEmpty(searchTb.Text) ?
                "lename IS NOT NULL" :
                String.Format("lename LIKE '{0}' OR lecni LIKE '{1}' OR ledatenais LIKE '{2}' OR lelieu LIKE '{3}'", searchTb.Text, searchTb.Text, searchTb.Text, searchTb.Text);
        }
        catch (Exception ex) {
            MessageBox.Show(ex.StackTrace);
        }
    }

Може дублюватися зі stackoverflow.com/questions/5843537/…
Тоні Донг,

-2

Я знайшов простий спосіб вирішити цю проблему. У прив’язуванні datagridview ви щойно зробили:datagridview.DataSource = dataSetName.Tables["TableName"];

Якщо ви кодуєте, як:

datagridview.DataSource = dataSetName;
datagridview.DataMember = "TableName";

datagridview ніколи більше не завантажуватиме дані під час фільтрації.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.