65.9K
CodeProject 正在变化。 阅读更多。
Home

自定义用户控件布局

2006年2月20日

3分钟阅读

viewsIcon

48589

downloadIcon

390

一篇关于通过特殊模板自定义用户控件的文章。基本上,这是一种封装用户控件的简单技术。

Sample Image

引言

设计网页是一回事,设计美观的页面和控件是另一回事,这始终是评估 Web 应用程序可用性和外观的重要因素之一。通常,Web 开发人员不太注意制作美观的页面和控件,而且维护整个应用程序的外观和感觉也是我们都知道的一个真正令人头疼的问题。此外,如今,我们不断收到关于自定义布局和个性化页面以及主题、着色和布局的需求。通过本文,我将尝试介绍一种通过 IDE 设计的模板轻松定义和自定义控件的简单技术。

换句话说

自定义用户控件无非是封装一个控件,具有丰富的布局、边框、标题和页脚。

仔细查看解决方案

要使此技术适用于您的应用程序,您需要为您的 Web 控件提供一个基类控件,您可以在其中控制控件的渲染行为。除了具有一个属性来保存模板 ID 并覆盖 Render 方法以通过模板的服务应用模板之外,此控件没有特别之处。

public class BaseUserControl: UserControl, IBaseUserControl
{
    #region Private fields
    private string _templateId = "";
    private string _title = "";
    #endregion Private fields

    #region Public properties
    public string TemplateId
    {
        get 
        {
            return _templateId;
        }
        set 
        {
            _templateId = value;
        }
    }

    
    public string Title
    {
        get 
        {
            return _title;
        }
        set 
        {
            _title = value;
        }
    }
    #endregion Public properties

    protected override void Render(HtmlTextWriter writer)
    {
        //Apply template only when _templateId is not null.
        if( _templateId != null)
        {
            CustomizedControlsTemplates.Global.
              UserControlTemplate.ApplyTemplate(this, 
              writer, _templateId);
        }
        else
        {
            base.Render (writer);
        }
    }
    
    
    //The basic render method, to be called from 
    //the template services, without causing cyclic calls
    public void RenderUserControl(HtmlTextWriter writer)
    {
        base.Render (writer);
    }
}


public interface IBaseUserControl
{
    void RenderUserControl(HtmlTextWriter writer);
}

BaseUserControl 类中,Render 方法将检查 _templateId 并调用 ApplyTemplate,其中方法 RenderUserControl 代表原始的 Render 方法。

嗯,您可能已经注意到,BaseUserControl 实现了接口 IBaseUserControl,这强制用户控件实现方法 RenderUserControl

什么是模板?

模板只不过是普通的用户控件,带有一个占位符,该占位符可以在运行时替换为任何用户控件,因此模板类似于

<script runat="server"> 

    protected string hitTime = DateTime.Now.ToString("hh:mm:ss"); 

    //overriding the method
     
    public override void Control_Loaded() 
    {
 
        base.MainPlaceHolder = MainPlaceHolder; 
 
        hitTime = DateTime.Now.ToString("hh:mm:ss"); 
 
    }
</script>

<TABLE style="BORDER-COLLAPSE: collapse" cellSpacing="0" 
              cellPadding="0" border="0" ID="Table1">
    ...
    ...
    <TR>
        <TD width="2" background= "images/left.gif" height="100%"></TD>
        <TD     width="145" height="16">
          <asp:PlaceHolder id="MainPlaceHolder" runat= "server">
          </asp:PlaceHolder>
        </TD> 
        <TD width="4" background="images/right.gif" height="100%"></TD>
    </TR> 
    ...
    ...
</TABLE>

自定义模板应该继承一个基类模板 MyTemplate,该模板实现接口 IMyTemplate。 主要是,MyTemplate 类将实现方法 SetPlaceHolderControl,该方法覆盖占位符的 Render 方法。新的 Render 方法不会渲染占位符,而是通过调用方法 RenderUserControl 来渲染用户控件,以避免 RenderApplyTemplate 之间的循环调用。

public class MyTemplate: UserControl,IMyTemplate
{
    //the place holder to represent the user control 
    //location in the template.
    public System.Web.UI.WebControls.PlaceHolder 
            MainPlaceHolder = new PlaceHolder();

    //the LoadedBaseControl will be accessible 
    //from the templates. 
    public BaseUserControl LoadedBaseControl;
    
    public MyTemplate()
    {
    }

    protected override void Render(HtmlTextWriter writer)
    {
        //nothing specific here.
        base.Render (writer);
    }

    private void BaseControlRenderMethod(HtmlTextWriter 
                              output, Control container)
    {
        //just render the user control.
        LoadedBaseControl.RenderUserControl(output);
    }

    //the following virtual method, allow 
    //flixible access to the controls.
    virtual public void Control_Loaded()
    {

    }

    virtual public void SetPlaceHolderControl(BaseUserControl 
                           userControl, HtmlTextWriter writer)
    {
        LoadedBaseControl = userControl;
        
        //call back to the template, to allow 
        //developers to have flixible access
        //to the rendered control.
        Control_Loaded();

        //specify the rendering method to the place holder.
        MainPlaceHolder.SetRenderMethodDelegate( new 
                RenderMethod(BaseControlRenderMethod));

        //and now, render the place holder.
        this.Render(new HtmlTextWriter(writer));
    }
}

模板可以通过覆盖方法 Control_Loaded 来在开始渲染控件之前执行一些逻辑。

加载模板

在 Web 应用程序启动时可以加载在 Web 应用程序中定义的模板。 这些模板可以放在哈希表中以供以后参考,如下面的代码片段所示

public class Global : System.Web.HttpApplication
{
    //the folloing is just a pointer 
    //to the UserControlTemplate service.
    public static IUserControlTemplate UserControlTemplate = 
                        new CustomizedUserControlTemplate();

    //define the default template id for the applicarion.
    public static string DefaultTemplateId = "SampleTemplate1";
    //the following string array holds all defined templates id.
    public static string [] AvailableTemplatesId = 
           new string[] {"SampleTemplate1","SampleTemplate2"};
    //the folowing hash table used to hold the templates objects.
    public static Hashtable AvailableTemplates = 
          new Hashtable(AvailableTemplatesId.Length);

    ...

    protected void Application_Start(Object sender, EventArgs e)
    {
        //load all templates in the AvailableTemplates.
        foreach(string templateId in AvailableTemplatesId)
        {
            if( AvailableTemplates.Contains(templateId) == false)
            {
                System.Web.UI.UserControl buc = 
                                new System.Web.UI.UserControl();
                AvailableTemplates.Add(templateId, 
                  buc.LoadControl("~/" + templateId + ".ascx"));
            }
        }
    }
    ...
}

Global 类中,我将所有模板 ID 保存在一个字符串数组 AvailableTemplatesId 中,并且 AvailableTemplates Hashtable 将保存加载的模板。

最后要说的话

CustomizedUserControlTemplate 类将像服务提供程序一样工作,以根据指定的模板 ID 应用请求的模板。 此外,它可以用于为所有控件设置默认模板。 方法 ApplyTemplate 将验证模板是否存在,并从缓存中加载它,然后通过 SetPlaceHolderControl 应用模板。

使用模板

Web 控件可以通过设置属性 TemplateId 来告知要使用的模板 ID,或者通过将此属性设置为空白来应用默认模板。

我提供了一个示例来展示两个不同的模板,页面“TestControlsTemplates.aspx”包含一个简单的用户控件,请查看此页面以了解如何在模板之间切换。

© . All rights reserved.