Tuesday, July 6, 2010

Xaf tip 13-Developing Platform Independent Code

Xaf supports for now only 2 platforms for the moment and this post will focus to a technique that can be used to write code that is platform independent same for web and win and is aiming advance xafers . Since xaf is 4 years old right now I am sure many of you fit in that category.

As you all of you know xaf v10 is a whole new framework very very clean and powerful but still has a lot of missing stuff in its architecture but also have many other powerful stuff that can help in providing workarounds. And this is the most perfect time to use them cause i am sure in the next months Devexpress will make it very very friendly to newbie as well.

The problem

You are developing a new module say A for both platforms so you are going to create 3 projects in your visual studio

A,
A.Win,
A.Web

The function of the module will be just to load a control into a view and the control will be given by your client. That means that the you have to store a System.Type to your application model.

Lets deifne our model

public interface IModelApplicationA {

        IModelControl ModelControl { get; }

    }

 

    public interface IModelControl {

        Type ControlType { get; set; }

    }


Now we have to register the above model to our platform independent module that is A with a code like

public override void ExtendModelInterfaces(DevExpress.ExpressApp.Model.ModelInterfaceExtenders extenders)

{

    base.ExtendModelInterfaces(extenders);

    extenders.Add<IModelApplication, IModelApplicationA>();

}

that will generate a model for us like

image

as you the model is generated but the ControlType is not editable. Why? Cause the model cannot represent System.Type we have to convert it. How? With a TypeConverter (.Net is wonderful!!) and Xaf v10 is also wonderful now that its model can speak .Net!!!

lets change the signature of IModelControl interface to

public interface IModelControl:IModelNode

{

    [TypeConverter(typeof(StringToTypeConverter))]

    Type ControlType { get; set; }

}

StringToTypeConverter is provided by Devexpress and does exactly what the name say converts a type to a string

image

Now as you can see ControlType is editable. We are good up to here but StringToTypeConverter by Devexpress to convert types of persistent objects and if we not pass a datasource that will contain the controls we want it is going to provide us a list of persistent object types and we do not want that.

image

lets change the signature of our interfaces one more time to provide our own datasource

public interface IModelControl:IModelNode

{

    [DataSourceProperty("ControlTypes")]

    [TypeConverter(typeof(StringToTypeConverter))]

    Type ControlType { get; set; }

    [Browsable(false)]

    IEnumerable<Type> ControlTypes { get; }

}

see what i have done? I created a new non browsable enumerations of ControlTypes and added the DataSourceProperty attribute over ControlType to point to that enumeration.

What is next? To provide the types for the ControlTypes enumeration. In v10 that is done by using the DomainLogic attribute.so we have to write something like

[DomainLogic(typeof(IModelControl))]

public class ControlTypesDomainLogic {

    public static List<Type> Get_ControlTypes(IModelControl modelNode) {

 

    }

}

But remember where we are ? We are still developing at out platform independent module A and that domain logic above is going to return controls that are specific to web or win platforms. How the heck are we are going to do that?

one may say come on man just write 2 domain logic classes for each platform and move them to their specific modules. Hahaha I will say that is not supported from current version of Xaf.

Lets say we have develop 2 controls per platform and we want our client to select from those 2.

ControlAWin,ControlBWin
ControlAWeb,ControlBWeb

As i said we want to continue developing at platform independent module A cause Xaf current version does not support DomainLogic in different assemblies than the one that the interface that is applied upon lives. But there must be a solution I can sense it.

How about if we create a dummy interface in module A and assign it to our controls in A.Win,A.Web then we could search AppDomain for all types that implement IDummy and return that list.

IEnumerable<Type> types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(assembly => assembly.GetTypes()).Where(type => typeof (IDummy).IsAssignableFrom(type));

hmmm i really not like that solution although can work cause i think it is going to be awfully slow. But I have another solution more elegant and more out of the box.

DevExpress.ExpressApp.XafTypesInfo what it does is for all loaded assemblies gets all types and creates ITypeInfo wrappers around them  which have an Implementors property that really fits our needs. So lets use the powerful XafTypesInfo provided by DevExpress.

So our domain logic class will become

[DomainLogic(typeof(IModelControl))]

public class ControlTypesDomainLogic {

    public static List<Type> Get_ControlTypes(IModelControl modelNode) {

        return XafTypesInfo.Instance.FindTypeInfo(typeof(IDummy)).Implementors.Select(info => info.Type).ToList();

    }

}

image

excellent!!! as you see we got our types and we still writing code at a platform independent module. What left is to subscribe to a xaf event responsible for adding the controls and add the controls using some .Net reflection

public class AddControlsToAViewController:ViewController {

    protected override void OnFrameAssigned(){

        base.OnFrameAssigned();

        Frame.TemplateChanged += FrameOnTemplateChanged;

    }

 

    void FrameOnTemplateChanged(object sender, EventArgs eventArgs){

        var supportViewControlAdding = (Frame.Template) as ISupportViewControlAdding;

        if (supportViewControlAdding != null)

            supportViewControlAdding.ViewControlAdding += SupportViewControlAddingOnViewControlAdding;

    }

 

    void SupportViewControlAddingOnViewControlAdding(object sender, EventArgs eventArgs) {

        if (View is DetailView) {

            var viewSiteTemplate = Frame.Template as IViewSiteTemplate;

            if (viewSiteTemplate == null)

                return;

            object viewSiteControl = viewSiteTemplate.ViewSiteControl;

            if (viewSiteControl != null) {

                IModelControl modelControl = ((IModelApplicationA)Application.Model).ModelControl;

                object instance = Activator.CreateInstance(modelControl.ControlType);

                object value = viewSiteControl.GetType().GetProperty("Controls").GetValue(viewSiteControl, null);

                value.GetType().GetMethod("Add").Invoke(value, new[] { instance });

            }

        }

    }

}

So in bottom line our case was platform independent--> (add controls to a view) and we wrote as platform independent code as it can be and very clean!!!

Happy xafing!!!

Subscribe to XAF feed
Subscribe to community feed

DiggIt!