问题
I am newbie in C# and probably this might be a possible duplicate but am unable to do filter of records on datagrid view whose columns were designed from designer view on TextBox change event.
This is my code to populate the datagridview. Any help will be highly appreciated.
IList<ProductEntity> emp = HibernateDao.fetchDAta();
IList<ProductModel> products = new List<ProductModel>();
foreach(ProductEntity e in emp)
{
dataGridView1.Rows.Add("" + e.id, e.barcode, e.product_name, e.product_desc, e.quantity + " " + e.units,""+e.buying_price,e.retail_selling_price,e.can_sell_whole_sale,e.whole_selling_price);
}
And this is the code that have tried to do filter which is not working at all
BindingSource bs=new BindingSource();
private void metroTextBox1_TextChanged(object sender, EventArgs e)
{
if (metroTextBox1.Text == string.Empty)
{
bs.RemoveFilter();
}
else
{
bs.Filter = string.Format("product_name LIKE '*{0}*'", metroTextBox1.Text);
}
}
and is the datagrid view designed from designer that i need to filter
回答1:
Consider something more like this:
IList<ProductEntity> emp = HibernateDao.fetchDAta();
//seems no-op, did you miss a mapping call? : IList<ProductModel> products = new List<ProductModel>();
BindingSource bs = new BindingSource();
bs.DataSource = emp; //emp will need to be a bindable list; does fetchData do a ToList() ?
bs.Filter = "[product_name] = 'cornflakes'";
dataGridView1.Columns.Clear();
dataGridView1.AutoGenerateColumns = true;
dataGridView1.DataSource = bs;
You can't get DataGridView to show two properties in a single column, so e.quantity + " " + e.units
type things won't work out. You'll have to add a property (not field) to ProductModel, that returns those two values as a single string
Databinding isn't a complicated thing. In some senses you're already doing part of it. That loop you had that iterated the data model and stuffed everything into the grid - databinding will do that for you, if you just set the datasource of the grid to a list that supports binding then the grid will create one row per list item (and in autogeneratecolumns mode it will make one column per simple property it finds). The more trick aspect of binding is that when you change the model (and you should change the data by changing the model, not programmatically altering the grid cells), the grid updates to reflect the changes
It helps achieve the separation of concerns that model/view/controller represents
Edit:
If HibernateDao.fetchDAta()
returns something that doesn't implement the IBindingListView interface you could consider:
To install the nuget package
morelinq
; it has methods to turn lists of objects into datatables, and datatables fully support filtering, ORTo install the nuget package
System.Linq.Dynamic
; it can allow you to query things with linq by writing string expressions
Make a new project and completely replace the contents of Form1.vb with this demo:
Imports System.Linq.Dynamic
Imports MoreLinq
Public Class Form1
Class Person
Property Name As String
Property Age As Integer
End Class
Private dgvDL As New DataGridView
Private dgvBLV As New DataGridView
Private tb As New TextBox
Private people As List(Of Person)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim names() = {"John", "James"}
people = names.Select(Function(x) New Person With {.Name = x, .Age = x.Length * 7}).ToList()
'dynamic linq dgv
dgvDL.DataSource = people
Me.Controls.Add(dgvDL)
'morelinq DGV
Dim bs As New BindingSource
bs.DataSource = people.ToDataTable()
dgvBLV.DataSource = bs
Me.Controls.Add(dgvBLV)
'irrelevant UI code
AddHandler tb.TextChanged, AddressOf TextChange
Me.Controls.Add(tb)
dgvBLV.Location = New Point(dgvDL.Right, dgvDL.Top) 'place adjacent to above grid
tb.Location = New Point(dgvDL.Left, dgvDL.Height)
End Sub
Private Sub TextChange(sender As Object, e As EventArgs)
If (tb.Text = "") Then
dgvDL.DataSource = people 'filter DL grid
DirectCast(dgvBLV.DataSource, BindingSource).RemoveFilter() 'filter BLV grid
Else
dgvDL.DataSource = people.Where("Name == @0", tb.Text).ToList() 'filter DL grid
DirectCast(dgvBLV.DataSource, BindingSource).Filter = $"[Name] = '{tb.Text}'" 'filter BLV grid
End If
End Sub
End Class
Note, if you're prepared to filter things using LINQ that doesnt use a built string filter, you don't need either of these helper libs; you can just use normal LINQ - see in the TextChange
method I say .Where("Name == @0", tb.Text)
- because Name
is hardcoded here, this could just as easily be .Where(Function(x) x.Name = tb.Text)
. Dynamic LINQ only really makes your life a bit easier if you're constructing the left side too
--
Edit
C# version, not a huge departure from the VB. Not sure why my brain slipped into VB.NET, but I've left the VB above for you to compare with this, as a "that's how to read VB".. The two languages are nearly the same these days, but for a couple of keywords and semicolons, and it's always handy to know how to read other flavors of .NET:
using System.Linq.Dynamic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System;
using System.Collections.Generic;
using MoreLinq;
namespace WindowsFormsApp1
{
public partial class Form1: Form
{
public Form1()
{
base.Load += Form1_Load;
}
public partial class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
private DataGridView dgvDL = new DataGridView();
private DataGridView dgvBLV = new DataGridView();
private TextBox tb = new TextBox();
private List<Person> people;
private void Form1_Load(object sender, EventArgs e)
{
var names = new string[] { "John", "James" };
people = names.Select(x => new Person() { Name = x, Age = x.Length * 7 }).ToList();
// dynamic linq dgv
dgvDL.DataSource = people;
this.Controls.Add(dgvDL);
// morelinq DGV
var bs = new BindingSource();
bs.DataSource = people.ToDataTable();
dgvBLV.DataSource = bs;
this.Controls.Add(dgvBLV);
// irrelevant UI code
this.tb.TextChanged += TextChange;
this.Controls.Add(tb);
dgvBLV.Location = new Point(dgvDL.Right, dgvDL.Top); // place adjacent to above grid
tb.Location = new Point(dgvDL.Left, dgvDL.Height);
}
private void TextChange(object sender, EventArgs e)
{
if (tb.Text == "")
{
dgvDL.DataSource = people; // filter DL grid
((BindingSource)dgvBLV.DataSource).RemoveFilter(); // filter BLV grid
}
else
{
dgvDL.DataSource = people.Where("Name == @0", tb.Text).ToList(); // filter DL grid
((BindingSource)dgvBLV.DataSource).Filter = $"[Name] = '{tb.Text}'";
} // filter BLV grid
}
}
}
来源:https://stackoverflow.com/questions/63245000/filter-datagrid-view-from-data-populated-from-entity-in-c-sharp