Friday, September 16, 2011

eXpandFrameWork Supporting Unbound Columns

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 UnboundColumnSynchronizer: 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

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!

Subscribe to XAF feed
Subscribe to community feed

DiggIt!

0 comments:

Post a Comment