C# 滤波器命令设计方法

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的行

这是我的应用程序的UI。它是一个基于WPF的应用程序,可以在设备之间进行分析(类似于Wireshark)

绑定到DataGrid的类如下(除Error和FuncType外的所有内容都绑定到该网格:)

基本上,我正在尝试设计一些东西,用户可以输入特定的过滤命令,并且只显示符合条件的行。以下是一些示例(不带引号)

  • 输入“Error”应该只显示Error属性设置为true的数据行

  • 输入“source==someipaddress”应该只显示匹配的IP地址

  • 输入“数字>100”应仅显示数字大于100的行

  • 如果用逗号分隔,则应应用多个条件。(错误,源==someipaddress)

  • 我已经为绑定数据创建了ICollectionView来处理过滤,但我不确定应该采取什么方法来解析命令并正确处理过滤以满足上述要求


    任何指导都将不胜感激。

    单击
    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+