This is specific to version 3.6 hotfix. There is an
updated article about 3.6 SP1.
Since there have been quite a lot of questions about this in the support tickets, and the documentation might take some time to be upgraded, I decided to show how to wrap a Generic Content control.
First of all,
in the existing documentation, an obsolete method is suggested. I advise you not to use it, as it causes a lot of problems.
Here is what has to be done at a glance:
- Create a custom control that inherits GenericContent
- Add template support to it
- Add custom functionality
- Override Render
In this example I will add a very basic functionality on top of the GenericContent control, as the purpose of this article is to show how to accomplish this, not to be a showcase.
It is important to note that, due to the current implementation of GenericContentDesigner, it is not possible to preview correctly the contents of such a wrapper in edit (design) mode.
Create a custom control that inherits GenericContent
Let us first determine our goal: we want to use the GenericContent control designer, but we don’t want to have simple text only. What we need is to add some functionality on top of the basic GenericContent. Here, I will show how to:
- use an embedded template
- reuse the functionality of the GenericConent control, and
- plug in the contents of a control in arbitrary place in our template
Now that we have our goals set, we need to examine our base class more closely – namely, the GenericContent control.
GenericContent inherits directly from
WebControl and implements the
IEmptyControl interface. Since GenericContent was designed to hold only formatted text, it doesn’t support Embedded Templates, and uses the Render method to directly write its content. While there is nothing wrong with this approach, we need to be aware of this limitation if we are going to inherit from GenericContent and extend it. Implementers of IEmptyControl can write an arbitrary message (including html) when the control holds no data (which is determined by the IEmptyControl.IsEmpty property implementation) in design mode.
The attributes that are applied to GenericContent are:
ControlDesignerAttribute and
ToolboxItemAttribute. They are the key to achieving the goals we set before. The specified GenericContentDesigner provides the familiar interface for editing a GenericContent control: entering text in RadEditor, sharing the written content and using already shared content. Since it works only with GenericConent classes, and there is no way to circumvent it (including overriding conversion operators), we will inherit from GenericContent. The second attribute is crucial. It specifies the type of the control that will be used once it is dragged from the toolbox and dropped onto a ContentPlaceholder. Since it is inheritable, we need to re-apply it and specify the type of our class, or the page editor will generate a GenericContent control instead.
A limitation that comes from the GenericContentDesigner, is that the contents of the RadEditor will be the rendered output of the control at design time. That is why our control cannot be properly rendered at design time. This is where the IEmptyControl. SetEmptyControlDefaultMessage method comes into play: we will warn the user of this limitation.
Here is what our code will look like so far:
Adding embedded templates support
Normally, you will inherit either from SimpleControl, ViewModeControl<T> or ViewModeUserControl<T>, and therefore – have support for embedded templates out of the box. If this were the case, you would only need to override LayoutTemplateName and LayoutTemplatePath and apply an EmbeddedTemplateAttribute (or derivative) to the latter. The template name would hold the relative path to the template file (see where our template is found in the solution explorer and compare it to the value of the GCWrapperLayoutTemplatePath constant). The attribute would hold the same relative path plus some descriptive information. LayoutTemplatePath holds the current url of the template, if it is not embedded. In most cases, you would use the base implementation and apply EmbeddedTemplateAttrubute.
Since we have to inherit from GenericControl, which doesn’t inherit from either of the above mentioned classes, we need to implement this by ourselves. Here comes another drawback from inheriting from GenericContent’s base – WebControl, and not from CompositeControl. If we overrode CreateChildControls, it would never be called!
These are the properties that are needed for embedded templates support:
- Container
- LayoutTemplate
- LayoutTemplatePath
- LayoutTemplateName
Two of those we already discussed. Let us explain the two remaining: LayoutTemplate is to eventually return an ITemplate implementer, and the Container will hold the template data (the template will be initialized in the container).
LayoutTemplate has to be marked with TemplateContainerAttribute, which defines the type we will use for our Container properly – namely, an IGenericContainer implementer.
Container is used to load controls from the embedded template.
There is a problem – as stated above, we need to somehow make the Container be a part of our Controls collection (that is – rendered on the page), and this can’t be done in CreateChildControls. A workaround is described later in Override Render.
Here is what our very simple template looks like:
The first label, title, is a custom title that will be displayed above the content. The second label, content, is used to hold the contents of the generic content control.
Note that it is important to compile this file as an embedded resource. To do so:
- Open solution explorer
- Navigate to the template file
- Right-click and choose Properties from the context menu
- Set Build Action to Embedded Resource
In this step we add the following code:
Add custom functionality
We have already added the functionality by adding the title label in the template, so we simply load the two labels from the template and add another to control them. And set the default property for this class.
Here is what the code looks like:
Override Render
In Render, we have to do the following things:
• If in normal mode, render the template and the two labels
• If in normal mode, plug in the contents of the generic control in the content label
• If in design mode, render GenericContent only
Everything is explained so far, so let the code speak for itself:
How do we extract the contents of the generic content control? There is more than one way, but the chosen one here is to render it to a string, then plug in the rendered output as the content label’s text.
How do we render the template? Well, we render all child controls and then the layout (the Container).
Since GenericContentDesigner sets its RadEditor content to the rendered output of the GenericContent control’s output, we have to do as expected. This is the reason the control won’t render normally in design mode.
Conclusion
This is a very basic example, which leaves actual customization of the looks of the control as an exercise for the reader. If you are not familiar
how to apply css to Sitefinity pages, watch this web cast.
I know there are limitations that come from inheriting GenericContent, but in the end, this is the very reason to write this article – to show you that it is not difficult to accomplish it once you know what you have to do.
Related Links:
3.6 Dev Manual Preview