文章

【DataGrid】如何为自动生成的列设置 CellTemplate

【DataGrid】如何为自动生成的列设置 CellTemplate

DataGrid 是展示表格的控件,设置AutoGenerateColumns="True"后,DataGrid 会自动生成列。有时候这很方便,但是自动生成的列,很死板,基本全都是 DataGridTextColumn,如果是 int string 可能还好,但稍微复杂一些的数据类型,比如 DateTime, 无论是展示还是编辑,效果都很难受。这里记录如何给自动生成的列设置模板。

0x01 AutoGeneratingColumn

当 DataGrid 自动生成列时,会触发 AutoGeneratingColumn 事件,在这里,可以控制生成的列。事件参数 e 中有一个 DataGridColumn 类型的 Column 属性,是可读可写的,构造一个 DataGridTemplateColumn,赋值给 Column 属性,就可以实现自定义列。

WPF 代码:

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
private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
    if(e.PropertyType == typeof(DateTime))
    {
        DataTemplate cellTemplate = new DataTemplate();
        FrameworkElementFactory fatcory = new FrameworkElementFactory(typeof(TextBlock));
        Binding binding = new Binding();
        binding.Path = new PropertyPath(e.PropertyName);
        binding.StringFormat = "{0:yyyy-MM-dd}";
        fatcory.SetBinding(TextBlock.TextProperty, binding);
        cellTemplate.VisualTree = fatcory;

        DataTemplate cellEditingTemplate = new DataTemplate();
        FrameworkElementFactory factory1 = new FrameworkElementFactory(typeof(DatePicker));
        Binding binding1 = new Binding();
        binding1.Path = new PropertyPath(e.PropertyName);
        factory1.SetBinding(DatePicker.SelectedDateProperty, binding);
        cellEditingTemplate.VisualTree = factory1;
        var column = new DataGridTemplateColumn()
        {
            Header = e.PropertyName,
            CellTemplate = cellTemplate,
            CellEditingTemplate = cellEditingTemplate,
        };
        e.Column = column;
    }
}

WPF 中这种简单的模板可以用 C# 实现,就懒得写 xaml 了,实际环境建议还是写 xaml

Avalonia 的写法:

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
private void DataGrid_AutoGeneratingColumn(object? sender, Avalonia.Controls.DataGridAutoGeneratingColumnEventArgs e)
{
    if (e.PropertyType == typeof(DateTimeOffset))
    {
        // 这里的 object 是 DataGridRow.DataContext,这里直接写 Object 走反射
        var cellTemplate = new FuncDataTemplate<object>((n, s) => new TextBlock()
        {
            VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
            [!TextBlock.TextProperty] = new Binding(e.PropertyName)
            {
                StringFormat = "{0:yyyy-MM-dd}"
            }
        });

        var cellEditingTemplate = new FuncDataTemplate<object>((n, s) => new DatePicker()
        {
            VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
            [!DatePicker.SelectedDateProperty] = new Binding(e.PropertyName)
        });

        var column = new DataGridTemplateColumn()
        {
            Header = e.PropertyName,
            CellTemplate = cellTemplate,
            CellEditingTemplate = cellEditingTemplate,
        };
        e.Column = column;
    }
}

0x02 Behavior

这种东西,如果直接写事件订阅,总感觉有些不 MVVM,建议封装成 Behavior 方便使用。

本文由作者按照 CC BY 4.0 进行授权