Фильтрация DataGridView без изменения источника данных
Я разрабатываю пользовательский элемент управления в C# Visual Studio 2010 - своего рода текстовое поле "быстрого поиска" для фильтрации просмотра данных. Он должен работать для 3 типов источников данных в виде сетки: 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 = 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());
}
Если вы протестируете его - хотя datatridView не фильтруется (хотя datatable [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, источник данных меняется...
Я не хочу этого делать, потому что программист может написать код, подобный следующему:
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.
8 ответов
Я просто потратил час на подобную проблему. Для меня ответ оказался смущающим простым.
(dataGridViewFields.DataSource as DataTable).DefaultView.RowFilter = string.Format("Field = '{0}'", textBoxFilter.Text);
Я разработал общее утверждение для применения фильтра:
string rowFilter = string.Format("[{0}] = '{1}'", columnName, filterValue);
(myDataGridView.DataSource as DataTable).DefaultView.RowFilter = rowFilter;
Квадратные скобки учитывают пробелы в имени столбца.
Кроме того, если вы хотите включить несколько значений в свой фильтр, вы можете добавить следующую строку для каждого дополнительного значения:
rowFilter += string.Format(" OR [{0}] = '{1}'", columnName, additionalFilterValue);
Более простой способ состоит в том, чтобы переместить данные и скрыть строки с 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 не относится к типу DataTable, но если это так, попробуйте снова назначить таблицу данных источнику данных DataGridView. В моем случае я назначил таблицу данных DataGridView в FormLoad (), и когда я пишу этот код
(dataGridViewFields.DataSource as DataTable).DefaultView.RowFilter = string.Format("Field = '{0}'", textBoxFilter.Text);
это давало мне ошибку, о которой я упоминал выше. Итак, я снова переназначил таблицу данных для dgv. Итак, код был примерно таким
dataGridViewFields.DataSource = Dt;
(dataGridViewFields.DataSource as DataTable).DefaultView.RowFilter = string.Format("Field = '{0}'", textBoxFilter.Text);
И это сработало.
Вы можете создать объект DataView из своего источника данных. Это позволит вам фильтровать и сортировать данные без непосредственного изменения источника данных.
Кроме того, не забудьте позвонить dataGridView1.DataBind();
после того, как вы установите источник данных.
//"Комментарий" Фильтрует сетку данных без изменения набора данных, отлично работает.
(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
};
У меня есть более четкое предложение по автоматическому поиску в 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);
}
}
Я нашел простой способ решить эту проблему. При связывании datagridview вы только что сделали: datagridview.DataSource = dataSetName.Tables["TableName"];
Если вы код как:
datagridview.DataSource = dataSetName;
datagridview.DataMember = "TableName";
datagridview никогда не загрузит данные снова при фильтрации.