Filtrando o DataGridView sem alterar a fonte de dados

Estou desenvolvendo controle de usuário em C # Visual Studio 2010 – uma espécie de checkbox de texto “quick find” para filtragem de datagridview. Ele deve funcionar para 3 tipos de datasources datagridview: DataTable, DataBinding e DataSet. Meu problema é com a filtragem DataTable do object DataSet, que é exibido no DataGridView.

Pode haver 3 casos (exemplos para o aplicativo WinForm padrão com DataGridView e TextBox) – os 2 primeiros estão funcionando OK, tenho problema com o terceiro:

1. datagridview.DataSource = dataTable: funciona
então eu posso 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
então eu posso 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
Isso acontece quando você cria uma tabela usando o designer: coloque o DataSet da checkbox de ferramentas no formulário, adicione dataTable a ele e defina o datagridview.DataSource = dataSource; e datagridview.DataMember = “TableName”.
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 – apesar de tabela de dados é filtrada (ds.Tables [0] .DefaultView.Count alterações), datagridview não é atualizado … Eu tenho procurado por um longo tempo para qualquer solução, mas o problema é que DataSource não pode mudar – como é o controle adicional, eu não quero que isso atrapalhe o código do programador.

Eu sei que as soluções possíveis são:
– ligar DataTable do DataSet usando DataBinding e usá-lo como exemplo 2: mas cabe ao programador durante a escrita do código,
– para alterar dataSource para BindingSource, dataGridView.DataSource = dataSet.Tables [0] ou para DefaultView programaticamente: no entanto, ele altera o DataSource. Então 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ê vê no dataSource do MessageBox está mudando …

Eu não quero fazer isso, porque é possível um programador escrever 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, como ele projetou DataGridView com DataSet e DataMember no designer. Código será compilado, no entanto, depois de usar um filtro, ele lançará uma exceção …

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

Por favor, note que o meu problema leva a partir de questões de design, então a solução deve funcionar no exemplo 3.

Eu passei apenas 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); 

Eu 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 você quiser include vários valores em seu filtro, poderá adicionar a seguinte linha para cada valor adicional:

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

Uma maneira mais simples é atravessar os dados e ocultar as linhas com a propriedade 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(); 

Apenas uma ideia ... funciona para mim.

Você poderia criar um object DataView a partir da sua fonte de dados. Isso permitiria filtrar e classificar seus dados sem modificar diretamente a fonte de dados.

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

// “Comment” Filtra o datagrid sem alterar o dataset, 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 }; 

Eu tenho uma proposta mais clara na pesquisa 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); } } 

Eu encontrei uma maneira simples de corrigir esse problema. Na binding datagridview você acabou de fazer: datagridview.DataSource = dataSetName.Tables["TableName"];

Se você codifica como:

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

o datagridview nunca carregará dados novamente ao filtrar.