none
DataGrid添加Filter的问题 RRS feed

  • 问题

  • 思路:

    DataGrid在列头添加TextBox,在TextBox的TextChanged事件中保存输入的筛选值(filterProperty)

    筛选按钮 遍历filterProperty,将筛选值读取并生成Lambda表达式,赋值给数据源 PagedCollectionView的Filter

    遇到的问题:

    单个列筛选好用,当两个列或以上时,筛选不准确,得不到正确结果;

    不知道是Lambda表达式树写的问题,还是 PagedCollectionView的Filter本身有问题,希望大家帮忙看看,万分感谢!

    后台代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Collections.ObjectModel;
    using System.Windows.Controls.Primitives;
    using System.Text;
    using System.Windows.Markup;
    using System.Windows.Data;
    using SLDemo.Lib;
    
    namespace SLDemo
    {
      public partial class Home : Page
      {
        public PagedCollectionView pcv;
        Dictionary<string, string> filterProperty;
    
        public Home()
        {
          InitializeComponent();
    
          ObservableCollection<Demo> demoCollection = new ObservableCollection<Demo>();
          demoCollection.Add(new Demo(1, "name1", DateTime.Now.AddDays(-100), 2.0M));
          demoCollection.Add(new Demo(2, "name11", DateTime.Now.AddDays(-100), 2.0M));
          demoCollection.Add(new Demo(3, "name111", DateTime.Now.AddDays(-200), 2.0M));
          demoCollection.Add(new Demo(1, "name2", DateTime.Now, 2.0M));
          demoCollection.Add(new Demo(2, "name22", DateTime.Now.AddDays(+100), 2.0M));
          demoCollection.Add(new Demo(1, "name1111111", DateTime.Now.AddDays(+100), 2.0M));
          pcv = new PagedCollectionView(demoCollection);
    
          this.dgDemo.ItemsSource = pcv;
    
          filterProperty = new Dictionary<string, string>();
    
        }
    
        // 当用户导航到此页面时执行。
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }
    
    
        private void filter_TextChanged(object sender, TextChangedEventArgs e)
        {
          SetFilterWhere(sender);
    
        }
    
        private void SetFilterWhere(object sender)
        {
          TextBox txtFilter = sender as TextBox;
          if (txtFilter != null)
          {
            //包含
            if (filterProperty.Keys.Contains(txtFilter.Name))
            {
              if (string.IsNullOrWhiteSpace(txtFilter.Text))
              {
                filterProperty.Remove(txtFilter.Name);
              }
              else
              {
                filterProperty[txtFilter.Name] = txtFilter.Text.Trim();
              }
    
            }
            //不包含
            else if (!string.IsNullOrWhiteSpace(txtFilter.Text))
            {
              filterProperty.Add(txtFilter.Name, txtFilter.Text.Trim());
    
            }
    
          }
        }
    
        #region 筛选
    
        private void btnFilter_Click(object sender, RoutedEventArgs e)
        {
          //筛选
          FirFilterMethod();
        }
    
        private void FirFilterMethod()
        {
          var predicate = PredicateBuilder.True<object>();
          foreach (var item in filterProperty)
          {
            switch (item.Key)
            {
              case "filter1":
                predicate = predicate.And(p => ((Demo)p).UID.ToString().Contains(item.Value));
                break;
              case "filter2":
                predicate = predicate.And(p => ((Demo)p).UName.Contains(item.Value));
                break;
    
            }
          }
          if (pcv != null && pcv.CanFilter)
          {
            pcv.Filter = null;
            pcv.Filter = predicate.Compile().ConvertToPredicate<object>();
          }
    
    
        }
        #endregion
    
      }
    
    
      #region 实体
      public class Demo
      {
        public Demo(int id, string name, DateTime date, decimal num)
        {
          UID = id;
          UName = name;
          Date = date;
          Num = num;
        }
        public int UID { get; set; }
        public string UName { get; set; }
        public DateTime Date { get; set; }
        public decimal Num { get; set; }
      }
    #endregion
    
      
    }
    
    XAML代码:
    
    <pre><Button Content="筛选" Height="23" Name="btnFilter" Width="75" Click="btnFilter_Click" />
            <sdk:DataGrid FrozenColumnCount="5" AutoGenerateColumns="False" Height="422" Name="dgDemo" Width="632">
              <sdk:DataGrid.Columns>
                <sdk:DataGridTextColumn Binding="{Binding UID}">
                  <sdk:DataGridTextColumn.HeaderStyle>
                    <Style TargetType="dataprimitives:DataGridColumnHeader">
                      <Setter Property="ContentTemplate">
                        <Setter.Value>
                          <DataTemplate>
                            <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" Width="Auto">
                              <TextBox x:Name="filter1" TextChanged="filter_TextChanged" VerticalAlignment="Center" Width="80" Text=""/>
                              <TextBlock TextAlignment="Center" VerticalAlignment="Center" Width="Auto" Text="ID"/>
                            </StackPanel>
                          </DataTemplate>
                        </Setter.Value>
                      </Setter>
                    </Style>
                  </sdk:DataGridTextColumn.HeaderStyle>
                </sdk:DataGridTextColumn>
                <sdk:DataGridTextColumn Binding="{Binding UName}">
                  <sdk:DataGridTextColumn.HeaderStyle>
                    <Style TargetType="dataprimitives:DataGridColumnHeader">
                      <Setter Property="ContentTemplate">
                        <Setter.Value>
                          <DataTemplate>
                            <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" Width="Auto">
                              <TextBox x:Name="filter2" TextChanged="filter_TextChanged" VerticalAlignment="Center" Width="80" Text=""/>
                              <TextBlock TextAlignment="Center" VerticalAlignment="Center" Width="Auto" Text="Uname"/>
                            </StackPanel>
                          </DataTemplate>
                        </Setter.Value>
                      </Setter>
                    </Style>
                  </sdk:DataGridTextColumn.HeaderStyle>
                </sdk:DataGridTextColumn>
         
              </sdk:DataGrid.Columns>
              
            </sdk:DataGrid>
    
     
    public static class PredicateBuilder
      {
        public static Expression<Func<T, bool>> True<T>() { return f => true; }
        public static Expression<Func<T, bool>> False<T>() { return f => false; }
    
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1,
                                  Expression<Func<T, bool>> expr2)
        {
          var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
          return Expression.Lambda<Func<T, bool>>
             (Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
        }
    
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,
                                   Expression<Func<T, bool>> expr2)
        {
          var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
          return Expression.Lambda<Func<T, bool>>
             (Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
        }
    
        public static Predicate<T> ConvertToPredicate<T>(this Func<T, bool> func)
        {
          return new Predicate<T>(func);
        }
      }
    
    2011年6月3日 9:31

