Filtering BindingList - c#

In C# WinForms I have two DataGrids displaying tabular data. First display all rows, second one is supposed to display filtered set of these rows. I need to build filtered view on my BindingList view instance. This view needs to be updated, once underlying list is changed.
I've tried to build new BindingList instance with LINQ and Where
but the filtered is not updated when underlying myList is changed.
var filtered = new BindingList<Clip>(myList.Where<Clip>
(
c => c.participant.Contains(id)
).ToList<Clip>());
How can I do this?
Thanks

BindingList<T> doesn't support filtering (not directly, at least), so I propose to substitute your BindingList with a DataTable. DataView supports filtering and the filtered data is just a custom subset of the same DataTable.
In the example, two BindingSource classes are use to bind the same DataTable to two DataGridView controls.
One of the BindingSource class is bound to a filtered DataView of the DataTable, using the DataView.RowFilter property, an Expression which accepts a subset of SQL-like commands.
Here, the second DataGridView.DataSource is set to the BindingSource that has its DataSource linked to the filtered DataView.
The filter is defined using a specific value ("Value A1") of the second Column (Column1).
You can see in the visual sample that the two DataGridView control update their cells values when the cells' values of both the DataGridViews are changed.
Also, the filter is active on the second DataGridView: the rows are filtered when the filtered Column(s) values change.
To test this behaviour, add 2 DataGridView controls to a Form, add a Button (here, named btnBind) and subscribe to the Click event with the btnBind_Click handler.
private BindingSource dgvBindingSource1 = null;
private BindingSource dgvBindingSource2 = null;
private DataTable dt = null;
private void btnBind_Click(object sender, EventArgs e)
{
FillData(3, 3);
dgvBindingSource1 = new BindingSource(dt, null);
DataView dv = dt.AsDataView();
dv.RowFilter = "Column1 = 'Value A1'";
dgvBindingSource2 = new BindingSource(dv, null);
dataGridView1.DataSource = dgvBindingSource1;
dataGridView2.DataSource = dgvBindingSource2;
}
private void FillData(int cols, int rows)
{
dt = new DataTable("TestTable");
dt.Columns.AddRange(Enumerable.Range(0, cols)
.Select(i => new DataColumn("Column" + i.ToString(), typeof(string))).ToArray());
for (int r = 0; r < rows; r++) {
dt.Rows.Add(Enumerable.Range(0, cols)
.Select(n => $"Value {(char)('A' + r)}" + n.ToString()).ToArray());
}
}

Related

c# combobox DisplayMember showing empty rows from datatable

I have a multi column datatable like this,
datatable
I'm loading it to a combobox.
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
datatable osdat = Loaddatatable();
postOScomboBox2.DataSource = osdat;
postOScomboBox2.DisplayMember = "Product";
postOScomboBox2.ValueMember = "Product";
postOScomboBox2.SelectedIndex = -1;
}
Instead of just showing p1,p2 the combobox is also showing two extra empty rows.
Same happens if i load the p1 column, 1 empty row is shown in combobox. Does display member have any property to check empty values and load only filled ones or any other way to achieve the same?
You can filter your table using methods from System.Linq.
datatable osdat = Loaddatatable();
var filteredTable = osdat.AsEnumerable()
.Where(row => row.Field<String>("Product") != null).CopyToDataTable();
postOScomboBox2.DataSource = filteredTable;
postOScomboBox2.DisplayMember = "Product";
postOScomboBox2.ValueMember = "Product";
postOScomboBox2.SelectedIndex = -1;
You have two empty values in product column and one empty value in p1 column. Combobox works as expected. If you want to filter these values filter your datasource instead. it's not a responsibility of combobox to filter datasource.
Here is a link how to filter datatable from empty values:
Filtering a empty string in DataTable

database connection to datagridview's row only

