Ejemplo de estilos con establecimiento inflexible de tipos para controles secundarios

Actualización: noviembre 2007

Este ejemplo muestra cómo crear un control denominado StyledRegister que implementa propiedades de estilo con establecimiento inflexible de tipos en un control compuesto. Estas propiedades permiten a los desarrolladores de páginas personalizar el aspecto de los controles secundarios del control compuesto. Los estilos con tipo también permiten ampliar fácilmente los controles compuestos. A medida que se agregan más controles secundarios al control compuesto, resulta más difícil exponer las numerosas propiedades de cada control secundario en el control compuesto. En lugar de ello, puede encapsular muchas propiedades en una misma propiedad de estilo.

El control StyledRegister del ejemplo es similar al control Register descrito en Ejemplo de control Web compuesto. El control StyledRegister tiene dos controles secundarios TextBox y un control secundario Button. La clase StyledRegister expone las propiedades ButtonStyle y TextBoxStyle para permitir a los desarrolladores de páginas establecer las propiedades Font, ForeColor y otras propiedades relacionadas con el estilo de los controles secundarios TextBox y Button.

El desarrollador de páginas puede establecer las propiedades Font-Names, ForeColor y BackColor de los controles del control StyledRegister como se muestra en el ejemplo siguiente:

<aspSample:StyledRegister ID="StyledRegister1" runat="server">
  <TextBoxStyle Font-Names="Arial" BorderStyle="Solid"  
    ForeColor="#804000"></TextBoxStyle>
  <ButtonStyle Font-Names="Arial" BorderStyle="Outset" 
    BackColor="Silver"></ButtonStyle>
</aspSample:StyledRegister>

Tal como se muestra en el ejemplo anterior, las propiedades de estilo con tipo se conservan como elementos secundarios en las etiquetas del control. Para obtener información sobre la implementación de las propiedades que se conservan de esta forma, vea la implementación de la propiedad Author en Ejemplo de propiedades de controles de servidor.

Un estilo con tipo es una propiedad del tipo Style o de un tipo derivado de Style. La clase Style expone propiedades relacionadas con el aspecto como Font, ForeColor y BackColor. La propiedad ControlStyle de la clase WebControl es del tipo Style. Las propiedades Font, ForeColor y BackColor de un control Web son subpropiedades de la propiedad ControlStyle, aunque también se exponen como propiedades de nivel superior de la clase WebControl.

Dado que la clase Style tiene subpropiedades, las propiedades de tipo Style requieren la administración de estado personalizada, como se describe en Ejemplo de administración personalizada de estados de propiedad. Los detalles de la implementación de los estilos con tipo se describen en la sección "Descripción del código" situada más adelante en este tema.