答案

  • 1)如果是比较简单的查询过滤条件,用Linq语句实现还是比较简单的。而且基本不会产生错误。

    2)思路一定要清晰,代码一定要让人看懂,

    3)我觉得可以定义一个 查询字段-用户输入的查询值 列表,来保存用户输入的查询条件

    4)有了这个 查询字段-用户输入的查询值 生成一个 Linq 查询语句就非常简单

    5)生成的Linq语句,直接绑定到DataGrid

    -------------------------------

    按这个思路,我觉得做一个这样的相对还算通用的查询过滤,不会超过两个小时,就可以搞定。

    2011年6月11日 5:42
  • 问题解决!多谢大家的帮助!
    //出错的位置在这个方法中
    //表达式树没有问题,是表达式树中的值有问题!
    
    private void FirFilterMethod()
     {
     var predicate = PredicateBuilder.True<object>();
     foreach (var item in filterProperty)
     {
     switch (item.Key)
     {
      case "filter1":
      predicate = predicate.And(p => ((Demo)p).UID.ToString().Contains(item.Value));
      break;
      case "filter2":
      predicate = predicate.And(p => ((Demo)p).UName.Contains(item.Value));
      break;
     }
     }
     if (pcv != null && pcv.CanFilter)
     {
     pcv.Filter = predicate.Compile().ConvertToPredicate<object>();
     }
    
    
     }
    
    //在foreach代码块里item.Value是引用类型
    //最终表达式树var predicate 的值为:
    f => ((True AndAlso Invoke(p => Convert(p).UID.ToString().Contains(value(SLDemo.Home+<>c__DisplayClass0).item.Value), f)) AndAlso Invoke(p => Convert(p).UName.Contains(value(SLDemo.Home+<>c__DisplayClass0).item.Value), f))
    //其中SLDemo.Home+<>c__DisplayClass0).item.Value就不知道怎么存储的了,目前分析是引用类型
    //,所以如果传两个条件,都会以最后的条件值来做筛选
    
    
    //修改这个方法用值类型
    private void FirFilterMethod()
     {
     var predicate = PredicateBuilder.True<object>();
     foreach (var item in filterProperty)
     {
      string key = item.Key;
      string value = item.Value;
      switch (key)
      {
       case "filter1":
       predicate = predicate.And(p => ((Demo)p).UID.ToString().Contains(value));
       break;
       case "filter2":
       predicate = predicate.And(p => ((Demo)p).UName.Contains(value));
       break;
      }
     }
     if (pcv != null && pcv.CanFilter)
     {
     pcv.Filter = predicate.Compile().ConvertToPredicate<object>();
     }
    
    
     }
    
    
    ok!



    • 已标记为答案 Anton Jin 2011年6月15日 5:22
    2011年6月15日 5:15

