Typed Styles for Child Controls Example

This example shows how to create a control named StyledRegister that implements strongly typed style properties in a composite control. These properties enable a page developer to customize the appearance of the composite control's child controls. Typed styles also allow composite controls to scale easily. As more child controls are added to the composite control, it becomes unmanageable to expose each child control's many properties in the composite control. Instead, you can encapsulate many properties in a single style property.

The StyledRegister control in the example is similar to the Register control described in Composite Web Control Example. The StyledRegister control has two TextBox child controls and a Button child control. The StyledRegister class exposes the ButtonStyle and TextBoxStyle properties to enable a page developer to set the Font, ForeColor, and other style-related properties of the child TextBox and Button controls.

A page developer can set the Font-Names, ForeColor, and BackColor properties of the controls in the StyledRegister control as shown in the following example:

<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>

As the preceding example shows, typed style properties are persisted as child elements within a control's tags. For information about implementing properties that are persisted this way, see the implementation of the Author property in Server Control Properties Example.

A typed style is a property of type Style or of a type derived from Style. The Style class exposes appearance-related properties such as Font, ForeColor, and BackColor. The ControlStyle property of the WebControl class is of type Style. The Font, ForeColor, and BackColor properties of a Web control are subproperties of the ControlStyle property, although they are also exposed as top-level properties of the WebControl class.

Because the Style class has subproperties, properties of type Style require custom state management, as described in Custom Property State Management Example. The implementation details for typed styles are described in the "Code Discussion" section later in this topic.

Code Listing for the StyledRegister Control

' StyledRegister.vb 
Option Strict On 
Imports System
Imports System.ComponentModel
Imports System.Drawing
Imports System.Security.Permissions
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls

Namespace Samples.AspNet.VB.Controls
    < _
    AspNetHostingPermission(SecurityAction.Demand, _
        Level:=AspNetHostingPermissionLevel.Minimal), _
    AspNetHostingPermission(SecurityAction.InheritanceDemand, _
        Level:=AspNetHostingPermissionLevel.Minimal), _
    DefaultEvent("Submit"), _
    DefaultProperty("ButtonText"), _
    ToolboxData("<{0}:Register runat=""server""> </{0}:Register>") _
    > _
    Public Class StyledRegister
        Inherits CompositeControl

        Private submitButton As Button
        Private nameTextBox As TextBox
        Private nameLabel As Label
        Private emailTextBox As TextBox
        Private emailLabel As Label
        Private emailValidator As RequiredFieldValidator
        Private nameValidator As RequiredFieldValidator

        Private Shared ReadOnly EventSubmitKey As New Object()

        Private buttonStyleValue As Style
        Private textBoxStyleValue As Style

#Region "Properties delegated to child controls"
        < _
        Bindable(True), _
        Category("Appearance"), _
        DefaultValue(""), _
        Description("The text to display on the Button.") _
        > _
        Public Property ButtonText() As String 
            Get
                EnsureChildControls()
                Return submitButton.Text
            End Get 
            Set(ByVal value As String)
                EnsureChildControls()
                submitButton.Text = value
            End Set 
        End Property

        < _
        Bindable(True), _
        Category("Default"), _
        DefaultValue(""), _
        Description("The user name.") _
        > _
        Public Property Name() As String 
            Get
                EnsureChildControls()
                Return nameTextBox.Text
            End Get 
            Set(ByVal value As String)
                EnsureChildControls()
                nameTextBox.Text = value
            End Set 
        End Property

        < _
        Bindable(True), _
        Category("Appearance"), _
        DefaultValue(""), _
        Description("The error message of the name validator.") _
        > _
        Public Property NameErrorMessage() As String 
            Get
                EnsureChildControls()
                Return nameValidator.ErrorMessage
            End Get 
            Set(ByVal value As String)
                EnsureChildControls()
                nameValidator.ErrorMessage = value
                nameValidator.ToolTip = value
            End Set 
        End Property

        < _
        Bindable(True), _
        Category("Appearance"), _
        DefaultValue(""), _
        Description("The text for the name Label.") _
        > _
        Public Property NameLabelText() As String 
            Get
                EnsureChildControls()
                Return nameLabel.Text
            End Get 
            Set(ByVal value As String)
                EnsureChildControls()
                nameLabel.Text = value
            End Set 
        End Property

        < _
        Bindable(True), _
        Category("Default"), _
        DefaultValue(""), _
        Description("The e-mail address.") _
        > _
        Public Property Email() As String 
            Get
                EnsureChildControls()
                Return emailTextBox.Text
            End Get 
            Set(ByVal value As String)
                EnsureChildControls()
                emailTextBox.Text = value
            End Set 
        End Property

        < _
        Bindable(True), _
        Category("Appearance"), _
        DefaultValue(""), _
        Description("Error message of the e-mail validator.") _
        > _
        Public Property EmailErrorMessage() As String 
            Get
                EnsureChildControls()
                Return emailValidator.ErrorMessage
            End Get 
            Set(ByVal value As String)
                EnsureChildControls()
                emailValidator.ErrorMessage = value
                emailValidator.ToolTip = value
            End Set 
        End Property
        < _
        Bindable(True), _
        Category("Appearance"), _
        DefaultValue(""), _
        Description("The text for the e-mail Label.") _
        > _
        Public Property EmailLabelText() As String 
            Get
                EnsureChildControls()
                Return emailLabel.Text
            End Get 
            Set(ByVal value As String)
                EnsureChildControls()
                emailLabel.Text = value
            End Set 
        End Property