// StyledRegister.cs
using System;
using System.ComponentModel;
using System.Drawing;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Samples.AspNet.CS.Controls
{
    [
    AspNetHostingPermission(SecurityAction.Demand,
        Level = AspNetHostingPermissionLevel.Minimal),
    AspNetHostingPermission(SecurityAction.InheritanceDemand, 
        Level=AspNetHostingPermissionLevel.Minimal),
    DefaultEvent("Submit"),
    DefaultProperty("ButtonText"),
    ToolboxData(
        "<{0}:StyledRegister runat=\"server\"> </{0}:StyledRegister>"),
    ]
    public class StyledRegister : CompositeControl
    {
        private Button submitButton;
        private TextBox nameTextBox;
        private Label nameLabel;
        private TextBox emailTextBox;
        private Label emailLabel;
        private RequiredFieldValidator _emailValidator;
        private RequiredFieldValidator _nameValidator;
        private Style _buttonStyle;
        private Style _textBoxStyle;

        private static readonly object EventSubmitKey = new object();

        #region Properties delegated to child controls
        [
        Bindable(true),
        Category("Appearance"),
        DefaultValue(""),
        Description("The text to display on the button.")
        ]
        public string ButtonText
        {
            get
            {
                EnsureChildControls();
                return submitButton.Text;
            }
            set
            {
                EnsureChildControls();
                submitButton.Text = value;
            }
        }

        [
        Bindable(true),
        Category("Default"),
        DefaultValue(""),
        Description("The user name.")
        ]
        public string Name
        {
            get
            {
                EnsureChildControls();
                return nameTextBox.Text;
            }
            set
            {
                EnsureChildControls();
                nameTextBox.Text = value;
            }
        }

        [
        Bindable(true),
        Category("Appearance"),
        DefaultValue(""),
        Description(
            "The error message of the name validator.")
        ]
        public string NameErrorMessage
        {
            get
            {
                EnsureChildControls();
                return _nameValidator.ErrorMessage;
            }
            set
            {
                EnsureChildControls();
                _nameValidator.ErrorMessage = value;
                _nameValidator.ToolTip = value;
            }
        }

        [
        Bindable(true),
        Category("Appearance"),
        DefaultValue(""),
        Description("The text for the name label.")
        ]
        public string NameLabelText
        {
            get
            {
                EnsureChildControls();
                return nameLabel.Text;
            }
            set
            {
                EnsureChildControls();
                nameLabel.Text = value;
            }
        }

        [
        Bindable(true),
        Category("Default"),
        DefaultValue(""),
        Description("The e-mail address.")
        ]
        public string Email
        {
            get
            {
                EnsureChildControls();
                return emailTextBox.Text;
            }
            set
            {
                EnsureChildControls();
                emailTextBox.Text = value;
            }
        }

        [
        Bindable(true),
        Category("Appearance"),
        DefaultValue(""),
        Description(
            "Error message of the e-mail validator.")
        ]
        public string EmailErrorMessage
        {
            get
            {
                EnsureChildControls();
                return _emailValidator.ErrorMessage;
            }
            set
            {
                EnsureChildControls();
                _emailValidator.ErrorMessage = value;
                _emailValidator.ToolTip = value;
            }
        }

        [
        Bindable(true),
        Category("Appearance"),
        DefaultValue(""),
        Description("The text for the e-mail label.")
        ]
        public string EmailLabelText
        {
            get
            {
                EnsureChildControls();
                return emailLabel.Text;
            }
            set
            {
                EnsureChildControls();
                emailLabel.Text = value;

            }
        }
        #endregion

        #region Typed Style properties
        [
        Category("Styles"),
        DefaultValue(null),
        DesignerSerializationVisibility(
            DesignerSerializationVisibility.Content),
        PersistenceMode(PersistenceMode.InnerProperty),
        Description(
            "The strongly typed style for the Button child control.")
        ]
        public virtual Style ButtonStyle
        {
            get
            {
                if (_buttonStyle == null)
                {
                    _buttonStyle = new Style();
                    if (IsTrackingViewState)
                    {
                        ((IStateManager)_buttonStyle).TrackViewState();
                    }
                }
                return _buttonStyle;
            }
        }

        [
        Category("Styles"),
        DefaultValue(null),
        DesignerSerializationVisibility(
            DesignerSerializationVisibility.Content),
        PersistenceMode(PersistenceMode.InnerProperty),
        Description(
            "The strongly typed style for the TextBox child control.")
        ]
        public virtual Style TextBoxStyle
        {
            get
            {
                if (_textBoxStyle == null)
                {
                    _textBoxStyle = new Style();
                    if (IsTrackingViewState)
                    {
                        ((IStateManager)_textBoxStyle).TrackViewState();
                    }
                }
                return _textBoxStyle;
            }
        }
        #endregion

        #region Event definition
        // The Submit event.
        [
        Category("Action"),
        Description("Raised when the user clicks the button")
        ]
        public event EventHandler Submit
        {
            add
            {
                Events.AddHandler(EventSubmitKey, value);
            }
            remove
            {
                Events.RemoveHandler(EventSubmitKey, value);
            }
        }

        // The method that raises theSubmit event.
        protected virtual void OnSubmit(EventArgs e)
        {
            EventHandler SubmitHandler =
                (EventHandler)Events[EventSubmitKey];
            if (SubmitHandler != null)
            {
                SubmitHandler(this, e);
            }
        }

        // Handles the Click event of the Button and raises
        // the Submit event.
        private void _button_Click(object source, EventArgs e)
        {
            OnSubmit(EventArgs.Empty);
        }
        #endregion

        #region Overridden methods

        protected override void RecreateChildControls()
        {
            EnsureChildControls();
        }


        protected override void CreateChildControls()
        {
            Controls.Clear();

            nameLabel = new Label();

            nameTextBox = new TextBox();
            nameTextBox.ID = "nameTextBox";

            _nameValidator = new RequiredFieldValidator();
            _nameValidator.ID = "validator1";
            _nameValidator.ControlToValidate = nameTextBox.ID;
            _nameValidator.Text = "*";
            _nameValidator.Display = ValidatorDisplay.Static;

            emailLabel = new Label();

            emailTextBox = new TextBox();
            emailTextBox.ID = "emailTextBox";

            _emailValidator = new RequiredFieldValidator();
            _emailValidator.ID = "validator2";
            _emailValidator.ControlToValidate = emailTextBox.ID;
            _emailValidator.Text = "*";
            _emailValidator.Display = ValidatorDisplay.Static;

            submitButton = new Button();
            submitButton.ID = "button1";
            submitButton.Click += new EventHandler(_button_Click);

            this.Controls.Add(nameLabel);
            this.Controls.Add(nameTextBox);
            this.Controls.Add(_nameValidator);
            this.Controls.Add(emailLabel);
            this.Controls.Add(emailTextBox);
            this.Controls.Add(_emailValidator);
            this.Controls.Add(submitButton);
        }

        protected override void Render(HtmlTextWriter writer)
        {
            AddAttributesToRender(writer);

            writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding,
                "1", false);
            writer.RenderBeginTag(HtmlTextWriterTag.Table);

            if (_buttonStyle != null)
            {
                submitButton.ApplyStyle(ButtonStyle);
            }

            if (_textBoxStyle != null)
            {
                nameTextBox.ApplyStyle(TextBoxStyle);
                emailTextBox.ApplyStyle(TextBoxStyle);
            }

            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            nameLabel.RenderControl(writer);
            writer.RenderEndTag();  // Closing td.
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            nameTextBox.RenderControl(writer);
            writer.RenderEndTag();  //Closing td.
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            _nameValidator.RenderControl(writer);
            writer.RenderEndTag();  //Closing td.
            writer.RenderEndTag();  //Closing tr.

            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            emailLabel.RenderControl(writer);
            writer.RenderEndTag();  //Closing td.
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            emailTextBox.RenderControl(writer);
            writer.RenderEndTag();  //Closing td.
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            _emailValidator.RenderControl(writer);
            writer.RenderEndTag();  //Closing td.
            writer.RenderEndTag();  //Closing tr.

            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
            writer.AddAttribute(HtmlTextWriterAttribute.Colspan,
                "2", false);
            writer.AddAttribute(HtmlTextWriterAttribute.Align,
                "right", false);
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            submitButton.RenderControl(writer);
            writer.RenderEndTag();  //closing td.
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            writer.Write("&nbsp;");
            writer.RenderEndTag();  //closing td.
            writer.RenderEndTag();  //closing tr.

            writer.RenderEndTag();  //closing table.
        }
        #endregion

        #region Custom state management
        protected override void LoadViewState(object savedState)
        {
            if (savedState == null)
            {
                base.LoadViewState(null);
                return;
            }
            else 
            {
                Triplet t = savedState as Triplet;

                if (t != null)
                {
                    // Always invoke LoadViewState on the base class even if 
                    // the saved state is null.
                    base.LoadViewState(t.First);

                    if ((t.Second) != null)
                    {
                        ((IStateManager)ButtonStyle).LoadViewState(t.Second);
                    }

                    if ((t.Third) != null)
                    {
                        ((IStateManager)TextBoxStyle).LoadViewState(t.Third);
                    }
                }
                else
                {
                    throw new ArgumentException("Invalid view state .");
                }
            }
        }

        protected override object SaveViewState()
        {
            object baseState = base.SaveViewState();
            object buttonStyleState = null;
            object textBoxStyleState = null;

            if (_buttonStyle != null)
            {
                buttonStyleState =
                    ((IStateManager)_buttonStyle).SaveViewState();
            }

            if (_textBoxStyle != null)
            {
                textBoxStyleState =
                    ((IStateManager)_textBoxStyle).SaveViewState();
            }

            return new Triplet(baseState,
                buttonStyleState, textBoxStyleState);

        }

        protected override void TrackViewState()
        {
            base.TrackViewState();
            if (_buttonStyle != null)
            {
                ((IStateManager)_buttonStyle).TrackViewState();
            }
            if (_textBoxStyle != null)
            {
                ((IStateManager)_textBoxStyle).TrackViewState();
            }
        }
        #endregion
    }
}


