Im trying to apply a filter to a DataGrid in WPF and the filter property needs a Predicate
ex:
dataGrid1.Filter = p => p.A_Field_Table1.Contains(textBox.Text);
But my datagrid is being filled with reflection, so I only know the type of objects inside the datagrid at runtime.
Then I created a method that dynamically generates the Predicate< T > :
public static Predicate< T > GetPredicate< T >(string column, string valueP, T objSource, string table)
{
Type itemType = typeof(T);
ParameterExpression predParam = Expression.Parameter(itemType, "p");
Expression left = Expression.Property(predParam, itemType.GetProperty("A_" + column+ "_" + table));
var valueStr= Expression.Constant(valueP);
var typeOfStr = valueStr.Type;
var containsMethod = typeOfStr.GetMethod("Contains", new [] { typeof(string) });
var call = Expression.Call(left, containsMethod, valueStr);
Func< T, bool > function = (Func< T, bool >)Expression.Lambda(call, new[] { predParam }).Compile();
return new Predicate< T >(function);
}
Then call this function on the interface:
var dataGridItem = dataGrid.Items[0];
dataGrid1.Filter = Class_X.GetPredicate(columnRefName,textBox.Text,dataGridItem,tableRefName);
But the generic method is throwing an exception saying that the type T is type of "object", even if objSource is type of Model.TableName.
I read some tutorials saying that T could not be resolved at runtime, then I should use "dynamic" instead of generic types.
I already tried using the "dynamic" type but I get a exception while casting the Lambda expression to Func< dynamic, bool>. Saying that I can't convert from < Model.TableName , bool > to < System.Object , bool >.
Is there an easier way to filter a datagrid that was filled by reflection?
You can't use generics in this case. Filter
is a Func<object, bool>
, so:
public static Predicate<object> GetPredicate(string column, string valueP, object objSource, string table)
{
Type itemType = objSource.GetType();
ParameterExpression predParam = Expression.Parameter(typeof(object), "p");
Expression left = Expression.Property(Expression.Convert(predParam, itemType), "A_" + column+ "_" + table);
var valueStr= Expression.Constant(valueP);
var typeOfStr = valueStr.Type;
var containsMethod = typeOfStr.GetMethod("Contains", new [] { typeof(string) });
var call = Expression.Call(left, containsMethod, valueStr);
//To handle null values. It considers null == string.Empty
//var left2 = Expression.Coalesce(left, Expression.Constant(string.Empty));
//var call = Expression.Call(left2, containsMethod, valueStr);
//If you want null values to be distinct from string.Empty, it's
//much more complex. You'll need a temporary variable (left2)
//where to put the value of the property, and then you can use the
//Expression.Condition (that is the ? : ternary operator) to
//test for null values
//var left2 = Expression.Variable(typeof(string));
//var assign = Expression.Assign(left2, left);
//var notNull = Expression.NotEqual(left2, Expression.Constant(null));
//var call = Expression.Call(left2, containsMethod, valueStr);
//var condition = Expression.Condition(notNull, call, Expression.Constant(false));
//var block = Expression.Block(new[] { left2 }, new Expression[] { assign, condition });
Predicate<object> function = Expression.Lambda<Predicate<object>>(call, new[] { predParam }).Compile();
return function;
}
The "trick" is that in the returned function the parameter is casted to the "right" type (obtained by objSource.GetType()
)
Be aware that you aren't testing for null
values in the property of the row ( NullReferenceException
if you try to use Contains
on a null
property)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.