全部回复

  • 1)呵呵,我看到Lambda表达式我就头大,我一般使用Linq语句,Linq语句排列特别智能,而且非常容易理解。

    如果你改写成Linq语句,就好了,我就有耐心看。

    2)我对Lamda总是接受不了,而对Linq语句,确实情有独钟。几乎在我的程序里面没有for语句,全部用的是Linq语句模拟代替。

    2011年6月10日 15:56
  • 1)Lambda表达式 个人觉得也有问题,根本没法调试,到现在还不知道是哪个步骤有问题,

        我测试对单列进行多条件是没问题的,超过一个列来筛选就存在问题!

     

    2)嗨,这个测试基本已经放弃了(因为在DataGrid列宽拖动变大时,根本无法让对应的TextBox随之变宽)

     

     

    不知道大家在做DataGrid筛选时,都是怎么实现的?

     

    寻求思路+

     

    thanks

     

    2011年6月11日 2:20
  • 1)如果是比较简单的查询过滤条件,用Linq语句实现还是比较简单的。而且基本不会产生错误。

    2)思路一定要清晰,代码一定要让人看懂,

    3)我觉得可以定义一个 查询字段-用户输入的查询值 列表,来保存用户输入的查询条件

    4)有了这个 查询字段-用户输入的查询值 生成一个 Linq 查询语句就非常简单

    5)生成的Linq语句,直接绑定到DataGrid

    -------------------------------

    按这个思路,我觉得做一个这样的相对还算通用的查询过滤,不会超过两个小时,就可以搞定。

    2011年6月11日 5:42
  • TF说的正确,我就冒昧的把他标记为正确答复
    努力!完成了js解析器,还差一个svg插件,一个绘图程序,做好自己,呵呵~!
    2011年6月11日 10:05
    版主
  • 问题解决!多谢大家的帮助!
    //出错的位置在这个方法中
    //表达式树没有问题,是表达式树中的值有问题!
    
    private void FirFilterMethod()
     {
     var predicate = PredicateBuilder.True<object>();
     foreach (var item in filterProperty)
     {
     switch (item.Key)
     {
      case "filter1":
      predicate = predicate.And(p => ((Demo)p).UID.ToString().Contains(item.Value));
      break;
      case "filter2":
      predicate = predicate.And(p => ((Demo)p).UName.Contains(item.Value));
      break;
     }
     }
     if (pcv != null && pcv.CanFilter)
     {
     pcv.Filter = predicate.Compile().ConvertToPredicate<object>();
     }
    
    
     }
    
    //在foreach代码块里item.Value是引用类型
    //最终表达式树var predicate 的值为:
    f => ((True AndAlso Invoke(p => Convert(p).UID.ToString().Contains(value(SLDemo.Home+<>c__DisplayClass0).item.Value), f)) AndAlso Invoke(p => Convert(p).UName.Contains(value(SLDemo.Home+<>c__DisplayClass0).item.Value), f))
    //其中SLDemo.Home+<>c__DisplayClass0).item.Value就不知道怎么存储的了,目前分析是引用类型
    //,所以如果传两个条件,都会以最后的条件值来做筛选
    
    
    //修改这个方法用值类型
    private void FirFilterMethod()
     {
     var predicate = PredicateBuilder.True<object>();
     foreach (var item in filterProperty)
     {
      string key = item.Key;
      string value = item.Value;
      switch (key)
      {
       case "filter1":
       predicate = predicate.And(p => ((Demo)p).UID.ToString().Contains(value));
       break;
       case "filter2":
       predicate = predicate.And(p => ((Demo)p).UName.Contains(value));
       break;
      }
     }
     if (pcv != null && pcv.CanFilter)
     {
     pcv.Filter = predicate.Compile().ConvertToPredicate<object>();
     }
    
    
     }
    
    
    ok!



    • 已标记为答案 Anton Jin 2011年6月15日 5:22
    2011年6月15日 5:15
  • 在客户端filter, 这种情况比较简单,直接使用linq就ok.如果想提高性能,在Ria service端筛选的话要用 DomainDataSource+ FilterDescriptor。

    比如

     FilterDescriptor fd =new FilterDescriptor { Value = "xxx", PropertyPath = "filter1", IsCaseSensitive = false, Operator =FilterOperator.Contains}

    datasource.FilterDescriptors.Add(fd);

    2011年6月17日 6:01
  • 实际上你的问题是没有解决的。

    1)生成Filter是非常容易的,能很好解决;

    2)列拖拉变化宽度,表头相应的TextBox大小相应变化,需要点小技巧,我看你写的代码的结构,就能猜出你没有解决,而且你不会去解决。因为从你的代码书写习惯,可以判断出你不愿意精益求精。

    3)如果你需要并且TFSoft有兴趣,TFSoft将给你写一个T4FilterViewHelper通用类给你参考。

    4)如下这样的代码只有超人才能读懂

    f => ((True AndAlso Invoke(p => Convert(p).UID.ToString().Contains(value(SLDemo.Home+<>c__DisplayClass0).item.Value), f)) AndAlso Invoke(p => Convert(p).UName.Contains(value(SLDemo.Home+<>c__DisplayClass0).item.Value), f))

    --------------------------------------

    TFSoft=Simple,Nice,Elegant

     







    2011年6月17日 10:01
  • 实际上你的问题是没有解决的。

    1)生成Filter是非常容易的,能很好解决;

    2)列拖拉变化宽度,表头相应的TextBox大小相应变化,需要点小技巧,我看你写的代码的结构,就能猜出你没有解决,而且你不会去解决。因为从你的代码书写习惯,可以判断出你不愿意精益求精。

    3)如果你需要并且TFSoft有兴趣,TFSoft将给你写一个T4FilterViewHelper通用类给你参考。

    4)如下这样的代码只有超人才能读懂

    f => ((True AndAlso Invoke(p => Convert(p).UID.ToString().Contains(value(SLDemo.Home+<>c__DisplayClass0).item.Value), f)) AndAlso Invoke(p => Convert(p).UName.Contains(value(SLDemo.Home+<>c__DisplayClass0).item.Value), f))

    --------------------------------------

    TFSoft=Simple,Nice,Elegant

    多谢TF的批评,我写的代码确实太粗糙了,因为项目需要,接触SL的时间比较短,希望TF多帮帮忙。

    1)列拖拉变化宽度,表头相应的TextBox大小相应变化,这个确实没有实现,因为datagrid的列宽属性不是依赖属性,还有datagrid没有提供相应的事件来处理拖动列宽

    2)那段”超人代码“  源于跟踪、调试 predicate 变量最终生成的结果,就因为这个我才了排除了解到筛选结果的不准确的问题(引用类型、值类型),

    3)真心希望TF能写个T4FilterViewHelper通用类给我参考

    thanks


    2011年6月20日 0:31
  • 这段时间有点忙,一下子没时间写,你自己写,不会太难,用点心,你一定能实现。

    友情提示:

    1) 拖拉判断:DataGrid的LostMouseCapture事件里面想办法。

    2)请用DataTemplateColumn来搞,这样比较灵活。

    3)每个TemplateColumn的Header Style 的定义里面的最外层元素,绑定一个列宽控制变量,

    我可以100%肯定,你按照这个思路来搞,一定能实现。

    实际上我连C#也就是最近半年才用。SL也用的时间不长。不过我编程的基础比较扎实,从80年代开始。估计那时候你还没出生。

    呵呵,我一般是用古老,原始,基础的东西来搞,虽然麻烦点,但是可控性强。比如我就从不用WCF RIA,我用我自己的T4Data







    2011年6月20日 1:54
  • 有二个问题想请教一下,Datagrid 绑定 Linq 出来的结果 ,,,

    1,如果Linq的结果是匿名类型,,,好像绑定后显示不了结果,不知道 有什么方法可以解决?

    2,绑定后的列顺序好像是字母顺序,,,并不是自定义的顺序

           比如: From i as aaa in aaas select i.c,i.b,i.a

           datagrid绑定后,,,列的顺序 是 a,b,c   并不是 c,b,a,,,如果我想要 c,b,a的顺序怎么办?

           (网上说调整displayindex,,,但这里好像不能直接设置displayindex,会变成 c,a,b


    不吝赐教
    2011年7月11日 14:06