El control StyledRegister muestra los pasos principales siguientes en la implementación de estilos con tipo para controles secundarios:

  • Definir las propiedades de tipo Style o de los tipos derivados de Style.

  • Implementar la administración de estado para las propiedades de estilo.

  • Aplicar estilos a los controles secundarios.

Dado que el tipo Style implementa la interfaz IStateManager, el modelo para definir una propiedad del tipo Style es idéntico al descrito en Ejemplo de administración personalizada de estados de propiedad. Una propiedad de estilo con tipo se define como propiedad de sólo lectura almacenada en un campo privado. En el descriptor de acceso de la propiedad, se crea una nueva instancia de Style sólo si el campo correspondiente a la propiedad es null (Nothing en Visual Basic). Si el control ha iniciado el seguimiento de estados, se llama al método TrackViewState de la instancia de Style que se acaba de crear. Las propiedades ButtonStyle y TextBoxStyle de StyledRegister se han definido mediante esta técnica.

Del mismo modo, la implementación de los métodos de administración de estado en StyledRegister es idéntica a la descrita en Ejemplo de administración personalizada de estados de propiedad. En el método TrackViewState reemplazado del control, se llama al método TrackViewState en la clase base y al método TrackViewState en cada propiedad de estilo. En el método SaveViewState reemplazado del control, se llama a SaveViewState en la clase base y a SaveViewState en cada propiedad de estilo. El estado que se devuelve de SaveViewState representa el estado combinado de la clase base y las propiedades de estilo. Para facilitar la recuperación en el método LoadViewState, el control StyledRegister utiliza un objeto Triplet para guardar el estado combinado. El método LoadViewState realiza la operación inversa del método SaveViewState y carga el estado en la clase base y en los estilos con tipo durante la devolución de datos.

