eXpandFrameWork Supporting Unbound Columns

Let me describe for a moment how we at DevExpress work. We build and sell software which means that we only sell and provide support for products that have been built and tested by us! However I am here as a framework evangelist and huge XAF fan. This makes it my duty to spread the word as much as I can and make XAF even bigger. To this end through collaboration within the XAF community, we have been building and supporting eXpand. This framework follows XAF to the letter and takes things even further. eXpand gets its inspiration from real life situations and bases itself on examples from DevExpress Support Center. eXpand is the first open source project based on the DevExpress eXpressApp Framework (XAF). More info is available at www.expandframework.com and our very existence relies on your efforts! Anyone is welcome to contribute and enjoy the rewards. It is not necessary to be a XAF guru, we can all manage to create a behavior taken from DevExpress code central. Let’s work together to enhance our beloved XAF!

Recently in Xpand forums Dionisis Soldatos raised a question about how unbound columns can be implemented with XAF. Unbound columns along with their UnboundExpression can be used for creating calculated fields even at runtime. Since we are talking about unbound grid columns it should be obvious that we will operate at the UI level by modifying the grid control columns. However lets do a deep dive inside XAF model to extend it as needed!

The Model

By now we are all used to XAF providing us with excellent out of the box solutions which negate the need for us to write hundredths of lines of code. This of course means money saved during developing and ultimately your product hits the market faster. Why spend time reinventing the wheel when the XAF team have already done the hard work for you?

XAF creates the model by reading the metadata of our classes, this model has 3 types of view. One of these is the ListView which can be displayed with data source enabled controls like Grid controls. ListView has columns which correspond to existing object properties metadata and when XAF creates a Grid at runtime it queries model’s ListView columns. It then creates and configures Grid columns from their attributes. These stages are well tested and it is preferable to use them in our solution and override the unnecessary stages. For example we could create a normal model column node using XAF default Add/Column menu. After the Grid column is created it we simply need a few lines of code to make it unbound and set its Unbound Expression.

 

image

 

In order to store this expression we still need to extend model’s ListView with an attribute. The model can be extended either by registering an interface at ModuleBase.ExtendModelInterfaces or by deriving it from an already registered interface. I am going to take the latter options by deriving from IModelColumn interface which I will explain as we go.

public interface IModelColumnUnbound : IModelColumn {

 

    [Category("eXpand")]

    bool ShowUnboundExpressionMenu { get; set; }

 

    [Category("eXpand")]

    [Required]

    string UnboundExpression { get; set; }

}

XAF model editor is a highly sophisticated tool which has the capability to recognize that we extended the model. It then takes care of the vital step of adding an entry to the Add menu for creating Unbound columns.

image

Now it is possible to create a new type of column with 2 extra attributes as shown,

image

Moving on we need to set the mandatory PropertyName attribute shown above to an always existing object property name. Remember XAF requires this in order to behave as designed. To this end we are going to set as PropertyName the object’s key property name using this simple DomainLogic class,

[DomainLogic(typeof(IModelColumnUnbound))]

public class IModelColumnUnboundLogic {

    public static string Get_PropertyName(IModelColumnUnbound columnUnbound) {

        return ((IModelListView)columnUnbound.Parent.Parent).ModelClass.KeyProperty;

    }

As a result (PropertyName, PropertyEditorType and Caption) attributes will be populated the next time we create a ColumnUnbound Node. However these will be fixed values and it is preferable to hide them from the end user. At the same time we need to mark Caption attribute as required and remove its default value. To do all of this we just need to extend our IModelColumnUnbound interface like this,

image

Note; Although PropertyName and Caption belong to IModelColumn using the new operator it is possible to override them!

We have now finished with the model modifications and for our ColumnUnbound nodes XAF by design will create a new column pointing back to object’s key property metadata.

The UI

A key benefit of XAF’s commitment to design patterns, specifically to the Single responsibility principle, is that it provides us with the model’s synchronizer classes. These can be used to synchronize our model with the control and vice versa. It is only necessary to derive from the abstract ModelSyncroniser<T,V> and implement ApplyModeCore method to synchronize the control and from SynchronizeModel to do the same with the model.

public class c: ModelSynchronizer<GridListEditor, IModelListView> {

    public UnboundColumnSynchronizer(GridListEditor control, IModelListView model)

        : base(control, model) {

    }

 

    protected override void ApplyModelCore() {

        var xafGridColumns = GetXafGridColumns();

        foreach (var column in xafGridColumns) {

            var modelColumnUnbound = (IModelColumnUnbound)column.Model;

            column.FieldName = modelColumnUnbound.Id;

            column.UnboundType = UnboundColumnType.Object;

            column.OptionsColumn.AllowEdit = false;

            column.ShowUnboundExpressionMenu = modelColumnUnbound.ShowUnboundExpressionMenu;

            column.UnboundExpression = modelColumnUnbound.UnboundExpression;

        }

    }

 

    IEnumerable<XafGridColumn> GetXafGridColumns() {

        IEnumerable<XafGridColumn> xafGridColumns =

            Model.Columns.OfType<IModelColumnUnbound>().Select(

                unbound => Control.GridView.Columns[unbound.PropertyName] as XafGridColumn).Where(column => column != null);

        return xafGridColumns;

    }

 

    public override void SynchronizeModel() {

        var xafGridColumns = GetXafGridColumns();

        foreach (var xafGridColumn in xafGridColumns) {

            ((IModelColumnUnbound) xafGridColumn.Model).UnboundExpression = xafGridColumn.UnboundExpression;

        }

    }

}

 

 

The above code uses the GetXafGridColumns method to return the grid columns that correspond to IModelColumnUnbound nodes. The web implementation is very similar and can be found here.

All that is left is to register our UnboundColumnSynchronizer like this,

public class UnboundColumnController : ViewController<ListView> {

    protected override void OnActivated() {

        base.OnActivated();

        var gridListEditor = View.Editor as GridListEditor;

        if (gridListEditor != null)

            gridListEditor.CreateCustomModelSynchronizer += GridListEditorOnCreateCustomModelSynchronizer;

    }

 

    void GridListEditorOnCreateCustomModelSynchronizer(object sender, CreateCustomModelSynchronizerEventArgs createCustomModelSynchronizerEventArgs) {

        createCustomModelSynchronizerEventArgs.ModelSynchronizer = new UnboundColumnSynchronizer((GridListEditor)sender, View.Model);

    }

}

 

Note; Setting ShowUnboundExpressionMenu to true is only supported by Windows platform. There, an end user can modify the UnBoundExpression by invoking Grid’s expression editor

image image

Together with the unbound column Xpand allows for up to 5 different approaches to creating calculated fields. In the next post we will discuss the pros and cons of each approach so stay tuned!

We are happy to read your feedback about this!. Remember that your questions are the best candidates for future posts

4 comment(s)
Willem de Vries

Interesting, but what can the user do to specify what is shown in the unbound column?

Could you share a sample or show some screen shots?

Willem

16 September, 2011
Mohsen Benkhellat

Thanks Tolis for sharing this.

I was thinking of using the grid' expression editor to modify the alias expression of dynamically created columns but not sure if it will not generate expressions not supported by XPO (in server mode).

What is your take on this?

16 September, 2011
Apostolis Bekiaris (DevExpress)

Thanks for the feedback guys

@Mohsen Benkhellat;

Short answer is yes as long as the expression uses persistent properties (see also S136026)

@Willem de Vries

I will update the post shortly

16 September, 2011
Dionisios Soldatos

Work Fine !

Bravo!

17 September, 2011

Please login or register to post comments.