#End Region

#Region "Typed Style properties"
        < _
        Category("Styles"), _
        DefaultValue(GetType(Style), Nothing), _
        DesignerSerializationVisibility( _
            DesignerSerializationVisibility.Content), _
        PersistenceMode(PersistenceMode.InnerProperty), _
        Description( _
            "The strongly typed style for the Button child control.") _
        > _
        Public Overridable ReadOnly Property ButtonStyle() As Style
            Get 
                If buttonStyleValue Is Nothing Then
                    buttonStyleValue = New Style
                    If IsTrackingViewState Then 
                        CType(buttonStyleValue, _
                            IStateManager).TrackViewState()
                    End If 
                End If 
                Return buttonStyleValue
            End Get 
        End Property

        < _
        Category("Styles"), _
        DefaultValue(GetType(Style), Nothing), _
        DesignerSerializationVisibility( _
            DesignerSerializationVisibility.Content), _
        PersistenceMode(PersistenceMode.InnerProperty), _
        Description( _
            "The strongly typed style for the TextBox child control.") _
        > _
        Public Overridable ReadOnly Property TextBoxStyle() As Style
            Get 
                If textBoxStyleValue Is Nothing Then
                    textBoxStyleValue = New Style
                    If IsTrackingViewState Then 
                        CType(textBoxStyleValue, _
                            IStateManager).TrackViewState()
                    End If 
                End If 
                Return textBoxStyleValue
            End Get 
        End Property
#End Region

#Region "Event definition"
        < _
        Category("Action"), _
        Description("Raised when the user clicks the button.") _
        > _
        Public Custom Event Submit As EventHandler
            AddHandler(ByVal value As EventHandler)
                Events.AddHandler(EventSubmitKey, value)
            End AddHandler 
            RemoveHandler(ByVal value As EventHandler)
                Events.RemoveHandler(EventSubmitKey, value)
            End RemoveHandler 
            RaiseEvent(ByVal sender As Object, _
                ByVal e As System.EventArgs)
                CType(Events(EventSubmitKey), _
                    EventHandler).Invoke(sender, e)
            End RaiseEvent 
        End Event 

        ' The method that raises the Submit event. 
        Protected Overridable Sub OnSubmit(ByVal e As EventArgs)
            Dim submitHandler As EventHandler = _
                CType(Events(EventSubmitKey), EventHandler)
            If submitHandler IsNot Nothing Then
                submitHandler(Me, e)
            End If 
        End Sub 

        ' Handles the Click event of the Button and raises 
        ' the Submit event. 
        Private Sub submitButton_Click(ByVal source As Object, _
            ByVal e As EventArgs)
            OnSubmit(EventArgs.Empty)
        End Sub
#End Region

#Region "Overridden methods" 
        Protected Overrides Sub 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" 
            AddHandler submitButton.Click, _
                AddressOf submitButton_Click

            Me.Controls.Add(nameLabel)
            Me.Controls.Add(nameTextBox)
            Me.Controls.Add(nameValidator)
            Me.Controls.Add(emailLabel)
            Me.Controls.Add(emailTextBox)
            Me.Controls.Add(emailValidator)
            Me.Controls.Add(submitButton)
        End Sub 

        Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)
            AddAttributesToRender(writer)

            writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, _
                "1", False)
            writer.RenderBeginTag(HtmlTextWriterTag.Table)

            If buttonStyleValue IsNot Nothing Then
                submitButton.ApplyStyle(buttonStyleValue)
            End If 

            If textBoxStyleValue IsNot Nothing Then
                nameTextBox.ApplyStyle(textBoxStyleValue)
                emailTextBox.ApplyStyle(textBoxStyleValue)
            End If

            writer.RenderBeginTag(HtmlTextWriterTag.Tr)
            writer.RenderBeginTag(HtmlTextWriterTag.Td)
            nameLabel.RenderControl(writer)
            writer.RenderEndTag()  ' Renders the </td> tag.
            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
        End Sub 

        Protected Overrides Sub RecreateChildControls()
            EnsureChildControls()
        End Sub

#End Region

