Фильтрация DataGridView без изменения источника данных


95

Я разрабатываю пользовательский элемент управления в C # Visual Studio 2010 - своего рода текстовое поле «быстрого поиска» для фильтрации datagridview. Он должен работать для 3 типов источников данных datagridview: DataTable, DataBinding и DataSet. Моя проблема связана с фильтрацией DataTable из объекта DataSet, который отображается в DataGridView.

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

1. datagridview.DataSource = dataTable: он работает,
поэтому я могу фильтровать, установив: dataTable.DefaultView.RowFilter = "country LIKE '% 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 = источник данных; datagridview.DataMember = "TableName": это не работает.
Это происходит, когда вы создаете таблицу с помощью конструктора: поместите DataSet из панели инструментов в форму, добавьте к ней dataTable, а затем установите datagridview.DataSource = dataSource; и datagridview.DataMember = "TableName".
Код ниже симулирует эти операции:

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

Если вы его протестируете - хотя datatable фильтруется (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());
}

неприемлемо, как вы видите, в сообщении MessageBox dataSource меняется ...

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

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, у вас есть все шансы создать хороший код. Нравится дата: bindingSource.Filter = string.Format .....
КУАКЕП АРНОЛЬД

Мне нравится комментарий 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. Если вы используете что-то еще, проверьте свой кастинг. "как 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();

Просто идея ... у меня это работает.


Как человек, заполняющий вручную DataGridView, это сработало отлично. :) Хотя я использовал foreachи напрямую назначал row.Visible = showAll || <condition>;без каких-либо if. Это showAllверно, если строка фильтра пуста.
Эндрю

отличная идея, потому что в этом случае мы не привязаны к типу источника данных. ни какой-либо DataTable.
Мшакуров

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

1

Вы можете создать объект DataView из своего источника данных. Это позволит вам фильтровать и сортировать данные без прямого изменения источника данных.

Кроме того, не забудьте позвонить dataGridView1.DataBind();после установки источника данных.


2
Спасибо за ответ. Да, объект DataView можно создать, однако он изменяет тип источника данных, см. Последний код. Я изменил причину, по которой я хочу избежать этого, в предыдущем посте. dataGridView1.DataBind () не существует в WinForms, я полагаю, он из ASP.
mj82 01

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.