Customizing Orchard shapes

Tags: orchard, shapes, tutorial

Shapes are the basic building blocks of Orchard UI. They can be easily created and further reused by just putting an appropriate .cshtml file in your module /Views folder. This is nicely described here and here. But what if you’d want to arm your shape with some more fancy logic (eg. let them contain other shapes/objects, make them auto-wrapped with some Html tags, allow fine-grained overriding and such)? I’ll try to show you how to get the most from your Orchard shapes.

Creating a custom IShapeTableProvider

This is the main extension points for adding some custom logic to shapes. What’s the most important thing – not only your shapes! You can also alter the behavior of existing ones – coolUśmiech! Basically, you are able to eg. add custom wrappers, class names, attributes, fire some custom logic on shape lifetime events and so on.

The place everything happens in is the Discover(ShapeTableBuilder builder) method. You operate on the provided builder object to create all shapes’ customizations. The basic structure looks like this:

public class MyShapes : IShapeTableProvider {
    public void Discover(ShapeTableBuilder builder)
    {
        builder.Describe("MyShape")
	    .Configure(descriptor => { })
            .OnCreated(context => { })
            .OnCreating(context => { })
            .OnDisplayed(context => { })
            .OnDisplaying(context => { });
    }
}

As you can see the builder adds a nice, fluent interface for specifying the necessary stuff. First, you have to tell the builder which shape you’d like to alter by using a Describe() method. “MyShape” name I provided corresponds to /Views/MyShape.cshtml shape file. The way shape names are constructed from the corresponding .cshtml file names is described in detail here (section “From template file name to shape name”).

Next, you are free to add your own logic inside one or more of the five provided methods. For each of those methods you can specify a delegate (or lambda/anonymous method as shown above) which will get called at an appropriate moment:

  • Configure – used to add some config options to your shape. Mostly used to define wrappers (other shapes that wrap your shape) via descriptor.Wrappers collection.
  • OnCreating – called when your shape is about to be created. This event is mostly used for providing some custom shape construction info, like context.ShapeType (the type of object you want to be the base type of this shape – nice if you’d like to add some custom properties to the shape) or context.Behaviors (collection of IClayBehavior implementations for defining behavior when accessing shape members - pretty low-level stuff).
  • OnCreated – called when your shape is fully created, but not pushed to display yet. Generally, all display structure is defined here (eg. shape zones, startup items, child items placement and such)
  • OnDisplaying – called when your shape is about to be displayed. Mainly used for adding custom CSS classes (via context.Shape.Classes) or defining shape alternates to allow fine-grained overriding by your module users. What are shape alternates is described here.
  • OnDisplayed – called when your shape Html just got rendered.

Next, you can operate on the provided context object to add necessary customizations. That’s it!Uśmiech

The most important thing - you can access the corresponding dynamic Shape object by context.Shape. The Shape property is not available in OnCreating event though (for obvious reasons).

There are more methods, of course, but these are the most useful ones. In most cases you won’t ever need to use the others.

As an example I’d advise you to take a look at CoreShapes.cs, located in Orchard.Core/Shapes. It contains the definitions for the most common shapes and nice examples of how you can customize your own shapes!Uśmiech

Cheers!

2 Comments

  • springy said

    I don't remember which shapes but I had some (maybe code defined CoreShapes?) which didn't trigger OnCreated events, but OnDisplaying event got triggered.

  • pszmyd said

    @springy: Thanks for info, I had that once too.

    It is because maybe you declared your shapes inside a theme, not module? Shapes customizations, which are declared in themes don't fire the OnCreated/OnCreating events - only the OnDisplaying/OnDisplayed ones.

    It is a complex issue connected with shape table creation and I've already posted it on Codeplex.