在 ASP.NET 中创建自定义用户控件设计器






4.43/5 (14投票s)
2005年3月22日
2分钟阅读

164219

1806
一篇关于在 ASP.NET 中创建自定义用户控件设计器的文章。

引言
用户控件是一种服务器控件,可以使用声明性样式作为 ASP.NET 网页来编写。它们为 Web 开发人员提供了一种快速创建服务器控件的方法。
用户控件在 VS.NET 中缺乏最小的设计时支持。它们的属性无法使用属性网格进行编辑。本文旨在为用户控件创建自定义设计器,从而允许使用属性网格编辑其属性。
页眉用户控件
下面的代码显示了一个页眉用户控件,它公开了 Heading 和 SubHeading 属性,允许用户为页面提供标题和副标题。
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;
    }
}
为页眉用户控件创建自定义设计器
启动 Visual Studio .NET,使用“文件 | 新建 | 项目”菜单项,然后选择“Web 控件库”项目模板。
如下所示,向项目添加一个组件类

组件类是继承 System.Component.Component 的类。它可以添加到 VS.NET 的工具箱中,并且可以拖放到设计图面上,选择后,其属性将使用属性网格显示。
以下是 HeaderDesigner 组件类的代码列表
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 向用户公开 Heading 和 SubHeading 属性。用户可以使用属性网格设置这些属性。
将设计器与页眉用户控件关联
打开包含页眉用户控件的项目。将 HeaderDesigner 组件添加到工具箱中的“组件”选项卡,如下所示

将 HeaderDesigner 拖放到设计图面上。现在,您可以使用属性网格设置 Heading 和 SubHeading 属性,如下所示

打开页面的代码隐藏文件,并添加以下代码
C#
protected  Header Header1;
private void Page_Load(object sender, System.EventArgs e)
{
    //Glue code to associate the Usercontrol with the Designer
    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 函数是将 HeaderDesigner 属性绑定到用户控件属性的粘合代码。它使用反射来读取设计器属性,并将它们与页眉用户控件中的相应属性相关联。
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)
    {
        //Get object properties using Reflection
        PropertyDescriptorCollection colWebCtlPropDesc = 
                      TypeDescriptor.GetProperties(obj);
        //Get Designer properties using Reflection
        PropertyDescriptorCollection coldesignerPropDesc = 
                   TypeDescriptor.GetProperties(designer);
        //Loop through all Designer properties
        //Each designer property corresponds to webcontrol property
        foreach(PropertyDescriptor pd in coldesignerPropDesc)
        {
                PropertyDescriptor webctlpd = colWebCtlPropDesc.Find(pd.Name, true);
            if (webctlpd!=null)
            {
                //Assign the designer property value to web control's property
                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)
        'Get object properties using Reflection
        Dim colWebCtlPropDesc As PropertyDescriptorCollection = _
                                 TypeDescriptor.GetProperties(obj)
        'Get Designer properties using Reflection
        Dim coldesignerPropDesc As PropertyDescriptorCollection = _
                              TypeDescriptor.GetProperties(designer)
        'Loop through Designer properties
        'Each designer property corresponds to webcontrol property
        For Each pd As PropertyDescriptor In coldesignerPropDesc
            Dim webctlpd As PropertyDescriptor = _
                           colWebCtlPropDesc.Find(pd.Name, True)
            If Not IsNothing(webctlpd) Then
                'Assign the designer property value to web control
                webctlpd.SetValue(obj, pd.GetValue(designer))
            End If
        Next
    End Sub
End Class
关注点
在撰写本文时,我发现可以使用 WebForms 设计器中的属性网格来设置 BasePage 类(所有页面都从中派生的自定义页面类)的属性。
未来的增强功能和反馈
我计划在时间允许的情况下进行以下增强
- 在 UserControlDesigner中创建一个名为“UserControl”的新属性,该属性列出Page中的所有UserControl。用户可以从下拉列表中选择他想要绑定到设计器的UserControl。
- 为设计器创建一个自定义 CodeDOMSerializer,它可以自动生成绑定代码。
请随时通过电子邮件向我发送您的建议和评论。我很乐意根据您的反馈进行改进。
历史
- 05 年 3 月 21 日 - 版本 1。
