Wednesday, August 7, 2013

Flexible-fast layouts with CSS styles in the XAF application model

XAF has a build-in mechanism for customizing the layout’s. However nothing can be compared with the power of native CSS styles. In this post I will discuss eXpand’s implementation of CSS styles in the XAF application model.

How XAF renders a layout

To render a web layout XAF uses the WebLayoutManager class. This class queries all the IModelViewLayoutElements of a DetailView.Model.Layout and renders WebControls accordingly. For example for an IModelLayoutViewItem which is an interface that links the layout element with a property editor, the WebLayoutManager will render two controls. The first one will hold the caption/label and the second one will be the actual control depending on the type of the property editor. Take a look how an looks IModelLayoutViewItem in the Model Editor.

image

As you can see XAF supports a lot of attributes by default! Due to the large number of attributes WebLayoutManager introduces a table to hold and position our controls and a div above it to allow more flexibility in configuration.

It is also possible to use more more interfaces to design a layout. In the next image we see what is supported by default. However WebLayoutManager behavior is the same.

image

 

What we need to style?

Of course everything fast-flexible and without introducing complexities and XAF application model is the perfect tool for this. To summurize the previous paragraph we may wish to style

  1. The actual WebControl
  2. The label of the WebControl
  3. The container table
  4. The container div
  5. The whole view

How we get control instances

All XAF controls are rendered by property editors. For the web there is an abstract WebPropertyEditor (see a simplistic inheritance view).
image
It is enough to create a Controller then query all WebPropertyEditor ViewItems and subscribe to ControlCreated event. When the event raised we can do webPropertyEditor.Control and get the instance we need. Let’s see this a pseudo code snippet.

public class LayoutStyleController:ViewController<DetailView>,IModelExtender {

 

    protected override void OnActivated() {

        base.OnActivated();

        foreach (var item in View.GetItems<WebPropertyEditor>()) {

            item.ControlCreated+=ItemOnControlCreated;

        }

    }

 

    void ItemOnControlCreated(object sender, EventArgs eventArgs) {

        var webPropertyEditor = ((WebPropertyEditor) sender);

        webPropertyEditor.ControlCreated-=ItemOnControlCreated;

     //here we have our webcontrol  instance

        var control = webPropertyEditor.Control;

    }

How to get the layout container

WebLayoutManager is designed with the highest standards, so it is as easy as subscribing to its LayoutCreated event and getting the value of the Container property

protected override void OnActivated() {

    base.OnActivated();

    View.LayoutManager.LayoutCreated += LayoutManager_LayoutCreated;

}

 

void LayoutManager_LayoutCreated(object sender, EventArgs e) {

    View.LayoutManager.LayoutCreated-=LayoutManager_LayoutCreated;

     //here we have our webcontrol  instance

    WebControl container = (WebControl) View.LayoutManager.Container;

}

How to get the rest of the objects we need to style

All the rest are only known from the WebLayoutManager which renders them using an advanced template mechanism. It is of course possible to override it however I want to continue working with pluggable controllers. So I will create an interface to help me parse those templates from a Controller.

public interface IWebLayoutManager {

    event EventHandler<TemplateInstantiatedEventArgs> Instantiated;

}

 

public class XpandLayoutManager : WebLayoutManagerIWebLayoutManager {

    ViewItemsCollection _detailViewItems;

 

    public event EventHandler<TemplateInstantiatedEventArgs> Instantiated;

 

    protected virtual void OnInstantiated(TemplateInstantiatedEventArgs e) {

        var handler = Instantiated;

        if (handler != null) handler(this, e);

    }

    protected override LayoutBaseTemplate CreateLayoutItemTemplate() {

        var layoutBaseTemplate = base.CreateLayoutItemTemplate();

        layoutBaseTemplate.Instantiated += LayoutBaseTemplateOnInstantiated;

        return layoutBaseTemplate;

    }

 

    protected override LayoutBaseTemplate CreateLayoutGroupTemplate() {

        var layoutBaseTemplate = base.CreateLayoutGroupTemplate();

        layoutBaseTemplate.Instantiated += LayoutBaseTemplateOnInstantiated;

        return layoutBaseTemplate;

    }

    void LayoutBaseTemplateOnInstantiated(object sender, TemplateInstantiatedEventArgs templateInstantiatedEventArgs) {

        OnInstantiated(templateInstantiatedEventArgs);

    }

Also I need to plug this custom XpandLayoutManager to my WebApplication descendant as below.

public class XpandWebApplication : WebApplication {

    protected override LayoutManager CreateLayoutManagerCore(bool simple) {

        return new XpandLayoutManager();

    }

Now it is possible to subscribe to to XpandLayoutManager Instanciated event from a controller and parse the templates to discover the label, the container table and the container div.

Extending the model

Having all the WebControls instances we want to style it is time to extend the model with a few interfaces so to control the styling from there. Bellow is the interface we need

public interface IModelLayoutStyle : IModelNode {

    FontStyle FontStyle { get; set; }

    Color FontColor { get; set; }

    Color BackColor { get; set; }

    string CssClass { get; set; }

    string Style { get; set; }

}

This interface can be used to extend IModelDetailView and IModelLayoutGroup to control the style of the whole view and of a grouped layout element as illustrated below.

public class LayoutStyleController:ViewController<DetailView>,IModelExtender {

     public void ExtendModelInterfaces(ModelInterfaceExtenders extenders) {

         extenders.Add<IModelDetailView, IModelLayoutStyle>();

         extenders.Add<IModelLayoutGroup, IModelLayoutStyle>();

     }

Now for the IModelLayoutViewItem as we discussed in the beginning there are three controls we need to style (actual, label and container). So we need to introduce another interface and to extend the IModelLayoutViewItem like the next one.

public interface IModelLayoutViewItemStyle {

    ILayoutStyle LayoutStyle { get; }

}

 

public interface ILayoutStyle:IModelNode {

    IModelLayoutStyle Container { get;  }

    IModelLayoutStyle Control { get;  }

    IModelLayoutStyle Caption { get; }

}

After extending the IModelLayoutViewItem the Model Editor now will display the next structure.

image

Finally it’s time to write the StyleProvider class and put them all in a reusable controller. However I will skip adding so much code in a post and I will redirect you to eXpandFramework GitHub repository (just grab the LayoutStyleController.cs  and extend the WebLayoutManager as  discussed above).

Demo

Bellow you can see a cool view a designed with CSS in the Application Model and the http://apobekiaris.blogspot.gr/2013/07/detailview-as-preview-in-gridview.html.

Always with zero code lines! and without fixed dimensions – totally relative!

image

Those of you that come often to eXpand forums will recognize for sure that this is a replicate of the Kunena forums (see for your self Smile)!

image

This functionality is available with eXpandFramework 13.1.5.19.

Until next time,

Happy XAF’ing to all!

Subscribe to XAF feed
Subscribe to community feed

DiggIt!

0 comments:

Post a Comment