Filtrando DataGridView sem alterar a fonte de dados


95

Estou desenvolvendo o controle do usuário em C # Visual Studio 2010 - uma espécie de caixa de texto de "localização rápida" para filtrar o datagridview. Deve funcionar para 3 tipos de fontes de dados datagridview: DataTable, DataBinding e DataSet. Meu problema é filtrar DataTable do objeto DataSet, que é exibido em DataGridView.

Pode haver 3 casos (exemplos de aplicativo WinForm padrão com DataGridView e TextBox nele) - os 2 primeiros estão funcionando bem, tenho problema com o terceiro:

1. datagridview.DataSource = dataTable: funciona
para que eu possa filtrar definindo: 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: funciona
para que eu possa filtrar definindo: 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 = "TableName": não funciona
Acontece quando você projeta uma tabela usando o designer: coloque o DataSet da caixa de ferramentas no formulário, adicione dataTable a ele e então defina datagridview.DataSource = dataSource; e datagridview.DataMember = "TableName".
O código abaixo finge essas operações:

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

Se você testá-lo - embora a tabela de dados seja filtrada (ds.Tables [0] .DefaultView.Count muda), o datagridview não é atualizado ... Há muito tempo que procuro uma solução, mas o problema é que o DataSource não pode mudança - como é um controle adicional, não quero atrapalhar o código do programador.

Eu sei que as soluções possíveis são:
- vincular DataTable a DataSet usando DataBinding e usá-lo como exemplo 2: mas cabe ao programador durante a escrita do código,
- alterar dataSource para BindingSource, dataGridView.DataSource = dataSet.Tables [0], ou para DefaultView programaticamente: no entanto, ele altera a DataSource. Portanto, a solução:

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

não é aceitável, como você pode ver no dataSource do MessageBox está mudando ...

Não quero fazer isso, porque é possível que um programador escreva um código semelhante a este:

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
}

Ele pode fazer isso, pois projetou DataGridView com DataSet e DataMember no designer. O código será compilado, porém, após usar um filtro, ele lançará uma exceção ...

Portanto, a questão é: como posso filtrar DataTable em DataSet e mostrar os resultados em DataGridView sem alterar DataSource para outro? Por que posso filtrar DataTable do exemplo 1 diretamente, enquanto filtrar DataTable do DataSet não está funcionando? Talvez não seja DataTable vinculado a DataGridView nesse caso?

Observe que meu problema decorre de problemas de design, portanto, a solução DEVE FUNCIONAR no exemplo 3.


1
Meus 2 centavos, além de todos os comentários e soluções valiosos. Aqui está um artigo que descreve os prós e os contras de filtrar DataGridView vinculado a dados dessa maneira e fornece algumas idéias de como fazer isso melhor.
TecMan de

Desculpe a repetição, mas acho que minha proposta não funciona sempre. Na verdade, às vezes uma exceção é levantada, o que meu código é improvável. Tentando filtrar com um bindingSource você tem todas as chances de fazer um bom código. Como data: bindingSource.Filter = string.Format .....
KOUAKEP ARNOLD

Eu gosto do comentário do TecMan. Você poderia delegar o trabalho de filtragem para a interface IBindingListView pela propriedade filter (menos funciona, mas apenas realmente utilizável com ADO.Net Datatable) ou fazendo todo o trabalho em seu controle (mais funciona, mas deve funcionar com qualquer coisa).
Marco Guignard

Respostas:


143

Acabei de passar uma hora em um problema semelhante. Para mim, a resposta acabou sendo embaraçosamente simples.

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

2
como colocar vincular este evento à caixa de texto
Arun Prasad ES

7
A sintaxe de filtragem pode ser encontrada aqui: csharp-examples.net/dataview-rowfilter
Sal

Usar uma DataTable como fonte contorna o problema de ter que implementar de IBindingListViewacordo com msdn.microsoft.com/en-us/library/…
Jeremy Thompson

Recebo este erro: Object reference not set to an instance of an object.para o GridView.
Si8

Qual é a sua fonte de dados? Meu exemplo assume que você está usando um DataTable. Se você estiver usando outra coisa, verifique seu elenco. "como DataTable" no meu exemplo.
Brad Bruce

22

Desenvolvi uma declaração genérica para aplicar o filtro:

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

Os colchetes permitem espaços no nome da coluna.

Além disso, se quiser incluir vários valores em seu filtro, você pode adicionar a seguinte linha para cada valor adicional:

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

12

Uma maneira mais simples é cruzar os dados e ocultar as linhas com a Visiblepropriedade.

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

Apenas uma ideia ... funciona para mim.


Como alguém que está preenchendo manualmente um DataGridView, isso funcionou perfeitamente. :) Embora eu tenha usado um foreache atribuído diretamente row.Visible = showAll || <condition>;sem nenhum if. Isso showAllé verdade se a string do filtro estiver vazia.
Andrew

ótima ideia porque neste caso não estamos amarrados ao tipo de fonte de dados. nem qualquer DataTable.
mshakurov

Funcionou perfeitamente, e para melhorar a lógica de pesquisa, podemos substituir a condição if por dataGridView3.Rows [u] .Cells [4] .Value.ToString (). IndexOf ("The filter string")> = 0
Ali Ali

1

Você pode criar um objeto DataView a partir de sua fonte de dados. Isso permitiria a você filtrar e classificar seus dados sem modificar diretamente a fonte de dados.

Além disso, lembre-se de ligar dataGridView1.DataBind();depois de definir a fonte de dados.


2
Obrigado pela resposta. Sim, o objeto DataView pode ser criado, porém altera o tipo do DataSource, consulte o último código. Eu modifiquei o motivo pelo qual quero evitar isso no post anterior. O método dataGridView1.DataBind () não existe no WinForms, suponho que seja do ASP.
mj82

0

// "Comentário" Filtrar datagrid sem alterar o conjunto de dados. Funciona perfeitamente.

            (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

Tenho uma proposta mais clara sobre a busca automática em um DataGridView

isto é um exemplo

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


-2

Eu encontrei uma maneira simples de resolver esse problema. Ao vincular datagridview, você acabou de fazer:datagridview.DataSource = dataSetName.Tables["TableName"];

Se você codificar como:

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

o datagridview nunca carregará dados novamente durante a filtragem.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.