【技术原创】如何在SilverLight/WPF中用代码给DataGrid动态组织模板内容
2012-08-23 20:51 Brush 阅读(1429) 评论(10) 编辑 收藏 举报
最近在使用SilverLight开发项目,感觉它很适合做企业后台管理软件开发。因为之前只用过WPF,对SilverLight这个子集了解得不是很多。于是我在之前的Asp.Net下写过的一个快速开发框架,就得在SiverLight中重新实现。这期间遇到不少问题,还好都一一解决了。其中我想跟大家分享一下今天中午我遇到的这个问题:如何在SilverLight中给DataGrid动态组织模板内容。
先说应用场景:
在需要以列表形式展示数据的界面中,放入一个DataGrid(实际上是我重写过的),再放一个分页控件,设置一下,即可实现自动绑定数据并分页。为了追求代码的简洁与框架的完美,我又添加了一个功能,就是将界面中功能按钮的添加放到分页控件里,这样无论前端界面还是后台代码,都可以变得很简洁。于是乎,在DataGrid中动态组织模板内容就成了一个技术难题。
首先,我尝试了一下用代码生成DataGridTemplateColumn模板列,但到CellTemplate这一步后就无法再进行下去了,因为DataTemplate对象无法操作其内部元素。在网上找了一些解决方法,无外乎这三种方案:
方案一:动态组织Xaml字符串,转成DataTemplate
先将要生成的模板内容用Xaml字符串拼起来,然后再使用XamlReader加载,转化成DataTemplate添加到模板列中;方案二:Xaml文件加载在资源中,转成DataTemplate
事先将Xaml文件写在资源中,使用时直接加载转化成DataTemplate;方案三:使用第三方控件
总结这三种方案,也只有方案一,即动态组合Xaml字符串的能凑合满足我的需求(因为我还涉及到模板里面的控件处理,事件触发等,使用此方案麻烦就大了),而且拼那字符串实在是比较蹩脚。于是我觉得肯定有方案能解决这个问题(毕竟我都能找到DataTemplate对象,只是不能访问内部元素而已)。忽然想到之前我写终端展示框架时用的一个技巧,我想这里可以借来用一下:总结成四个字就是:借鸡下蛋。
具体地讲,就是DataTemplate我没办法自己生成自己需要的,但我可以从别人那里拿过来用一下(说得不好听一点是抢过来用)。这样从别人那边拿过来的DataTemplate就是事先整理好的,跳过了自己组装Xaml字符串的过程,而且还可以自行决定模板里显示的内容。具体做法是,新建一个用户控件,里面只放一个DataGrid,DataGrid只有一个模板列,模板列里只有一个元素:Grid,没错,就是大家都喜欢用的Grid。具体情况如下:
<Controls:DataGrid x:Name="myList">
<Controls:DataGrid.Columns>
<Controls:DataGridTemplateColumn Header="操作" Width="Auto">
<Controls:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid Loaded="Grid_Loaded">
</Grid>
</DataTemplate>
</Controls:DataGridTemplateColumn.CellTemplate>
</Controls:DataGridTemplateColumn>
</Controls:DataGrid.Columns>
</Controls:DataGrid>
然后我们利用Grid的Loaded事件,触发每行的模板内容创建委托,同外部代码来决定每行数据要填充哪些内容。代码如下:
public delegate void EventHandlerSL<TParam>(TParam e);
/// <summary>
/// 模板子项创建时触发的委托
/// </summary>
public EventHandlerSL<Grid> TemplateItemRequired;private void Grid_Loaded(object sender, RoutedEventArgs e)
{
Grid gd = sender as Grid;
if (gd.Tag == null)
{
gd.Tag = ""; // 避免多次初始化if (this.TemplateItemRequired != null)
{
this.TemplateItemRequired(gd);
}
}
}public DataTemplate GetDataTemplate() // 借鸡
{
DataGridTemplateColumn col = myList.Columns[0] as DataGridTemplateColumn;
return col.CellTemplate;
}
最后呢,在外部调用时是这样子的:
var c = new DataGridTemplateColumn(); // 自己的,不能下蛋的鸡
c.Header = "操 作";
c.Width = DataGridLength.Auto;
UcDataTemplate uc = new UcDataTemplate(); // 鸡来了
c.CellTemplate = uc.GetDataTemplate(); // 借鸡生蛋this.Columns.Add(c);
// 自己决定蛋里面是什么东东
uc.TemplateItemRequired = delegate(Grid gd) // 给了万能的Grid来部署每行内容
{
StackPanel sp = new StackPanel();
sp.Orientation = Orientation.Horizontal;
gd.Children.Add(sp);Button btn = new Button();
btn.Content = "按钮名称";
btn.Margin = new Thickness(5, 0, 0, 0);
btn.Width = 60;Binding b = new Binding("绑定字段");
btn.SetBinding(Button.CommandParameterProperty, b);
btn.Click += delegate
{
MessageBox.Show("这个就是绑定的值哦:" + btn.CommandParameter.ToString());
};sp.Children.Add(btn);
画外音:万能的Grid啊,想干嘛就干嘛,爽啊。
写到这里,有没有忽然开朗的感觉?所以我还是在坚持我的一个观点:技巧重于技术。最后以项目运行截图结束这篇文章。
图1-列表界面(其实也没啥)
图2-后台代码也没啥(连Using没超过80行)
图3-界面功能却是很丰富(两种删除一样效果)
这篇文章同时也发布到了我的博客中:http://www.zaibc.com/programming/how-to-design-the-datagrid-template-in-run-time-on-silverlight-or-wpf.html ,欢迎大家支持,您的支持是我继续发文章的动力。