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

      最近在使用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)
{
    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-界面功能却是很丰富(两种删除一样效果)

4条评论

  1. guoc说道:

    仁兄!有没有源码参考下啊、谢谢

  2. 不懂技术
    菜鸟飘过

  3. cxy说道:

    楼主我正在学习,一直被困扰,能否发份代码参考一下,[email protected],谢谢楼主

  4. hihahuha说道:

    太妙了

    不过,用
    DataGridTemplateColumn zsCol = this.Brow.Columns[0] as DataGridTemplateColumn;
    myList.Remove(zsCol);

    然后需要的时候
    myList.Add(zsCol);更简单

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注