I Have requirement to fill the datagridview row by row . i.e. if 3rd row is currently selected then data needs to be filled in 3rd row (my query returns always single row ) . same for every row.
some what like
DataTable dt = new DataTable();
dataadapter.Fill(dt);
dataGridView1.Rows[index].DataSource = dt; (just a hunch, but not working)
(instead of ) //dataGridView1.DataSource = dt;
hope that I made my requirement clear to you
Is their any way to accomplish it ....
Thanks in advance....
If your query returns a single row then you just have to fill the columns with correct values from your query:
dataGridView1.Rows[rowIndex].Cells["ColumnName"].Value =
dt.Rows[0]["ColumnNameInDataTable"].ToString();
Your grid will not be bound to the data this way. But it will fill the columns in the current row like you've asked.
I don't think this is possible directly,
You could use a List with a certain amount of empty objects and bind this List to your DataGridView... Then you would have empty Rows to work with. You can then fill single rows of the List with data and update the binding.
I also think you need a BindingSource and not a DataTable in this case...
Something like this:
private List<YourObject> gridData; // List
private BindingSource bindingSource;
public void Init()
{
this.gridData = new List<Anything>();
//prefill list, in this case we want to have 100 empty rows in DataGrid
for (var i = 0; i < 100; i++)
{
this.gridData.Add(new YourObject());
}
this.bindingSource.DataSource = this.gridData;
}
public void UpdateRow(int row)
{
this.gridData[row] = (from .. in select ...).FirstOrDefault(); // your query
}

Trouble adding values in combobox column of datagridview c#

I have a datagridview, in this datagirdview, i added two unbound columns, one is a combobox and second is a textbox type column, besides this , I have a datatable of some rows.
I am setting gird datasource with my datatable, then I am trying to add values to combobox control, it should display three values (Truck, Plane, Ship)
when grid loads it displays stores, but no value in combobox, I tried various methods like creating a combobox data column and adding values to it by binding a datatable, I also tried by adding in column.items both in designer and by code but combobox never gets the values.
as an experiment I am also trying to add a new column by below code, but second column also show no values.
DataGridViewComboBoxColumn dgvcbc = new DataGridViewComboBoxColumn();
DataTable dt = new DataTable();
dt.Columns.Add("Media", typeof(string));
dt.Rows.Add("Truck");
dt.Rows.Add("Car");
dgvcbc.DataSource = dt;
dgvcbc.DisplayMember = "Media";
dgvcbc.ValueMember = "Media";
this.grdDestShops.Columns.Add(dgvcbc);
your code is adding a new column!, If you have added a ComboBox columns in the design mode, why not you find it using FindControl function and then bound it?
Think this solves ur problem..
namespace activator
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
DataTable dt = new DataTable();
dt.Columns.Add("Media", typeof(string));
dt.Rows.Add("Truck");
dt.Rows.Add("Car");
ComboBox combo = new ComboBox();
List<string> media=(from x in dt.AsEnumerable()
select x.Field<string>(0)).ToList();
combo.DataSource = media;
dataGridView1.Controls.Add(combo);
}
}
}

How to sort data from DataGridView with automatically generated columns from BindingList?