#Region "Custom state management" 
        Protected Overrides Sub LoadViewState( _
            ByVal savedState As Object)

            If savedState is Nothing Then 
                MyBase.LoadViewState(Nothing)
                Return 
            Else 
                Dim t As Triplet = TryCast(savedState, Triplet)

                If t IsNot Nothing Then 
                    ' Always invoke LoadViewState on the base class even if  
                    ' the saved state is null. 
                    MyBase.LoadViewState(t.First)

                    If t.Second IsNot Nothing Then 
                        CType(buttonStyleValue, _
                            IStateManager).LoadViewState(t.Second)
                    End If 

                    If t.Third IsNot Nothing Then 
                        CType(textBoxStyleValue, _
                            IStateManager).LoadViewState(t.Third)
                    End If 
                Else 
                    Throw New ArgumentException("Invalid view state .")
                End If 
            End If 
        End Sub 

        Protected Overrides Function SaveViewState() As Object 
            Dim baseState As Object = MyBase.SaveViewState
            Dim buttonStyleState As Object = Nothing 
            Dim textBoxStyleState As Object = Nothing 

            If buttonStyleValue IsNot Nothing Then
                buttonStyleState = CType(buttonStyleValue, _
                    IStateManager).SaveViewState
            End If 

            If textBoxStyleValue IsNot Nothing Then
                textBoxStyleState = CType(textBoxStyleValue, _
                    IStateManager).SaveViewState
            End If 
            Return New Triplet(baseState, buttonStyleState, _
                textBoxStyleState)
        End Function 

        Protected Overrides Sub TrackViewState()
            MyBase.TrackViewState()
            If buttonStyleValue IsNot Nothing Then 
                CType(buttonStyleValue, IStateManager).TrackViewState()
            End If 
            If textBoxStyleValue IsNot Nothing Then 
                CType(textBoxStyleValue, IStateManager).TrackViewState()
            End If 

        End Sub
#End Region
    End Class 

End Namespace
// 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
    }
}

Code Discussion

The StyledRegister control demonstrates the following main steps in implementing typed styles for child controls:

  • Defining properties of type Style or of types derived from Style.

  • Implementing state management for the style properties.

  • Applying styles to child controls.

Because the Style type implements the IStateManager interface, the pattern for defining a property of type Style is identical to that described in Custom Property State Management Example. You define a typed style property as a read-only property stored in a private field. In the property's accessor, you create a new Style instance only if the field corresponding to the property is null (Nothing in Visual Basic). If the control has started state tracking, you call the TrackViewState method of the newly created Style instance. The ButtonStyle and TextBoxStyle properties of StyledRegister are defined using this technique.

Similarly, the implementation of the state-management methods in StyledRegister is identical to that described in Custom Property State Management Example. In the overridden TrackViewState method of your control, you call the TrackViewState method on the base class and the TrackViewState method on each style property. In the overridden SaveViewState method of your control, you call SaveViewState on the base class and SaveViewState on each style property. The state you return from SaveViewState represents the combined state of the base class and the style properties. For ease of retrieval in the LoadViewState method, the StyledRegister control uses a Triplet object to save the combined state. The LoadViewState method performs the reverse of the SaveViewState method and loads state into the base class and into the typed styles on postback.

The last step in implementing a typed style is to apply styles to child controls. The StyledRegister control performs this step in the render phase, after view state is saved, so that state corresponding to typed styles is not saved in the view state of the child controls. If typed styles were applied to child controls when state tracking was on, these styles would be saved in the view state of the child controls. This would be inefficient, since typed style properties manage their own state. The following code excerpt from the Render method of the StyledRegister control shows the implementation pattern for applying styles in the render phase:

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.
} 
Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)
    ' Add code here to set up rendering.    
    If buttonStyleValue IsNot Nothing Then
        submitButton.ApplyStyle(buttonStyleValue)
    End If

    If textBoxStyleValue IsNot Nothing Then
        nameTextBox.ApplyStyle(textBoxStyleValue)
        emailTextBox.ApplyStyle(textBoxStyleValue)
    End If
    ' Add code here to continue rendering.
End Sub

Test Page for the StyledRegister Control

The following example shows an .aspx page that uses the StyledRegister control. In the Submit event handler, you would add code to enter the registration data into a database or write a cookie on the user's computer. In a production application, you would also need to check for script injection attacks. For more information, see Script Exploits Overview.

<%@ Page Language="VB"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
        Label1.Visible = False
    End Sub

    Sub StyledRegister_Submit(ByVal sender As Object, _
        ByVal e As EventArgs)
        Label1.Text = String.Format( _
            "Thank you, {0}! You are registered.", _
            StyledRegister1.Name)
        Label1.Visible = True
        StyledRegister1.Visible = False
    End Sub
</script>
<html xmlns="https://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>
<%@ Page Language="C#"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "https://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="https://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>

Building and Using the Example

For information about compiling and using the custom control examples, see Building the Custom Server Control Examples.

See Also

Concepts

Composite Web Control Example

Custom Property State Management Example

Other Resources

Developing Custom ASP.NET Server Controls