C# 滤波器命令设计方法
这是我的应用程序的UI。它是一个基于WPF的应用程序,可以在设备之间进行分析(类似于Wireshark) 绑定到DataGrid的类如下(除Error和FuncType外的所有内容都绑定到该网格:) 基本上,我正在尝试设计一些东西,用户可以输入特定的过滤命令,并且只显示符合条件的行。以下是一些示例(不带引号)C# 滤波器命令设计方法,c#,.net,wpf,parsing,command,C#,.net,Wpf,Parsing,Command,这是我的应用程序的UI。它是一个基于WPF的应用程序,可以在设备之间进行分析(类似于Wireshark) 绑定到DataGrid的类如下(除Error和FuncType外的所有内容都绑定到该网格:) 基本上,我正在尝试设计一些东西,用户可以输入特定的过滤命令,并且只显示符合条件的行。以下是一些示例(不带引号) 输入“Error”应该只显示Error属性设置为true的数据行 输入“source==someipaddress”应该只显示匹配的IP地址 输入“数字>100”应仅显示数字大于100的行
任何指导都将不胜感激。单击
Start
后,您应该为网格绑定到的集合(比如设备
)引发notify change事件。设备的getter
将应用过滤器并仅返回过滤后的记录
void OnStartClicked(object sender, EventArgs e)
{
NotifyPropertyChanged("Devices");
}
public IEnumberable<Device> Devices
{
get
{
if(string.IsNullOrEmpty(Filter)) return _devices;
return _devices.Where(d => d.Satisfies(Filter));
// or
return _devices.Where(d => _filter.Satisfies(d, Filter));
}
}
void on开始单击(对象发送方,事件参数e)
{
NotifyPropertyChanged(“设备”);
}
公共可编号设备
{
得到
{
if(string.IsNullOrEmpty(Filter))返回_设备;
返回_设备,其中(d=>d.满足(过滤器));
//或
返回_devices.Where(d=>_filter.conferences(d,filter));
}
}
在设备
类或某些过滤类中,您将定义满足
方法,该方法将按照您希望的方式工作
对于解析过滤器,您应该提供一些语法(ANTLR),或者您可以使用Roslyn项目将字符串转换为C#代码。如果你只有有限数量的非常简单的过滤器,你可以很容易地使用正则表达式。我个人不喜欢你描述的方法 若您想在过滤器字符串中使用类似SQL(或LINQ)的语法,那个么您必须让用户知道该语法。。。特别是,如果您不能很好地使用SQL(LINQ)语法,这将是一件痛苦的事情 我认为,将expander放在网格上,网格将包含好的旧控件来设置过滤,然后动态构建相应的谓词来过滤代码中的数据,这样会更好 但是,如果您决定以这种方式进行过滤,那么您的任务可以归结为“如何从字符串中获取LINQ表达式”。此链接将帮助您:
10000英尺概述 是通过将处理程序附加到事件来完成的,因此实际的问题是如何从用户输入创建一个事件。这个问题可以通过表达式树以一种强大的方式解决 我不打算详细讨论如何解析输入字符串,因为它可以从非常简单到非常复杂;一种强大的方法是使用像ANTR这样的工具将输入解析为抽象语法树 我将展示的是,在输入被解析并且用户意图已知的情况下,如何创建过滤器(在解析过程中动态创建过滤器也很容易) 假设用户为过滤器输入了“Number>10”。如果此过滤器是硬编码的,
FilterEventHandler
将如下所示:
public void CustomFilterHandler(object sender, FilterEventArgs e)
{
CommDGDataSource source = (CommDGDataSource)e.Item;
return source.Number > 10;
}
var predicate = Expression.GreaterThan(
Expression.MakeMemberAccess(variable, sourceType.GetProperty("Number")),
Expression.Constant(10));
// Maps operators to Expression factory methods
var dict = new Dictionary<string, Func<Expression, Expression, BinaryExpression>>
{
{ "==", Expression.Equal },
{ ">", Expression.GreaterThan },
{ "<", Expression.LessThan },
// etc
};
var predicate = Expression.Constant(true); // by default accept all rows
// Logical AND everything in parseResult to construct the final predicate
foreach (parseResult in parsedUserInput)
{
var exprConstructor = dict[parseResult.Operator];
var property = sourceType.GetProperty(parseResult.PropertyName);
var target = Expression.MakeMemberAccess(variable, property);
var additionalTest = exprConstructor(target, Expression.Constant(parseResult.Value));
predicate = Expression.AndAlso(predicate, additionalTest);
}
动态构造筛选事件处理程序
我们要做的是使用表达式树动态构造这个方法。让我们从序言开始:
var sourceType = typeof(CommDGDataSource);
var eventArgsType = typeof(System.Windows.Data.FilterEventArgs);
我们将要构造一个有两个和一个的。让我们先构造参数:
var parameters = new[] {
Expression.Parameter(typeof(object), "sender"),
Expression.Parameter(eventArgsType, "e"),
};
尸体将是一个(严格来说这不是必要的,但以后可能会派上用场)。BlockExpression
使用多个,并由任意多个组成;非常重要的是,最后一个将是块的返回值
上面给出的模拟事件处理程序的第一行告诉我们需要一个变量:
var variable = Expression.Variable(sourceType, "source");
我们肯定需要一个产生返回值的谓词:
var predicate = Expression.GreaterThan(
Expression.MakeMemberAccess(variable, sourceType.GetProperty("Number")),
Expression.Constant(10));
我们现在已经准备好生成BlockExpression
的主体。正文将需要访问事件处理程序的第二个参数(parameters[1]
)的Item
属性,并将其强制转换为sourceType
,因为FilterEventArgs.Item
属于对象类型,所以我们不能直接使用它。强制转换的结果应存储在变量
中,然后谓词
将执行测试:
// Some intermediate variables to cut down on the line length
var itemProperty = eventArgsType.GetProperty("Item");
var itemAccessExpression = Expression.MakeMemberAccess(parameters[1], itemProperty);
var castItemToCorrectType = Expression.TypeAs(itemAccessExpression, sourceType);
// And the body is comprised of these two expressions:
var body = new[] { Expression.Assign(variable, castItemToCorrectType), predicate };
由于谓词
是主体的最后一个表达式,因此它也将生成其返回值
现在我们可以构造块表达式,最后是过滤lambda表达式本身:
var block = Expression.Block(new[] { variable }, body);
var filter = Expression.Lambda<Func<object, FilterEventArgs, bool>>(block, parameters);
构造复杂滤波器
使用这种方法的另一个原因是扩展过滤逻辑非常容易。例如,很容易想象,与其像这样硬连接谓词,不如:
public void CustomFilterHandler(object sender, FilterEventArgs e)
{
CommDGDataSource source = (CommDGDataSource)e.Item;
return source.Number > 10;
}
var predicate = Expression.GreaterThan(
Expression.MakeMemberAccess(variable, sourceType.GetProperty("Number")),
Expression.Constant(10));
// Maps operators to Expression factory methods
var dict = new Dictionary<string, Func<Expression, Expression, BinaryExpression>>
{
{ "==", Expression.Equal },
{ ">", Expression.GreaterThan },
{ "<", Expression.LessThan },
// etc
};
var predicate = Expression.Constant(true); // by default accept all rows
// Logical AND everything in parseResult to construct the final predicate
foreach (parseResult in parsedUserInput)
{
var exprConstructor = dict[parseResult.Operator];
var property = sourceType.GetProperty(parseResult.PropertyName);
var target = Expression.MakeMemberAccess(variable, property);
var additionalTest = exprConstructor(target, Expression.Constant(parseResult.Value));
predicate = Expression.AndAlso(predicate, additionalTest);
}
我们可以从用户输入构建它,如下所示:
public void CustomFilterHandler(object sender, FilterEventArgs e)
{
CommDGDataSource source = (CommDGDataSource)e.Item;
return source.Number > 10;
}
var predicate = Expression.GreaterThan(
Expression.MakeMemberAccess(variable, sourceType.GetProperty("Number")),
Expression.Constant(10));
// Maps operators to Expression factory methods
var dict = new Dictionary<string, Func<Expression, Expression, BinaryExpression>>
{
{ "==", Expression.Equal },
{ ">", Expression.GreaterThan },
{ "<", Expression.LessThan },
// etc
};
var predicate = Expression.Constant(true); // by default accept all rows
// Logical AND everything in parseResult to construct the final predicate
foreach (parseResult in parsedUserInput)
{
var exprConstructor = dict[parseResult.Operator];
var property = sourceType.GetProperty(parseResult.PropertyName);
var target = Expression.MakeMemberAccess(variable, property);
var additionalTest = exprConstructor(target, Expression.Constant(parseResult.Value));
predicate = Expression.AndAlso(predicate, additionalTest);
}
//将运算符映射到表达式工厂方法
var dict=新字典
{
{“==”,表达式.Equal},
{“>”,Expression.GreaterThan},
{ "这是一个坏主意-过滤数据源本身。WPF有极好的收集视图概念,topicstarter的做法是正确的。如果你过滤数据源,而不是它的视图,你就失去了用不同视图表示相同数据的可能性。是的,我认为这也不是一个好主意,但我的客户特别要求不幸的是,对于上述方法,我只能接受:(例外的回答。非常感谢您的努力。除了最后一部分,我觉得这很好。collectionViewSource.Filter+