Rozwijam kontrolę użytkownika w C # Visual Studio 2010 - rodzaj pola tekstowego „szybkiego wyszukiwania” do filtrowania datagridview. Powinien działać dla 3 typów źródeł danych datagridview: DataTable, DataBinding i DataSet. Mój problem polega na filtrowaniu DataTable z obiektu DataSet, który jest wyświetlany w DataGridView.
Mogą być 3 przypadki (przykłady dla standardowej aplikacji WinForm z DataGridView i TextBox) - pierwsze 2 działają OK, mam problem z trzecim:
1. datagridview.DataSource = dataTable: to działa,
więc mogę filtrować przez ustawienie: dataTable.DefaultView.RowFilter = "kraj 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: działa,
więc mogę filtrować przez ustawienie: 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": to nie działa
Dzieje się tak, gdy projektujesz tabelę za pomocą projektanta: umieść DataSet z toolboxa w formularzu, dodaj do niego DataTable, a następnie ustaw datagridview.DataSource = dataSource; i datagridview.DataMember = "TableName".
Kod poniżej udaje te operacje:
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());
}
Jeśli to przetestujesz - mimo że datatable jest filtrowane (zmiany ds.Tables [0] .DefaultView.Count), datagridview nie jest aktualizowany ... Długo szukałem rozwiązania, ale problem polega na tym, że DataSource nie może zmiana - ponieważ jest to dodatkowa kontrola, nie chcę, żeby zepsuła kod programisty.
Wiem, że możliwe rozwiązania to:
- powiązanie DataTable z DataSet za pomocą DataBinding i użycie go jako przykładu 2: ale to programista podczas pisania kodu,
- zmiana dataSource na BindingSource, dataGridView.DataSource = dataSet.Tables [0] lub do DefaultView programowo: jednak zmienia DataSource. Więc rozwiązanie:
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());
}
jest nie do przyjęcia, jak widać na MessageBox's dataSource się zmienia ...
Nie chcę tego robić, ponieważ jest możliwe, że programista napisze kod podobny do tego:
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
}
Może to zrobić, ponieważ zaprojektował DataGridView z DataSet i DataMember w projektancie. Kod zostanie skompilowany, jednak po zastosowaniu filtru wyrzuci wyjątek ...
Zatem pytanie brzmi: jak mogę filtrować DataTable w DataSet i wyświetlać wyniki w DataGridView bez zmiany DataSource na inny? Dlaczego mogę bezpośrednio filtrować DataTable z przykładu 1, podczas gdy filtrowanie DataTable z DataSet nie działa? Może w takim przypadku nie jest to DataTable powiązany z DataGridView?
Zwróć uwagę, że mój problem bierze się z projektowania, więc rozwiązanie MUSI DZIAŁAĆ na przykładzie 3.
Odpowiedzi:
Właśnie spędziłem godzinę nad podobnym problemem. Dla mnie odpowiedź okazała się żenująco prosta.
(dataGridViewFields.DataSource as DataTable).DefaultView.RowFilter = string.Format("Field = '{0}'", textBoxFilter.Text);
źródło
IBindingListView
zgodnie z msdn.microsoft.com/en-us/library/ ...Object reference not set to an instance of an object.
dla GridView.Opracowałem ogólne stwierdzenie, aby zastosować filtr:
string rowFilter = string.Format("[{0}] = '{1}'", columnName, filterValue); (myDataGridView.DataSource as DataTable).DefaultView.RowFilter = rowFilter;
Nawiasy kwadratowe dopuszczają spacje w nazwie kolumny.
Ponadto, jeśli chcesz uwzględnić wiele wartości w filtrze, możesz dodać następujący wiersz dla każdej dodatkowej wartości:
rowFilter += string.Format(" OR [{0}] = '{1}'", columnName, additionalFilterValue);
źródło
Prostszym sposobem jest przecięcie danych i ukrycie linii z
Visible
właściwością.// 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();
Tylko pomysł ... to działa dla mnie.
źródło
DataGridView
, działało to idealnie. :) Chociaż użyłemforeach
i bezpośrednio przypisanerow.Visible = showAll || <condition>;
bez żadnegoif
. ToshowAll
prawda, jeśli ciąg filtru jest pusty.Możesz utworzyć obiekt DataView na podstawie źródła danych. Umożliwiłoby to filtrowanie i sortowanie danych bez bezpośredniej modyfikacji źródła danych.
Pamiętaj też, aby zadzwonić
dataGridView1.DataBind();
po ustawieniu źródła danych.źródło
// "Komentarz" Filtruj datagrid bez zmiany zbioru danych. Działa doskonale.
(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 };
źródło
Mam jaśniejszą propozycję dotyczącą automatycznego wyszukiwania w DataGridView
to jest przykład
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); } }
źródło
Znalazłem prosty sposób na rozwiązanie tego problemu. Przy wiązaniu datagridview właśnie zrobiłeś:
datagridview.DataSource = dataSetName.Tables["TableName"];
Jeśli kodujesz jak:
datagridview.DataSource = dataSetName; datagridview.DataMember = "TableName";
datagridview nigdy więcej nie załaduje danych podczas filtrowania.
źródło