Filtering list of objects in datagridview based on cell value

喜夏-厌秋 提交于 2021-02-05 05:23:24

问题


I am currently unsure on the best way of going about getting a filter to work right on a datagridview that has its datasource set to a list of objects.

So given an object of:

    public class DepositAccountBill
    {
        #region Properties
        public int AccountBillID { get; set; }
        public int AccountID { get; set; }
        public string AccountNumber { get; set; }
        public string ControlNumber { get; set; }
        public DateTime BillDate { get; set; }
        public decimal DepositAmount { get; set; }
}

I have a datagridview table that looks roughly like this:

Account Number  |  Control Number  | Bill Date  |   Deposit Amount 
==================================================================
123456          | AJA1234367       | 5/21/2018  | 12.99 
123456          | PSA1234367       | 5/21/2018  | 5.77 
567332          | HBA1234367       | 5/21/2018  | 1255.99 
769843          | AJA1234367       | 5/21/2018  | 12.99 

So when clicking on a cell. Lets say the first cell on the first column. If I right click a cell and select an option in a context Menu that says filter I need to display the datagridview table with only rows that have the same Account number. In this case, it would be rows 1 and 2. In order for me to do this, I have to access the object that the datagridview table is populated with DepositAccountBill. So what I need to do is look and at the column I'm looking at has the selected cell's value.

So in my method I have tried thus far with no results:

var collection = (List<DepositAccountBill>)dataGridView1.DataSource;
var filterList = collection.Where ( q => (collection.Select(r => GetPropValue(r, dataGridView1.Columns[clickedCell.ColumnIndex].DataPropertyName))) == (clickedCell.Value);

dataGridView1.DataSource = filterList.ToList();

public object GetPropValue(object obj, string propName)
{
    return obj.GetType().GetProperty(propName).GetValue(obj, null);
}

I don't know if SELECT is the right LINQ method to use here or if this is even possible. I want to use WHERE because it only grabs the list of objects that match the condition. Something like this:

var filterList = collection.Where(r => r.AccountNumber == clickedCell.Value); 

Only problem is the r.AccountNumber is dependant on the data property of the selected column. The program does not know what the data property is based on a click event on the selected cell. This is why I think reflection might be necessary.


回答1:


I suppose you know how to extract the property name using DataPropertyName property of the column and also how to get the value from Value property of the cell.

In the answer I'll focus on how to perform dynamic filtering a List<T> having Property Name and the Value, like when you use .Where(x=>x.PropertyName == Value).

To do so, you have a couple of options which you can choose, including:

  1. Dynamic Linq Library
  2. Creating Expression dynamically at run-time
  3. Using a Dictionary containing criteria that you need for each property
  4. Simply using if/else

I'll share more details about above solutions and you can choose either of them.


Note: If the answer is too long, probably the easiest and most obvious answer would be 4th option.


Option 1 - Dynamic Linq Library

As an option you can add System.Linq.Dynamic reference to your project, then after using System.Linq.Dynamic namespace, you will be able to create dynamic queries on IEnumerable<T> or IQueryable<T> by passing criteria as string, for example:

var list = db.Products.ToList();
var result = list.Where("Name = @0", "product1").ToList();

This way you can simply use the column data property name and column value in the query dynamically. To find more about dynamic linq, take a look at following resources:

  • NuGet package for System.Linq.Dynamic. You can install the package simply using this command in package manager console: Install-Package System.Linq.Dynamic

  • GitHub repository for System.Linq.Dynamic

  • Scott Guthrie's blog post about Dynamic LINQ (Part 1: Using the LINQ Dynamic Query Library)

Option 2 - Create Lambda Expression at run-time

As another option you can create lambda expression at run-time and use it with Where method. For example, you can create the following method:

//Creates x=>x.Something == value
public Expression<Func<T, bool>> EqualCriteria<T>(string propertyName, object value)
{
    var property = typeof(T).GetProperty(propertyName);
    var x = Expression.Parameter(typeof(T), "x");
    var propertyExpression = Expression.Property(x, property.Name);
    var valueExpression = Expression.Convert(Expression.Constant(value),
        property.PropertyType);
    var criteria = Expression.Equal(propertyExpression, valueExpression);
    var lambda = Expression.Lambda<Func<T, bool>>(criteria, x);
    return lambda;
}

And then use it this way:

var list = db.Products.ToList();
var result = list.Where(EqualCriteria<Product>("Name", "product1").Compile()).ToList();

As you can see, also in this solution, you can use column data property name and value dynamically.

Option 3 - Using predefined criteria in a dictionary

As another option you can create a dictionary containing the criteria which you need, then use them dynamically. For example let's say you've created the following dictionary:

var criterias = new Dictionary<string, Func<Product, object, bool>>() {
    { "Id" , (p,v)=>p.Id.Equals(v) },
    { "Name" , (p,v)=>p.Name.Equals(v) }
};

Then based on the column data property name and cell value, you can simply use the following statement to filter the list:

var list = db.Products.ToList();
var result = list.Where(x => criterias["Name"](x, "product1")).ToList();

Option 4 - Using if/else

As another option you can use if/else conditions simply. For example let's say you've the value in an object variable and property name in a string variable, then you can write:

var list = db.Products.ToList();
var result = list.ToList();
if (propertyName == "Id")
{
    result = result.Where(x=>x.Id == (int)value).ToList();
}
else if (propertyName == "Name")
{
    result = result.Where(x=>x.Name == (string)value).ToList();
}


来源:https://stackoverflow.com/questions/50473727/filtering-list-of-objects-in-datagridview-based-on-cell-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!