代码改变世界

【技术原创】如何在SilverLight/WPF中用代码给DataGrid动态组织模板内容

2012-08-23 20:51  Brush  阅读(1434)  评论(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啊,想干嘛就干嘛,爽啊。

       写到这里,有没有忽然开朗的感觉?所以我还是在坚持我的一个观点:技巧重于技术。最后以项目运行截图结束这篇文章。

      image图1-列表界面(其实也没啥)

image图2-后台代码也没啥(连Using没超过80行)

image图3-界面功能却是很丰富(两种删除一样效果)

 

   这篇文章同时也发布到了我的博客中:http://www.zaibc.com/programming/how-to-design-the-datagrid-template-in-run-time-on-silverlight-or-wpf.html ,欢迎大家支持,您的支持是我继续发文章的动力。