I have a DataGridView that receives a BindingList
dataGrid.DataSource = new BindingList<Value>(ValueList);
After it I try to set the SortMode
dataGrid.Columns.OfType<DataGridViewColumn>().ToList()
.ForEach(c =>
{
c.SortMode = DataGridViewColumnSortMode.Automatic;
});
The breakpoint does stop on it, but my DataGridView is not sortable... I try to click on the headers and nothing happens.
These columns are auto generated, what can I do to be able to sort the data?
I believe the problem is that you need to make a custom binding list that implements the necessary sorting functions so that the DataGridView knows how to sort each column.
This post provides good information on getting sorting working:
http://xiaonanstechblog.blogspot.com/2009/03/how-to-enable-column-sorting-on.html
If you want to both filter and sort, you may want to do a custom implementation of the IBindingListView interface:
http://msdn.microsoft.com/en-us/library/system.componentmodel.ibindinglistview.aspx
When DataGridView binds to the DataSource (DataView, BindingSource, Table, DataSet+"tablename") in all cases it refere to the DataView. Get reference to this DataView and set Sort (and Filter) as you wish:
DataView dv = null;
CurrencyManager cm = (CurrencyManager)(dgv.BindingContext[dgv.DataSource, dgv.DataMember]);
if (cm.List is BindingSource)
{
// In case of BindingSource it may be chain of BindingSources+relations
BindingSource bs = (BindingSource)cm.List;
while (bs.List is BindingSource)
{ bs = bs.List as BindingSource; }
if (bs.List is DataView)
{ dv = bs.List as DataView; }
}
else if (cm.List is DataView)
{
// dgv bind to the DataView, Table or DataSet+"tablename"
dv = cm.List as DataView;
}
if (dv != null)
{
dv.Sort = "somedate desc, firstname";
// dv.Filter = "lastname = 'Smith' OR lastname = 'Doe'";
// You can Set the Glyphs something like this:
int somedateColIdx = 5; // somedate
int firstnameColIdx = 3; // firstname
dgv.Columns[somedateColIdx].HeaderCell.SortGlyphDirection = SortOrder.Descending;
dgv.Columns[firstnameColIdx].HeaderCell.SortGlyphDirection = SortOrder.Ascending;
}
Note: Column names used in Sort and Filter correspond to the column names in DataTable,
Column names in DataGridView are the names of controls used to display cells in dgv.
You can get column name used in DataView like this:
string colName = dgv.Columns[colIdx].DataPropertyName
Depends of how do you want to track sorted columns (colSequence, colName, asc/desc, dgvColIdx) you may decide how to build Sort and Filter expression and set the SortGlyph in dgv (I made hardcode for simplicity).

DataView.Count different DataView.Table.Rows.Count

I am using C# and .NET 3.5 and have a GridView that I am setting the dataSource programatically in the code-behind page. I have data in a DataTable and then depending on a column value (isValid boolean) of each Row, I create a new row using DataRowView.AddNew() method into 1 of 2 DataViews - dvValid or dvInvalid. I am NOT creating a new DataTable.NewRow to add to the DataView Table. Then I bind the GridView to the appropriate dataView.
There is a problem when I am sorting the GridView. I am having a problem with 1 row not being sorted correctly, all other rows are sorted fine. I debugged my code and found that the DataView.Count is 1 more than the DataView.Table.Rows.Count even though I am calling DataView.Table.AcceptChanges() method. This is strange since the dataTable should have all committed rows and therefore the counts should be the same.
So why are the 2 counts different? A DataView is a subset of the DataTable so should it not have equal or less rows than the DataTable.
When I populate the DataView, should I first create the DataTables rather than creating the DataView directly? Right now, I am directly creating a DataRowView without a dDataTableRow, is this the correct approach?
Thanks for your help.
Code snippet : C#
...
//get the data as DataTable
members = GetMemberDataTable ();
//create views from a new DataTable with no rows
dvValidMembers = new DataView (CreateMembersDT("ValidMembers"));
dvInValidMembers = new DataView (CreateMembersDT("InvalidMembers"));
//iterate thru each row and put into appropriate DataView
foreach (DataRow memberRow in members.Rows)
{
if ((bool)memberRow["isValid"])
//Add to valid members Dview
member = dvValidMembers.AddNew();
else
//add to InValid members Dview
member = dvInvalidMembers.AddNew();
member["memberID"] = memberRow["memID"];
} //foreach
dvInvalidMembers.Table.AcceptChanges();
dvValidMembers.Table.AcceptChanges();
}
private System.Data.DataTable CreateMembersDT ( string tableName)
{
System.Data.DataTable dtMembers = new System.Data.DataTable(tableName);
dtMembers.Columns.Add(new DataColumn("memID", typeof(int)));
return dtMembers;
}
That 1 row that isn't sorting right, could that be the last row?
I think you are missing a DataView.EndEdit():
foreach (DataRow memberRow in members.Rows)
{
DataView dv;
if (...)
//Add to valid members Dview
dv = dvValidMembers;
else
dv = dvInvalidMembers;
member = dv.Addnew();
member["memberID"] = memberRow["memID"];
dv.EndEdit();
}
But I would also like to note that you could probably use 2 Views with a Filter on isValid and then you would only need to point them to the original members table.

Categories