# ListViewColumnBuilders - SourceGenerators Syntax

Starting with .NET5 Microsoft introduced SourceGenerators (opens new window). This allows us to inspect source code and generate code for a more powerful experience of Xenial ColumnBuilders. This is one of the many interpretations of SourceGenerators that helps us to write type safe and more maintainable code.

Whilst Xenial.Framework does provide a robust and IntelliSense driven way to craft columns in code, it is still verbose and requires a lot of lambda expressions, which can lead to a lot of nested braces, which might be hard to read and maintain. Combined with the record syntax it provides the cleanest and concise way of writing columns without loosing any type safety.

By way of a quick reminder the illustration below shows what the final ListView should look like;

Person Target Columns

# Installation

In your platform agnostic module (opens new window) install the Xenial.Framework.SourceGenerators (opens new window) package.

INFORMATION

By convention the platform agnostic module is usually named <Your Application>.Module. If you're unfamiliar with the Command Line Interface (cli) you can always use the Nuget package manager.

Whilst the Xenial.Framework can of course be used in platform specific modules, for the purposes of this documentation emphasis will be given to its use in the platform agnostic module of your project.

TIP

If you use VisualStudio make sure you at least are using 2019 v16.9 or higher (opens new window). According to our tests VisualStudio 2022 or higher is recommended. You also need to install the .NET5 SDK (or greater) (opens new window) even if you want to use generators with net462 (Full Framework)

Warning

Make sure to restart VisualStudio after installing the SourceGenerator to make sure VisualStudio's intellisense recognizes the generator.
If your build is working fine, but your intellisense is giving you errors, you forgot to restart VisualStudio.

# Registration

As before the first task is to tell XAF to use the ListViewColumnBuilders.

Override the AddGeneratorUpdaters in the platform agnostic module and call the updaters.UseListViewColumnBuilders() extension method.








 



 




using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Model.Core;

namespace MyApplication.Module
{
    public sealed partial class MyApplicationModule : ModuleBase
    {
        public override void AddGeneratorUpdaters(ModelNodesGeneratorUpdaters updaters)
        {
            base.AddGeneratorUpdaters(updaters);

            updaters.UseListViewColumnBuilders();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Deriving from the ColumnsBuilder<TModelClass> class

After we installed the Xenial.Framework.Generators package, we need derive from ColumnsBuilder<TModelClass> class and declare it as partial as illustrated below;

















 





using DevExpress.Persistent.Base;
using DevExpress.Xpo;

using Xenial;
using Xenial.Framework.Layouts;
using Xenial.Framework.Layouts.Items;
using Xenial.Framework.Layouts.Items.Base;
using Xenial.Framework.Layouts.Items.LeafNodes;

namespace MainDemo.Module.BusinessObjects
{
    [Persistent]
    [DefaultClassOptions]
    [ListViewColumnsBuilder(typeof(PersonColumnsBuilder))]
    public class Person : XPObject { }

    public partial class PersonColumnsBuilder : ColumnsBuilder<Person>
    {
        public Columns BuildColumns() => new();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

We of course need to tell Xenial to use the builder;














 








using DevExpress.Persistent.Base;
using DevExpress.Xpo;

using Xenial;
using Xenial.Framework.Layouts;
using Xenial.Framework.Layouts.Items;
using Xenial.Framework.Layouts.Items.Base;
using Xenial.Framework.Layouts.Items.LeafNodes;

namespace MainDemo.Module.BusinessObjects
{
    [Persistent]
    [DefaultClassOptions]
    [ListViewColumnsBuilder(typeof(PersonColumnsBuilder))]
    public class Person : XPObject { }

    public partial class PersonColumnsBuilder : ColumnsBuilder<Person>
    {
        public Columns BuildColumns() => new();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Afterwards we can use the static Column class generated to access all the properties of TModelClass to define our columns. With the help of the Record-Syntax we can define additional properties;




















 
 
 
 
 
 
 
 




/**/

namespace MainDemo.Module.BusinessObjects;

public partial class PersonLayoutBuilder : LayoutBuilder<Person>
{
    public partial class PersonColumnsBuilder : ColumnsBuilder<Person>
    {
        public Columns BuildColumns() => new(new()
        {
            Caption = "All Persons",
            IsGroupPanelVisible = true,
            IsFooterVisible = true,
            ShowAutoFilterRow = true,
            ShowFindPanel = true,
            AutoExpandAllGroups = true
        })
        {
            /**/
            Column.FirstName with
            {
                Width = 70,
                SortOrder = ColumnSortOrder.Ascending
            },
            Column.LastName with { Width = 70 },
            Column.Phone with { Width = 30 },
            Column.Email with { Width = 30 }
        };
    }
}
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
30

To write columns for nested objects we need to declare the [Xenial.XenialExpandMember("XXX")] where the generated static Constants class get handy;





 
 








/**/

namespace MainDemo.Module.BusinessObjects
{
    [XenialExpandMember(Constants.Address1)]
    public partial class PersonColumnsBuilder : ColumnsBuilder<Person>
    {
        public Columns BuildColumns() => new()
        {
            /**/
        };
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

Afterwards we use the nested static Column._XXXX classes generated to access all the nested properties to define our layout;


















 










/**/

namespace MainDemo.Module.BusinessObjects;

[XenialExpandMember(Constants.Address1)]
public partial class PersonColumnsBuilder : ColumnsBuilder<Person>
{
    public Columns BuildColumns() => new(new()
    {
        Caption = "All Persons",
        IsGroupPanelVisible = true,
        IsFooterVisible = true,
        ShowAutoFilterRow = true,
        ShowFindPanel = true,
        AutoExpandAllGroups = true
    })
    {
        Column._Address1.City with
        {
            Caption = "Address",
            Index = -1,
            GroupIndex = 0,
            SortOrder = ColumnSortOrder.Ascending
        }
        /**/
    };
}
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

TIP

You can nest calls of [Xenial.XenialExpandMember] recursively if you have a deep object graph you need to display.

[XenialExpandMember(Constants.Address1)]
[XenialExpandMember(Constants._Address1.Country)]
[XenialExpandMember(Constants._Address1._Country.Currency)]
1
2
3

# Final result

using DevExpress.Persistent.Base;
using DevExpress.Xpo;

using Xenial;
using Xenial.Framework.Layouts;
using Xenial.Framework.Layouts.Items;
using Xenial.Framework.Layouts.Items.Base;
using Xenial.Framework.Layouts.Items.LeafNodes;

namespace MainDemo.Module.BusinessObjects;

[Persistent]
[DefaultClassOptions]
[ListViewColumnsBuilder(typeof(PersonColumnsBuilder))]
public class Person : XPObject { }

[XenialExpandMember(Constants.Address1)]
public partial class PersonColumnsBuilder : ColumnsBuilder<Person>
{
    public Columns BuildColumns() => new(new()
    {
        Caption = "All Persons",
        IsGroupPanelVisible = true,
        IsFooterVisible = true,
        ShowAutoFilterRow = true,
        ShowFindPanel = true,
        AutoExpandAllGroups = true
    })
    {
        Column._Address1.City with
        {
            Caption = "Address",
            Index = -1,
            GroupIndex = 0,
            SortOrder = ColumnSortOrder.Ascending
        },
        Column.FirstName with
        {
            Width = 70,
            SortOrder = ColumnSortOrder.Ascending
        },
        Column.LastName with { Width = 70 },
        Column.Phone with { Width = 30 },
        Column.Email with { Width = 30 }
    };
}
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46