最近一直在做WINFORM项目,所以经常有些新的想法或尝试与大家分享,之前与大家分享了通用窗体遮罩层、通用可附加数据绑定的DataGridView、窗体渐显,今天来分享一个大家在其它软件中常见的功能:数据过滤查询。
先看一下我实现的的整体效果:
过滤之后:
说一下实现上述功能的思路:
首先说一下界面的设计》
1.创建一个窗体(在此称作:过滤窗体FrmFilter),然后在窗体上部放一个DataGridView控件、下面放一个Panel,然后Panel中放两个按钮,至于如何更好的布局或是否需要适应窗体变化,这些都比较简单,在此就不介绍了;
2.设计DataGridView控件,分别加入4列(字段名、运算符、值、值2),其中字段名、运算符列需支持下拉,值、值2列需支持输入
界面设计很简单,现在说一下代码的实现,完整代如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
using
System;
using
System.Data;
using
System.Windows.Forms;
using
TEMS.Service;
namespace
TEMS.Forms
{
public
partial
class
FrmFilter : FormBase
{
private
DataTable filterTable =
null
;
/// <summary>
/// 获取过滤条件的表格(zuowenjun.cn)
/// </summary>
public
DataTable FilterTable
{
get
{
return
filterTable;
}
}
public
FrmFilter(
object
source,
string
text,
string
value)
{
InitializeComponent();
dataGridFilter.AutoGenerateColumns =
false
;
var
col0 = dataGridFilter.Columns[0]
as
DataGridViewComboBoxColumn;
col0.DataSource = source;
col0.DisplayMember = text;
col0.ValueMember = value;
var
col1 = dataGridFilter.Columns[1]
as
DataGridViewComboBoxColumn;
col1.DataSource = FilterOperators.Operators;
col1.DisplayMember =
"Value"
;
col1.ValueMember =
"Key"
;
InitFilterDataTable();
}
private
void
InitFilterDataTable()
{
filterTable =
new
DataTable();
foreach
(DataGridViewColumn col
in
dataGridFilter.Columns)
{
filterTable.Columns.Add(col.DataPropertyName,
typeof
(
string
));
}
dataGridFilter.DataSource = filterTable;
}
private
void
btnOk_Click(
object
sender, EventArgs e)
{
this
.Close();
}
private
void
btnReset_Click(
object
sender, EventArgs e)
{
InitFilterDataTable();
this
.Close();
}
}
}
|
以下是系统自动生成的窗体设计代码:
构造函数中的的 FilterOperators.Operators表示的是运算符的数据源,我是定义的一个struct,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public
struct
FilterOperators
{
public
const
string
Equal =
"Equal"
;
public
const
string
NotEqual =
"NotEqual"
;
public
const
string
LessThan =
"LessThan"
;
public
const
string
GreaterThan =
"GreaterThan"
;
public
const
string
LessThanOrEqual =
"LessThanOrEqual"
;
public
const
string
GreaterThanOrEqual =
"GreaterThanOrEqual"
;
public
const
string
Contains =
"Contains"
;
public
const
string
StartsWith =
"StartsWith"
;
public
const
string
EndsWith =
"EndsWith"
;
public
const
string
Between =
"Between"
;
public
static
KeyValueList<
string
,
string
> Operators =
new
KeyValueList<
string
,
string
>()
{
{Equal,
"等于"
},{NotEqual,
"不等于"
},
{LessThan,
"小于"
},{GreaterThan,
"大于"
},
{LessThanOrEqual,
"小于或等于"
},{GreaterThanOrEqual,
"大于或等于"
},
{Contains,
"包含"
},{StartsWith,
"开头包含"
},{EndsWith,
"结尾包含"
},
{Between,
"区间"
}
};
}
|
FilterTable属性是用来获取过滤条件的表格,表格的数据是通过绑定DataGridView控件来获得的,如果不知道为什么通过数据源绑定到一个空表格就能获取数据,请查找相关资料,这里不作说明,当然也可以通过评论与我交流。
以上都是关于过滤窗体的实现,下面我要讲解最关键字部份,也是最重要的部份,就是:如何将获得的过滤条件DataTable转换成查询语句,这里的查询语句包括SQL或表达式树,由于是通过的过滤窗体,所以关于生成查询条件语句我放在了调用完该窗体后来实现,当然如果大家只用一种方式(实体类或表),可以直接集成在窗体类中,直接返回生成的查询语句即可。
因为我项目中用的是实体类来查询,所以我采用动态生成Lambda表达式树,然后借助PredicateBuilder类(这是别人开发的类,详见我之前的博文)进行拼接,最后生成用于查询的表达式树,实现代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
/// <summary>
/// 获取查询表达式树 (zuowenjun.cn)
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="fieldName"></param>
/// <param name="operatorName"></param>
/// <param name="value"></param>
/// <param name="value2"></param>
/// <returns></returns>
public
static
Expression<Func<TEntity,
bool
>> GetQueryExpression<TEntity>(
string
fieldName,
string
operatorName,
string
value,
string
value2)
where
TEntity :
class
{
PropertyInfo fieldInfo =
typeof
(TEntity).GetProperty(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
Type pType = fieldInfo.PropertyType;
if
(
string
.IsNullOrEmpty(operatorName))
{
throw
new
ArgumentException(
"运算符不能为空!"
,
"operatorName"
);
}
dynamic convertedValue;
if
(!value.TryChangeType(pType,
out
convertedValue))
{
throw
new
ArgumentException(
string
.Format(
"【{0}】的查询值类型不正确,必须为{1}类型!"
, General.GetDisplayName(fieldInfo), pType.FullName),
"value"
);
}
ParameterExpression expParameter = Expression.Parameter(
typeof
(TEntity),
"f"
);
MemberExpression expl = Expression.Property(expParameter, fieldInfo);
ConstantExpression expr = Expression.Constant(convertedValue, pType);
Expression expBody =
null
;
Type expType =
typeof
(Expression);
var
expMethod = expType.GetMethod(operatorName,
new
[] { expType, expType });
if
(expMethod !=
null
)
{
expBody = (Expression)expMethod.Invoke(
null
,
new
object
[] { expl, expr });
}
else
if
(FilterOperators.Between == operatorName)
{
dynamic convertedValue2;
if
(!value2.TryChangeType(pType,
out
convertedValue2))
{
throw
new
ArgumentException(
string
.Format(
"【{0}】的查询值2类型不正确,必须为{1}类型!"
, General.GetDisplayName(fieldInfo), pType.FullName),
"value"
);
}
ConstantExpression expr2 = Expression.Constant(convertedValue2, pType);
expBody = Expression.GreaterThanOrEqual(expl, expr);
expBody = Expression.AndAlso(expBody, Expression.LessThanOrEqual(expl, expr2));
}
else
if
(
new
[] { FilterOperators.Contains, FilterOperators.StartsWith, FilterOperators.EndsWith }.Contains(operatorName))
{
expBody = Expression.Call(expl,
typeof
(
string
).GetMethod(operatorName,
new
Type[] {
typeof
(
string
) }), expr);
}
else
{
throw
new
ArgumentException(
"无效的运算符!"
,
"operatorName"
);
}
Expression<Func<TEntity,
bool
>> lamExp = Expression.Lambda<Func<TEntity,
bool
>>(expBody, expParameter);
return
lamExp;
}
|
其中TryChangeType是我扩展的支持任意类型的转换,该扩展方法又引用了其它的自定义扩展方法,具体方法定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
/// <summary>
/// 判断是否可为转换为指定的类型
/// </summary>
/// <param name="str"></param>
/// <param name="type"></param>
/// <returns></returns>
public
static
bool
TryChangeType(
this
object
str, Type type,
out
dynamic returnValue)
{
try
{
if
(type.IsNullableType())
{
if
(str ==
null
|| str.ToString().Length == 0)
{
returnValue =
null
;
}
else
{
type = type.GetGenericArguments()[0];
returnValue = Convert.ChangeType(str, type);
}
}
else
{
returnValue = Convert.ChangeType(str, type);
}
return
true
;
}
catch
{
returnValue = type.DefaultValue();
return
false
;
}
}
/// <summary>
/// 判断是否为可空类型
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public
static
bool
IsNullableType(
this
Type type)
{
return
(type.IsGenericType &&
type.GetGenericTypeDefinition().Equals
(
typeof
(Nullable<>)));
}
/// <summary>
/// 默认值
/// </summary>
/// <param name="targetType"></param>
/// <returns></returns>
public
static
dynamic DefaultValue(
this
Type targetType)
{
return
targetType.IsValueType ? Activator.CreateInstance(targetType) :
null
;
}
|
以下是具体的调用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
//获取用于过滤的字段(这里直接采用数据列表的相应列,大家可以生成相应的List数据源) (zuowenjun.cn)
private
List<DataGridViewColumn> GetQueryGridColumnInfos()
{
List<DataGridViewColumn> cols =
new
List<DataGridViewColumn>();
for
(
int
i = 0; i < dataGridBase.Columns.Count - 3; i++)
{
cols.Add(dataGridBase.Columns[i]);
}
return
cols;
}
//工具栏点击过滤查询按钮事件 (zuowenjun.cn)
private
void
ToolStrip_OnFilter(
object
sender, EventArgs e)
{
if
(filterForm ==
null
)
{
filterForm =
new
FrmFilter(GetQueryGridColumnInfos(),
"HeaderText"
,
"DataPropertyName"
);
}
filterForm.ShowDialog(Common.MainForm);
whereExpr = PredicateBuilder.True<Category>();
var
p = whereExpr.Parameters[0];
foreach
(DataRow row
in
filterForm.FilterTable.Rows)
{
string
fieldName = row[0].ToString();
string
opt = row[1].ToString();
string
value = row[2].ToString();
string
value2 = row[3].ToString();
var
fieldExpr = Common.GetQueryExpression<Category>(fieldName, opt, value, value2);
//动态生成查询表达式树
whereExpr = whereExpr.And(fieldExpr);
//连接表达式树
}
FirstLoadList();
//加载数据并显示
}
///加载数据 (zuowenjun.cn)
private
void
FirstLoadList()
{
int
recordCount = 0;
base
.PageInfo =
new
PageInfo();
base
.PageInfo.PageSize = 10;
base
.PageInfo.CurrentPageNo = 1;
var
resultList = QueryBusiness<Category>.GetListByPage(whereExpr, t => t, t => t.ID, 1,
base
.PageInfo.PageSize,
base
.PageInfo.CurrentPageNo,
out
recordCount);
base
.PageInfo.RecordCount = recordCount;
base
.AppendDataToGrid(resultList,
false
);
}
|
由于代码较多,且使用了一些其它的编写好的类,若有不明白的地方,可以在此文下评论,我会帮忙解答,希望能帮到大家,不足之处也欢迎大家指证,谢谢!
本文转自 梦在旅途 博客园博客,原文链接:http://www.cnblogs.com/zuowj/p/4459956.html ,如需转载请自行联系原作者