Depending on the type of control, various settings must be done for creating a designer for an ASP.Net control or a custom control. This
topic provides sample code for illustration of the settings that need to be done in order to build a Control Designer in Sitefinity.
 |
In this topic, "ASP.Net controls" is used for both the standard ASP.Net controls and the controls provided in Sitefinity (currently not all of the
Sitefinity controls have designers implemented for them).
|
Building a Designer for ASP.Net Controls
To build a designer for an ASP.Net control, there are several settings to be done. A mapping of the module to the control designer should be added to the
web.config. Also, a template of that designer should be added to the project. Lastly, there are some important settings and inherited methods that should be
implemented in the designer's class. The following sections cover the essential settings of each of the described parts of a designer.
web.config Settings
In order to create a designer of an existing ASP.Net control, a mapping must be added to the web.config. At the controlDesigners tag,
add an <add> tag with key and value attributes for the control. The key attribute
represents the full name of the control type, while the value one represents the namespace and the assembly of the designer. The following code
illustrates the mapping for the Image control:
| web.config |
Copy Code |
|
<framework>
...
<controlDesigners>
<add key="System.Web.UI.WebControls.Image" value="Telerik.Cms.Web.UI.Design.ImageControlDesigner, Telerik.Cms.Web.UI" />
</controlDesigners>
</framework>
|
In the above code example, "System.Web.UI.WebControls.Image" is the full name of the Image type. "Telerik.Cms.Web.UI.Design.ImageControlDesigner" is the namespace where the
control designer is situated, and "Telerik.Cms.Web.UI" is its assembly.
Template Settings
The ImageControlDesigner.ascx file is a template and represents a mix of HTML elements and ASP.NET controls that make up the layout for a particular control
designer. Templates customize a control's user interface.
Add the template for the control in a special template folder in that control such as the following path:
/<your_project>/Sitefinity/Admin/ControlTemplates/ControlEditors/
| ImageControlDesigner.ascx |
Copy Code |
|
<div class="ctrlProps">
<div class="ctrlContent">
<div class="ctrlContentImage">
<div class="ctrlContentImageColumn">
<p>
<asp:Label AssociatedControlID="ImageUrl" runat="server" Text="<$Resources>"></asp:Label>
<asp:TextBox ID="ImageUrl" runat="server" CssClass="txt"></asp:TextBox>
<asp:LinkButton ID="picker" runat="server" Text="Select" CssClass="picker"></asp:LinkButton>
</p>
<p>
<asp:Label AssociatedControlID="AlternateText" runat="server" Text="AlternativeText>"></asp:Label>
<asp:TextBox ID="AlternateText" runat="server" CssClass="txt"></asp:TextBox>
<em><asp:Literal ID="Literal3" runat="server" Text="AlternativeTextDescription"></asp:Literal></em>
</p>
</div>
<div class="ctrlContentImageColumn">
<p class="image-preview">
<asp:Image ID="imagePreview" runat="server" ImageUrl="~/Sitefinity/Admin/Themes/Default/Images/noSelectedImage.gif" />
</p>
<p>
<asp:Literal ID="imageName" runat="server"></asp:Literal>
</p>
</div>
</div>
</div>
</div>
|
The .ascx template does not inherit from ControlDesigner (an abstract class). ControlDesigner represents the base class
for Sitefinity custom control designers and all custom property editors should inherit it (see the following section in this topic - Designer Class Settings). The
access to the corresponding resource file and the value for the specified key are provided by Resources. For example, put in "<%$ %>" tags
"Resources:SelectImage" - this will get the string value for SelectImage resource key. This should be done for all values of "Text=" - for
AlternativeText, Select, etc.
The composite control for the given Control Designer loads the template describing its appearance as an external file with LoadTemplate method of
Page class. The other possible approach for creating server control interface is to hard-code it. For example:
| Hard-coded Interface |
Copy Code |
|
LinkButton lnkCreateNewList = new LinkButton();
lnkCreateNewList.ID = "newListButton";
lnkCreateNewList.Text = "Create New List"; this.Controls.Add(lnkCreateNewList);
|
Designer Class Settings
The Designer class must inherit the abstract class ControlDesigner. The most important method in it is GetControlDesigner. It first
checks for a mapping of a control to a designer in the web.config file. If such exists, the type name of that designer is taken. If not, the collection of
deign-time attributes is checked looking for ControlDesignerAttribute (see the Building a Designer for Custom Controls section
below). If no designer is found, then only the regular Properties tab is displayed.
| ControlDesigner |
Copy Code |
|
public abstract class ControlDesigner :
ControlDesignerBase
{
...
public static ControlDesigner GetControlDesigner(Type type)
{
if (type != null)
{
string typeName = string.Empty;
ControlDesignerCollection designerProviders = ConfigHelper.Handler.ControlDesigners;
if (designerProviders.ContainsKey(type.FullName))
{
typeName = designerProviders[type.FullName].Value;
}
else
{
System.ComponentModel.AttributeCollection coll =
TypeDescriptor.GetAttributes(type);
if (coll != null)
{
foreach (Attribute attr in coll)
{
if (attr is ControlDesignerAttribute)
typeName = ((ControlDesignerAttribute)attr).DesignerTypeName;
}
}
}
if (typeName != string.Empty)
return (ControlDesigner)Activator.CreateInstance(
TypeResolutionService.ResolveType(typeName));
}
return null;
}
|
The ControlDesigner class inherits ControlDesignerBase class. The base class handles the saving and the loading of the control state. One
important property is DesignedControl which gets or sets the designed control. An important method is the SetProperty one. It sets a
new value for a specific property of the control. Another important method is GetEditorType. It gets the type name of the specified property. This will be useful
when using a Selector in order to know what type of property is to be displayed.
| ControlDesignerBase |
Copy Code |
|
namespace Telerik.Framework.Web.Design
{
public abstract class ControlDesignerBase :
CompositeControl
{
#region Properties
...
public Control DesignedControl
{
get { return this.control; }
set
{
this.control = value;
if (this.control != null && this.control.Parent != null)
{
this.SaveControl();
this.control = null;
this.LoadControl();
}
if (base.ChildControlsCreated)
base.RecreateChildControls();
}
}
#endregion
...
#region Helper methods
/// <param name="component">The component with the property value that is to be set.</param>
/// <param name="properties">A collection of component properties.</param>
/// <param name="name">Property name.</param>
/// <param name="value">Property value.</param>
public virtual void SetProperty(object component, PropertyDescriptorCollection properties, string name, object value, out
PropertyDescriptor desc)
{
desc = properties.Find(name, false);
desc.SetValue(component, value);
}
public virtual string GetEditorType(PropertyDescriptor desc)
{
string typeName = string.Empty;
WebEditorAttribute editorAttr = (WebEditorAttribute)desc.Attributes[typeof(WebEditorAttribute)];
if (editorAttr != null)
typeName = editorAttr.EditorTypeName;
return typeName;
}
...
|
If Selector is used, the updating will/should be done in the method SelectedLists_RowCommand().
In the Designer class, the method base.OnPropertyChanged(EventArgs.Empty) is used for synchronization of the settings. In this way, when a the settings in the
Basic tab of the module's properties are changed, the same property in the Advanced tab will be automatically changed as well.
| base.OnPropertyChanged |
Copy Code |
|
void EditorDialog_PropertyChanged(object source,
PropertyValueChangedEventArgs e)
{
PropertyDescriptor desc;
this.SetProperty(component,
properties, e.PropertyName, e.PropertyValue, out desc);
if (e.PropertyName ==
"ImageUrl")
this.SetImagePreview(e.PropertyValue.ToString());
base.OnPropertyChanged(EventArgs.Empty);
}
|
Building a Designer for Custom Controls
The Custom Control must declare its control designer using design-time attributes. The following code shows how the ListDisplay class declares its control
designer before the class definition:
| ListDisplay Attributes |
Copy Code |
|
using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Text; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; using System.IO; [assembly: WebResource("Telerik.Lists.Resources.ExpandableList.js", "application/x-javascript")] namespace Telerik.Lists.WebControls
{
using Telerik.Lists.Resources;
using Telerik.WebControls;
using Telerik.Cms.Web.UI;
using Telerik.Framework.Web.Design;
//[ToolboxItem(typeof(ListDisplayToolboxItem))]
/// <summary>
/// Public control that displays lists on the page
/// </summary>
[ToolboxData("<{0}:ListDisplay
runat=\"server\"></{0}:ListDisplay>")]
[DefaultProperty("ListsIds")]
[ControlDesignerAttribute("Telerik.Lists.Design.ListsControlDesigner, Telerik.Lists")]
public class ListDisplay : CompositeControl, IListDisplay,
IEmptyControl
{ ... }
|
This class is created for the Lists module. Using the [ControlDesignerAttribute("")] attribute, a declaration is passed of the full name of the
designer as well as the assembly of the module (in this case of the Lists module).
See Also