Introduction
A User Control is a server control that can be authored using declarative style as an ASP.NET web page. They provide web developers a quick way to create server controls.
User controls suffer from minimal design-time support in VS.NET. Their properties cannot be edited using Property Grid. This article is an attempt to create Custom Designers for User Controls which allow to edit their properties using Property Grid.
Header User Control
The code below shows a Header User Control which exposes Heading
and SubHeading
properties to allow the user to provide a heading and subheading for the page.
VB.NET
<%@ Control Language="vb" AutoEventWireup="false"
Codebehind= "Header.ascx.vb" Inherits="CustomDesignersWebVB.Header" %>
<table align="center" ID= "tblHeader">
<tr>
<td><asp:label id="lblHeading" Font-Size="18" Font-Name="Arial"
Font-Bold="True" Runat="server">Heading</asp:label></td>
</tr>
<tr>
<td><asp:label id="lblSubHeading" Font-Size="14" Font-Name="Arial"
Font-Bold="True" Runat="server">Sub Heading</asp:label></td>
</tr>
</table>
Public Property Heading() As String
Get
Return lblHeading.Text
End Get
Set(ByVal Value As String)
lblHeading.Text = Value
End Set
End Property
Public Property SubHeading() As String
Get
Return lblSubHeading.Text
End Get
Set(ByVal Value As String)
lblSubHeading.Text = Value
End Set
End Property
C#
<%@ Control Language="C#" AutoEventWireup="false"
Codebehind= "Header.ascx.cs" Inherits="CustomDesignersWebCS.Header" %>
<table align="center" ID= "tblHeader">
<tr>
<td><asp:label id="lblHeading" Font-Size="18" Font-Name="Arial"
Font-Bold="True" Runat="server">Heading</asp:label></td>
</tr>
<tr>
<td><asp:label id="lblSubHeading" Font-Size="14" Font-Name="Arial"
Font-Bold="True" Runat="server">Sub Heading</asp:label></td>
</tr>
</table>
public string Heading
{
get
{
return lblHeading.Text;
}
set
{
lblHeading.Text=value;
}
}
public string SubHeading
{
get
{
return lblSubHeading.Text;
}
set
{
lblSubHeading.Text=value;
}
}
Creating a Custom Designer for Header User Control
Fire up Visual Studio .NET, use the File | New | Project menu item and select Web Control Library project template.
Add a Component Class to the project as shown below:
A Component Class is a class that inherits System.Component.Component
. It can be added to the toolbox of VS.NET and can be dragged and dropped onto the design surface, and when selected, its properties are displayed using Property Grid.
Following is the code listing for the HeaderDesigner
component class:
C#
private string _Heading ;
private string _SubHeading;
public string Heading
{
get
{
return _Heading;
}
set
{
_Heading=value;
}
}
public string SubHeading
{
get
{
return _SubHeading;
}
set
{
_SubHeading=value;
}
}
VB.NET
Private _Heading As String
Private _SubHeading As String
Public Property Heading() As String
Get
Return _Heading
End Get
Set(ByVal Value As String)
_Heading = Value
End Set
End Property
Public Property SubHeading() As String
Get
Return _SubHeading
End Get
Set(ByVal Value As String)
_SubHeading = Value
End Set
End Property
Header Designer exposes Heading
and SubHeading
properties to the user. The user sets these properties using a Property Grid.
Associating Designer with Header User Control
Open the project containing Header User Control. Add the HeaderDesigner
component to the Components tab in the Toolbox, as shown below:
Drag and drop the HeaderDesigner
to the design surface. Now you can set the Heading
and SubHeading
properties using the Property Grid as shown:
Open the code-behind for the page and add the following code:
C#
protected Header Header1;
private void Page_Load(object sender, System.EventArgs e)
{
if(!IsPostBack)
{
CustomDesignersCS.DesignerHelper.BindDesignerToControl(headerDesigner1,
Header1);
}
}
VB.NET
Protected Header1 As Header
Private Sub Page_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
If Not Page.IsPostBack Then
CustomDesignersVB.DesignerHelper.BindDesignerToControl(HeaderDesigner1, _
Header1)
End If
End Sub
BindDesignerToControl
function is the glue code which binds HeaderDesigner
properties to the user control properties. It uses Reflection to read Designer properties and associate them with the corresponding properties in the Header User Control.
C#
public class DesignerHelper
{
public static void
BindDesignerToControl(System.ComponentModel.Component designer, Control ctl)
{
BindDesignerToObject(designer, ctl);
}
public static void
BindDesignerToObject(System.ComponentModel.Component designer, Object obj)
{
PropertyDescriptorCollection colWebCtlPropDesc =
TypeDescriptor.GetProperties(obj);
PropertyDescriptorCollection coldesignerPropDesc =
TypeDescriptor.GetProperties(designer);
foreach(PropertyDescriptor pd in coldesignerPropDesc)
{
PropertyDescriptor webctlpd = colWebCtlPropDesc.Find(pd.Name, true);
if (webctlpd!=null)
{
webctlpd.SetValue(obj, pd.GetValue(designer));
}
}
}
}
VB.NET
Public NotInheritable Class DesignerHelper _
Public Shared Sub BindDesignerToControl(ByVal designer As _
System.ComponentModel.Component, ByRef ctl As Control)
BindDesignerToObject(designer, ctl)
End Sub
Public Shared Sub BindDesignerToObject(ByVal designer As _
System.ComponentModel.Component, ByRef obj As Object)
Dim colWebCtlPropDesc As PropertyDescriptorCollection = _
TypeDescriptor.GetProperties(obj)
Dim coldesignerPropDesc As PropertyDescriptorCollection = _
TypeDescriptor.GetProperties(designer)
For Each pd As PropertyDescriptor In coldesignerPropDesc
Dim webctlpd As PropertyDescriptor = _
colWebCtlPropDesc.Find(pd.Name, True)
If Not IsNothing(webctlpd) Then
webctlpd.SetValue(obj, pd.GetValue(designer))
End If
Next
End Sub
End Class
Points of Interest
While working on this article, I discovered that one can set the properties of BasePage
class (custom Page class which all pages derive from) using the Property Grid in WebForms Designer.
Future enhancements and feedback
I am planning to do the following enhancements when time permits:
- Creating a new property called "
UserControl
" in UserControlDesigner
, which lists all the UserControl
s in the Page
. User can pick the UserControl
he wants to bind to the designer from the dropdown.
- Creating a custom
CodeDOMSerializer
for the designer which automatically generates the binding code.
Feel free to email me your suggestions and comments. I would like to make improvements based on your feedback.
History