# ListViewColumnBuilders - Simple Columns

As has been stated Xenial.Framework is designed to be flexible and to minimize overheads. This is exemplified by the simple columns approach of ListViewColumnBuilders.

The first task is to tell XAF to use the ColumnBuilders.

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

# Defining the builder method

With that done declare a public static method in the business object class for which the layout is to be created, called BuildColumns, that returns a Xenial.Framework.Layouts.Columns instance and decorate the business object with the ListViewColumnsBuilderAttribute.

The ListViewColumnsBuilderAttribute defines the method and type that is responsible for building the ListView.










 


 
 
 
 



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

using Xenial.Framework.Layouts;

namespace MainDemo.Module.BusinessObjects
{
    [Persistent]
    [DefaultClassOptions]
    [ListViewColumnsBuilder]
    public class Person : XPObject
    {
        public static Columns BuildColumns()
        {
            return new Columns();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

After registering the builder and restarting the application (recall that XAF requires an application restart to register and apply changes to metadata) there is now an empty ListView because as yet there is no code within the ListViewColumnBuilders to construct the view.

Person Void Columns

TIP

There are some overloads for stricter registration patterns.

WARNING

If a blank page is not visible at this stage, make sure that the Model.DesignedDiffs.xafml files (also in the Win project) for this ListView have no differences and be sure to delete or disable the User differences file.

This file is usually located in the Application output directory called and named Model.User.xafml.

# Building the columns

All the components used to build the columns are normal C# classes and have been designed to work well with C#'s initializer syntax as illustrated in the code below.

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

using Xenial.Framework.Layouts;
using Xenial.Framework.Layouts.ColumnItems;

namespace DXApplication6.Module.BusinessObjects
{
    [Persistent]
    [DefaultClassOptions]
    [ListViewColumnsBuilder]
    public class Person : XPObject
    {
        public static Columns BuildColumns()
        {
            return new Columns(new ListViewOptions()
            {
                Caption = "All Persons",
                IsGroupPanelVisible = true,
                IsFooterVisible = true,
                ShowAutoFilterRow = true,
                ShowFindPanel = true,
                AutoExpandAllGroups = true
            })
            {
                new Column($"{nameof(Address1)}.{nameof(Address.City)}")
                {
                    Caption = "Address",
                    Index = -1,
                    GroupIndex = 0,
                    SortOrder = ColumnSortOrder.Ascending
                },
                new Column(nameof(FirstName))
                {
                    SortOrder = ColumnSortOrder.Ascending,
                    Width = 70
                },
                new Column(nameof(LastName))
                {
                    Width = 70
                },
                new Column(nameof(Phone))
                {
                    Width = 30
                },
                new Column(nameof(Email))
                {
                    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
47
48
49
50
51
52
53
54

This may appear to be a very verbose and long syntax pattern (Xenial.Framework does provide a more compact and advanced syntax patterns, see the reference for the used classes for more details) which will be examined in greater detail shortly.

Before that examination look at the result:

Person Target Columns

# Columns-Code-Review

The Columns class is the container for the columns.



 





    public static Columns BuildColumns()
    {
        return new Columns
        {
            /* ... */
        }
    }
1
2
3
4
5
6
7

TIP

From C#6 it has been possible to use Expression-bodied members (opens new window) to shorten the syntax to:

public static Columns BuildColumns() => new Columns {};
1

The basic building blocks for defining layouts are currently only the ListViewOptions and Column classes.




 





 






    public static Columns BuildColumns()
    {
        return new Columns(
            new ListViewOptions
            {
                /* ... */
            }
        )
        {
            new Column("PropertyName")
            {
                /* ... */
            }
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

TIP

There are several Column properties that can be specified like Width and SortOrder, GroupIndex, DisplayFormat etc.

The Columns class is a collection. So you can use initializer syntax, or use the default Add method called by the initializer.

# Other registrations

If the convention based BuildColumns is not suitable , there is the option to provide a custom method name by passing it as a parameter to the ListViewColumnsBuilderAttribute.










 


 






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

using Xenial.Framework.Layouts;

namespace MainDemo.Module.BusinessObjects
{
    [Persistent]
    [DefaultClassOptions]
    [ListViewColumnsBuilder(nameof(BuildMyListViewColumns))]
    public class Person : XPObject
    {
        public static Columns BuildMyListViewColumns()
        {
            return new Columns();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

TIP

The creation of columns in code can lead to large code files. Layout code can be moved to a separate file using the partial class pattern (opens new window).

ListViewColumnBuilders can be created in a separate class if, for example, there is a requirement to split XPO/XAF into separate assemblies, by providing the type of the class:










 


 
 
 
 
 
 
 


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

using Xenial.Framework.Layouts;

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

    public static class PersonColumnsBuilder
    {
        public static Columns BuildMyListViewColumns()
        {
            return new Columns();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

TIP

The convention based naming approach also works for external types by just removing the target method name [ListViewColumnsBuilder(typeof(PersonColumns))].
Then of course the method name would be BuildColumns in the PersonColumns class.

# Declaring LookupListViews

Because LookupListViews are basically just ListViews that follow a specific naming convention Xenial.Framework provides a convenience attribute that has it's own convention by using the LookupListViewColumnBuilderAttribute which expects an BuildLookupColumns method, but otherwise follows the exact same semantics of the ListViewColumnsBuilderAttribute.










 


 






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

using Xenial.Framework.Layouts;

namespace MainDemo.Module.BusinessObjects
{
    [Persistent]
    [DefaultClassOptions]
    [LookupListViewColumnsBuilder]
    public class Person : XPObject
    {
        public static Columns BuildLookupColumns()
        {
            return new Columns();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18