Thursday, April 17, 2008

Creating a Rendering Template that supports Edit and Display modes

I have been working on a Field Control rendering template that works for both Edit and Display modes.  Along the way I learned quite a few things.

First I am not covering the steps to create a SharePoint field control.  You can find a walk-through for creating a custom field control here.

DefaultTemplateName is not used in Display mode.

The first problem I ran into was the rendering of my control in Display mode.  I had put in an override for the DefaultTemplateName property in my control.  The documentation was vague but seemed to imply that the Display rendering template would be pulled from this value.

Well that is not the case.  To get my custom rendering template loaded in Display mode I had to override the DisplayTemplateName property. 

Once I had both DefaultTemplateName and DisplayTemplateName overridden I was in full control of the display.

Mixing the TemplateContainer and EditModePanel objects

With my rendering template I thought it would be best to just create one rendering template for both Edit and Display modes.  The main reason I did this was so I could encapsulate all of the markup for a control in one rendering template. 

My rendering template was nothing more than a ASP.Net User Control (ASCX file).  By using a user control I had a nice container to edit the HTML mark-up for my control.

To give myself different looks for Edit and Display modes I decided to use the Publishing EditModePanel control.  EditModePanels are a nice way of isolating a set of mark-up based on the page mode. 

I put my EditModePanel controls inside my RenderingTemplate control.  This is very important because it caused me grief later on.

<SharePoint:RenderingTemplate ID="VolvoCWPRelatedLinkControl" runat="server">
<Template>
<SharePointPublishing:EditModePanel ID="RelatedLinkEditPanel" runat="server" PageDisplayMode="Edit" SuppressTag="true">

In the CreateChildControls routine of my control I needed to instantiate classes that were mapped to the controls in my ASCX file.  According to the walk-through sample I should use the TemplateContainer.FindControl routine.  Well this did not work because my controls were sitting inside an Edit Mode Panel.


To make this work I had to first find the Edit Mode Panel and then use the FindControl routine in its class.


this.empPanel = (EditModePanel)Utility.FindAndValidateControl(TemplateContainer, "RelatedLinkDisplayPanel");
this.empPanel.PreRender += new EventHandler(RelatedLinkDisplayPanel_PreRender);

this.lblTitle = (Label)Utility.FindAndValidateControl(this.empPanel, "tbRelatedLinkTitle");

this.repLinks = (Repeater)Utility.FindAndValidateControl(this.empPanel, "repRelatedLinkItems");

The FindAndValidateControl is a routine I wrote to do call the FindControl routine and make sure that the value passed back is not null. 


public static object FindAndValidateControl(System.Web.UI.Control controlContainer, string controlName)
{
object returnValue = controlContainer.FindControl(controlName);
if (returnValue == null)
throw new ConfigurationErrorsException(string.Format("{0} not found. Corrupt control template.", controlName));

return returnValue;
}


Once I figured out that the parent container mattered I was on my way.  But it took me a few times staring at the "Control not found error" before I figured out what was going on.  Just in case you are wondering I have not done a lot of ASP.Net control development so I realize I may have made a rookie mistake.


ItemFieldValue vs. Value

The next problem I ran into was a difference in the way the BaseFieldControl Value and ItemFieldValue properties work. 


In my control I had put an override in for the Value property (per the Custom Control Walk-through).  This works great when you are in Edit mode (I assume it also works when in New mode).  But when in Display mode I discovered that the Value property was not being set.  The documentation clearly states that when the control is loaded the Value is supposed to be given the same value as the ItemFieldValue property.  Well this did not happen in my control.


To get around the issue I created a special handler for my Display Mode panel pre-render event.


public void RelatedLinkDisplayPanel_PreRender(object item, EventArgs e)
{
EnsureChildControls();

RelatedLinkValue fieldValue = new RelatedLinkValue(ItemFieldValue.ToString());
QueryService queryService = new QueryService();
DataTable queryResults = queryService.GetCategoryItems(fieldValue.Category, PropertyService.RelatedLinkPath, "PublishingPageContent");

this.lblTitle.Text = fieldValue.Title;
this.repLinks.DataSource = queryResults;
this.repLinks.DataBind();

}

In the PreRender event I call the EnsureChildControls (for the same reason why it should be called in the Value override) and I use the ItemFieldValue property.


Conclusion

I was able to consolidate my Edit and Display mode rendering templates into one control without too much effort.  I did run into some gotchas, but nothing that made me think I had made a big mistake.


 

1 comments:

ballance said...

I was beginning to think the FindAndValidateControl(string) function was part of the framework until I saw your post. It probably should be, as checking for null is an important step to have added here. Thanks for the clarification.