接上篇,继续学习DataGrid。下面我来学习DataGrid的Header的自定义功能。
在上篇中,我们实现的对Grid的排序,但是用的是Grid自动生产的Header,现在我们用自定义的控件来代替系统自动生成的Header,并且在Header下面添加一个可以对数据进行过滤的功能。
在这里我参考了silverlight.net上一群牛人的代码。
因为过去没有用过silverlight,在silverlight.net上听说在2.0RC出来之前,自定义ColumnHeader很容易,只需要设置Column.Header为某个控件就可以了,但是现在全变了,Column.Header变成了表头的内容了,要更改Header,必须用做一个新的Style,赋给Column.HeaderStyle。不是太明白微软问什么要这样做,把一个简单的问题搞的很复杂,对一个新手来说,做一个Style真的很费事。按照字面的意思,我认为Style应该是用来控制控件的展现方式,有点像 WEB 上的 CSS ,但是这里的Style不但可以控制如何展示控件,还要设置用什么控件,如何绑定数据,甚至还是事件的处理等等,感觉像个像个自定义控件,但是又没有自定义控件好用。
好了,废话不说了,先看效果:
具体实现的过程:
第一步,我们要做一个新的ColumnHeader。这里直接用来silverlight.net上一位高手的代码,并且做了扩展,主要是添加了Header的点击事件和过滤框的事件,
以及一个新的属性用来保存字段名。应为在实际的项目中,我们不可能在Grid上直接显示数据库的字段名,而是要用中文来显示。代码不是太复杂,就不做解释了。
GridHeader.cs
Code
1 [TemplatePart(Name = GridHeader.HeaderTextElement, Type = typeof(FrameworkElement))]
2 [TemplatePart(Name = GridHeader.FilterTextElement, Type = typeof(TextBox))]
3 public class GridHeader : Control
4 {
5 public delegate void HeaderClickEvent(string fieldname);
6 public event HeaderClickEvent OnSort;
7
8 public delegate void FilterTextEvent(string fieldname, string filtertext);
9 public event FilterTextEvent OnFilter;
10
11 protected const string HeaderTextElement = "btnHeaderText";
12 protected const string FilterTextElement = "txtFilerText";
13
14 Button btn_header;
15 TextBox filterText;
16
17 #region Constructor
18 public GridHeader()
19 {
20 this.DefaultStyleKey = typeof(GridHeader);
21 }
22 #endregion
23
24 #region HeaderText
25 /// <summary>
26 /// Identifies the HeaderText dependency property.
27 /// </summary>
28 public static readonly DependencyProperty HeaderTextProperty = DependencyProperty.Register("HeaderText", typeof(string), typeof(GridHeader), null);
29
30 /// <summary>
31 /// Gets or sets the HeaderText possible Value of the int object.
32 /// </summary>
33 public string HeaderText
34 {
35 get { return (string)GetValue(HeaderTextProperty); }
36 set { SetValue(HeaderTextProperty, value); }
37 }
38 #endregion HeaderText
39
40 #region FilterText
41
42 /// <summary>
43 /// Identifies the FilterText dependency property.
44 /// </summary>
45 public static readonly DependencyProperty FilterTextProperty = DependencyProperty.Register("FilterText", typeof(string), typeof(GridHeader), null);
46
47 /// <summary>
48 /// Gets or sets the FilterText possible Value of the string object.
49 /// </summary>
50 public string FilterText
51 {
52 get { return (string)GetValue(FilterTextProperty); }
53 set { SetValue(FilterTextProperty, value); }
54 }
55 #endregion FilterText
56
57 #region FieldText
58 public static DependencyProperty FieldTextProperty = DependencyProperty.Register("FieldText", typeof(string), typeof(GridHeader), null);
59 public string FieldText
60 {
61 get { return (string)GetValue(FieldTextProperty); }
62 set { SetValue(FieldTextProperty, value); }
63 }
64
65 #endregion
66
67 public override void OnApplyTemplate()
68 {
69 base.OnApplyTemplate();
70 StackPanel sp = this.GetTemplateChild("LayoutRoot") as StackPanel;
71
72 btn_header = sp.Children[0] as Button;
73 filterText = sp.Children[1] as TextBox;
74
75 if (this.filterText != null)
76 this.filterText.LostFocus += new RoutedEventHandler(filterText_LostFocus);
77 if (this.btn_header != null)
78 this.btn_header.Click += new RoutedEventHandler(header_Click);
79 }
80
81 void header_Click(object sender, RoutedEventArgs e)
82 {
83 OnSort(FieldText);
84 }
85
86 void filterText_LostFocus(object sender, RoutedEventArgs e)
87 {
88 OnFilter(FieldText, filterText.Text.Trim());
89 }
90
第二步,在Themes下添加一个Generic.xaml,内容如下:
Generic
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:SilverlightDemoApp;assembly=SilverlightDemoApp">
<Style TargetType="controls:GridHeader">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:GridHeader">
<StackPanel x:Name="LayoutRoot" Background="{TemplateBinding Background}">
<Button x:Name="btnHeaderText" Margin="0" BorderThickness="0" Content="{TemplateBinding HeaderText}"></Button>
<TextBox x:Name="txtFilterTextBox" Margin="1,0,1,1" Text="{TemplateBinding FilterText}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary
这里定义了我们自定义的ColumnHeader的展现方式,包括用了一个Button和TextBox。
关于Generic.xaml我不是太明白这个文件,有些人说会自动产生这个文件,但是我的机器上并没有这个文件,我是手工添加的,现建一个themes下建一个 SilverLight用户控件,然后用上面的内容替换掉自动生成的内容,并且去掉Generic.xaml.cs的public Generic()里面的代码。
第三步,在DataGrid页面上添加Resources。如下:
Code
<UserControl.Resources>
<Style x:Key="grid-header" TargetType="primitives:DataGridColumnHeader">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<my:GridHeader HeaderText="{TemplateBinding Content}" Height="Auto" Loaded="GridHeader_Loaded" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources
注意,要在文件头部加上
xmlns:my="clr-namespace:SilverlightDemoApp;assembly=SilverlightDemoApp"
就是这个my:GridHeader的namespace和assembly信息。不然就找不到my:GridHeader的了。
第四步,实现DataGrid所在页面的代码。
基础的代码可以见我的前一篇文档。
Silverlight学习笔记二:DataGrid 服务器端分页、排序的实现
Silverlight学习笔记二(续)
这里主要写一下修改的地方
xaml中的DataGrid部分。
Code
<!--DataGrid-->
<data:DataGrid x:Name="dgData" AutoGenerateColumns="False"
SelectionMode="Single" IsReadOnly="True"
Grid.Column="0" Grid.Row="2"
Height="Auto" Width="Auto" CanUserSortColumns="False"
CanUserReorderColumns="False"
MouseEnter="dgData_MouseEnter" MouseLeave="dgData_MouseLeave"
>
<data:DataGrid.Columns>
<data:DataGridTextColumn Header="产品编号" Binding="{Binding ProductID}"></data:DataGridTextColumn>
<data:DataGridTextColumn Header="产品名称" Binding="{Binding ProductName}"></data:DataGridTextColumn>
<data:DataGridTextColumn Header="单价" Binding="{Binding UnitPrice}"></data:DataGridTextColumn>
<data:DataGridTextColumn Header="库存数量" Binding="{Binding UnitsInStock}"></data:DataGridTextColumn>
<data:DataGridTextColumn Header="订单数量" Binding="{Binding UnitsOnOrder}"></data:DataGridTextColumn>
<data:DataGridTextColumn Header="规格" Binding="{Binding QuantityPerUnit}"></data:DataGridTextColumn>
<data:DataGridCheckBoxColumn Header="是否停产" Binding="{Binding Discontinued}"></data:DataGridCheckBoxColumn>
<data:DataGridTemplateColumn Header="操作">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button x:Name="btnEdit" Content="编辑" ></Button>
<Button x:Name="btnDelete" Content="删除"></Button>
</StackPanel>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
</data:DataGrid.Columns>
</data:DataGrid>
在获取数据之后,对DataGrid的Header进行了处理。新增了CreateHeader()这个方法。
1
private
void
client_GetProductsPagingCompleted(
object
sender, GetProductsPagingCompletedEventArgs e)
2
{
3
if
(e.Error
==
null
)
4
{
5
products_list
=
e.Result.ToList();
6
dgData.ItemsSource
=
products_list;
7
if
(pager
==
null
)
8
{
9
pager
=
new
Pager(e.TotalPage,
2
);
10
pager.Click
+=
new
Pager.PagerButtonClick(pager_Click);
11
pager.PageIndex
=
1
;
12
spPager.Children.Clear();
13
spPager.Children.Add(pager);
14
//
第一次时创建GridHeader
15
CreateHeader();
16
}
17
}
18
else
19
MessageBox.Show(e.Error.Message);
20
canvas.Visibility
=
Visibility.Collapsed;
21
}
22
23
//
用来存储HeaderText和数据库字段之间的对照信息
24
Dictionary
<
string
,
string
>
FieldDict
=
new
Dictionary
<
string
,
string
>
();
25
private
void
CreateHeader()
26
{
27
var v
=
from p
in
dgData.Columns
28
where
p
is
DataGridBoundColumn
29
select p;
30
foreach
(DataGridBoundColumn column
in
v.ToList())
31
{
32
string
fieldname
=
column.Binding.Path.Path;
33
Style style_header
=
Resources[
"
grid-header
"
]
as
Style;
34
column.HeaderStyle
=
style_header;
35
36
//
本来这里我是希望可以直接设置Header的FieldText为字段名的,但是却找不到什么好的方法,
37
//
38
//
<my:GridHeader HeaderText="{TemplateBinding Content}" Height="Auto" Loaded="GridHeader_Loaded" />
39
//
Resources中这里也不能直接绑定FieldText为Binding.Path.Path,因为这里是从DataGridColumnHeader来的,
40
//
DataGridColumnHeader只有Content,而找不到对应的绑定信息。
41
//
42
//
另外,通过访问Style的Setter,我也只能获取到ControlTemplate,但是ControlTemplate下面的内容,也就是my:GridHeader取怎么也获取不到,
43
//
如果有高人知道,请指教一下。谢谢
44
//
45
//
所以,没办法,只能暂时把字段名和HeaderText都保存到Dictionary里面,供后面使用
46
47
FieldDict.Add(column.Header.ToString(), fieldname);
48
}
49
}
50
51
private
void
GridHeader_Loaded(
object
sender, RoutedEventArgs e)
52
{
53
GridHeader header
=
sender
as
GridHeader;
54
header.OnFilter
+=
new
GridHeader.FilterTextEvent(header_OnFilter);
55
header.OnSort
+=
new
GridHeader.HeaderClickEvent(header_OnSort);
56
string
fieldtext;
57
//
在这里,我们对Header的FieldText设置为字段名。
58
//
如果能在这里获取到当前的Column的话,前面就不要用Dictionary了,但是这里我仍然不知道该怎么获取到当前的Column。
59
if
(FieldDict.TryGetValue(header.HeaderText,
out
fieldtext))
60
header.FieldText
=
fieldtext;
61
}
62
63
void
header_OnSort(
string
fieldname)
64
{
65
//
这里具体的代码就不写了。
66
//
在获取到字段名之后,对数据进行排序。
67
MessageBox.Show(
"
对字段:
"
+
fieldname
+
"
进行排序
"
);
68
}
69
70
void
header_OnFilter(
string
fieldname,
string
filtertext)
71
{
72
//
这里具体的代码就不写了。
73
//
在获取到字段名和关键字只有,就可以通过WCF获取到指定的数据了。
74
MessageBox.Show(
"
对字段:
"
+
fieldname
+
"
按照
"
+
filtertext
+
"
进行过滤
"
);
75
}
ok,打完手工。