# ListViewColumnBuilders - Record Syntax

C#9 introduced a new record syntax (opens new window) which has been implemented within Xenial.Framework ColumnBuilders ensuring that columns can be built using with expressions. Although not very different from initializers they make it possible to create a copy of a given record, which is particularly beneficial to a clean fluent syntax in combination with a functional style API.

# Setting the compiler options

Using this feature requires the compiler version to be set in the project. By default the framework will choose the compiler level based on the .NET version being used but it can be overridden by setting the LangVersion (opens new window) property in the*.csproj files.

By far the best way to use this feature is to create a Directory.Build.props file in the same location as the application *.sln file:

<Project>
  <PropertyGroup>
    <LangVersion>9.0</LangVersion>
    <!--<LangVersion>latest</LangVersion> Alternative: just use the latest version, if you want the latest and greatest -->
  </PropertyGroup>
</Project>
1
2
3
4
5
6

For more information on this topic please look at the Microsoft Documentation (opens new window)

TIP

To ensure that the compiler is picked up correctly close VisualStudio, delete all bin and obj folders and then restart VisualStudio.

CAUTION

Whilst it is possible to use this in projects targeting .net frameworks below net5 (by adding a class called IsExternalInit in the project) it is not officially supported by Microsoft:

#if !NET5

using System.ComponentModel;

namespace System.Runtime.CompilerServices
{
    /// <summary>
    /// Reserved to be used by the compiler for tracking metadata.
    /// This class should not be used by developers in source code.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    internal static class IsExternalInit
    {
    }
}
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

WARNING

All code should be thoroughly tested after changing the compiler version.

Microsoft has done a great job trying not to break any existing client project, but because it is not supported on the old full framework (.NET4xx) officially, use this technique at your own risk.

# Registration

Registration is exactly the same as in the previous examples, 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

# Build the Columns

As this uses C#9 it is now possible to use the Target-typed new expressions feature (opens new window) which removes a little bit of redundancy in the code as shown below:

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

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]
    public class Person : XPObject
    {
        private static ColumnsBuilder<Person> b = new();
        public static Columns BuildColumns() => new(new()
        {
            Caption = "All Persons",
            IsGroupPanelVisible = true,
            IsFooterVisible = true,
            ShowAutoFilterRow = true,
            ShowFindPanel = true,
            AutoExpandAllGroups = true
        })
        {
            b.Column(m => m.Address1.City, "Address") with
            {
                Index = -1,
                GroupIndex = 0,
                SortOrder = ColumnSortOrder.Ascending
            },
            b.Column(m => m.FirstName, 70) with
            {
                SortOrder = ColumnSortOrder.Ascending
            },
            b.Column(m => m.LastName, 70),
            b.Column(m => m.Phone, 30),
            b.Column(m => m.Email, 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

The syntax is more concise than the initializer syntax and it is a little more structured. It's combining both the power of expression trees to specify type safe layouts, as well as a familiar syntax comparable to initializers.

# A mixed sample

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

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]
    public class Person : XPObject
    {
        private static ColumnsBuilder<Person> b = new();
        public static Columns BuildColumns() => new(new()
        {
            Caption = "All Persons",
            IsGroupPanelVisible = true,
            IsFooterVisible = true,
            ShowAutoFilterRow = true,
            ShowFindPanel = true,
            AutoExpandAllGroups = true
        })
        {
            b.Column(m => m.Address1.City, "Address") with
            {
                Index = -1,
                GroupIndex = 0,
                SortOrder = ColumnSortOrder.Ascending
            },
            b.Column(m => m.FirstName, 70, c =>
            {
                c.SortOrder = ColumnSortOrder.Ascending;
            }),
            new Column(nameof(LastName))
            {
                Width = 70
            },
            b.Column(m => m.Phone, 30),
            b.Column(m => m.Email, 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

WARNING

Whilst this sample works without issue mixing syntax styles is not recommended. It may work from a technical standpoint but it adds complexity and harms readability.

Wherever possible coding styles and conventions should be clearly defined and adhered to.