El último paso de la implementación de un estilo con tipo consiste en aplicar los estilos a los controles secundarios. El control StyledRegister realiza este paso en la fase de representación, después de que se guarda el estado de vista, a fin de que el estado correspondiente a los estilos con tipo no se guarde en el estado de vista de los controles secundarios. Si se han aplicado estilos con tipo a los controles secundarios cuando el seguimiento de estado estaba activado, estos estilos se guardarían en el estado de vista de los controles secundarios. Esta acción no sería eficaz, dado que las propiedades de estilo con tipo administran su propio estado. El código siguiente extraído del método Render del control StyledRegister muestra el modelo de implementación para aplicar los estilos en la fase de representación:

protected override void Render(HtmlTextWriter writer)
{
    // Add code here to set up rendering.    
    if (_buttonStyle != null)
    {
        _button.ApplyStyle(ButtonStyle);
    }

    if (_textBoxStyle != null)
    {
        _nameTextBox.ApplyStyle(TextBoxStyle);
        _emailTextBox.ApplyStyle(TextBoxStyle);
    }
    // Add code here to continue rendering.
} 

En el ejemplo siguiente se muestra una página .aspx que utiliza el control StyledRegister. En el controlador de eventos Submit, se agregaría el código para introducir los datos de registro en una base de datos o escribir una cookie en el equipo del usuario. En una aplicación de producción, también sería necesario comprobar los ataques de inserción de secuencias de comandos. Para obtener más información, vea Información general sobre los ataques mediante secuencias de comandos.

<%@ Page Language="C#"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
  void Page_Load(object sender, EventArgs e)
  {
    Label1.Visible = false;
  }
  void StyledRegister_Submit(object sender, EventArgs e)
  {
    Label1.Text = String.Format("Thank you, {0}! You are registered.", 
      StyledRegister1.Name);
    Label1.Visible = true;
    StyledRegister1.Visible = false;
  }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
  <head id="Head1" runat="server">
    <title>
      StyledRegister Control Test Page
    </title>
  </head>
  <body>
    <form id="form1" runat="server">
      <aspSample:StyledRegister ButtonText="Register" 
        OnSubmit="StyledRegister_Submit" ID="StyledRegister1"
        Runat="server" NameLabelText="Name:" EmailLabelText="Email:" 
        EmailErrorMessage="You must enter your e-mail address."
        NameErrorMessage="You must enter your name." 
        BorderStyle="Solid" BorderWidth="1px" BackColor="#E0E0E0">
        <TextBoxStyle Font-Names="Arial" BorderStyle="Solid" 
          ForeColor="#804000"></TextBoxStyle>
        <ButtonStyle Font-Names="Arial" BorderStyle="Outset" 
          BackColor="Silver"></ButtonStyle>
      </aspSample:StyledRegister>
      <br />
      <asp:Label ID="Label1" Runat="server" Text="Label">
      </asp:Label>
      <asp:ValidationSummary ID="ValidationSummary1" 
        Runat="server" DisplayMode="List" />
    </form>
  </body>
</html>


Para obtener información sobre la compilación y el uso de los ejemplos de controles personalizados, vea Generar ejemplos de controles de servidor personalizados.

Adiciones de comunidad

Mostrar: