자식 컨트롤의 형식화된 스타일 예제

Visual Studio 2010

업데이트: 2007년 11월

이 예제에서는 합성 컨트롤에서 강력한 형식의 스타일 속성을 구현하는 StyledRegister 컨트롤을 만드는 방법을 보여 줍니다. 페이지 개발자는 이러한 속성을 사용하여 합성 컨트롤의 자식 컨트롤 모양을 사용자 지정할 수 있습니다. 또한 형식화된 스타일을 사용하면 합성 컨트롤을 쉽게 확장할 수 있습니다. 합성 컨트롤에 자식 컨트롤을 많이 추가할수록 합성 컨트롤에서 각 자식 컨트롤의 여러 속성을 노출하기가 어려워집니다. 대신 이러한 여러 속성을 하나의 스타일 속성으로 캡슐화할 수 있습니다.

이 예제의 StyledRegister 컨트롤은 합성 웹 컨트롤 예제에서 설명하는 Register 컨트롤과 비슷합니다. StyledRegister 컨트롤에는 TextBox 자식 컨트롤 두 개와 Button 자식 컨트롤 한 개가 있습니다. StyledRegister 클래스는 페이지 개발자가 자식 컨트롤, 즉 TextBoxButton 컨트롤의 Font, ForeColor 및 기타 스타일 관련 속성을 설정할 수 있도록 ButtonStyle 속성과 TextBoxStyle 속성을 노출합니다.

다음 예제와 같이 페이지 개발자는 StyledRegister 컨트롤에서 자식 컨트롤의 Font-Names, ForeColorBackColor 속성을 설정할 수 있습니다.

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

위의 예제에서 알 수 있듯이 형식화된 스타일 속성은 컨트롤 태그 내에 자식 요소로 유지됩니다. 이런 식으로 유지되는 속성을 구현하는 데 대한 내용은 서버 컨트롤 속성 예제Author 속성 구현을 참조하십시오.

형식화된 스타일은 Style 형식 또는 Style에서 파생된 형식의 속성입니다. Style 클래스는 Font, ForeColor, BackColor 등과 같이 모양과 관련된 속성을 노출합니다. WebControl 클래스의 ControlStyle 속성은 Style 형식을 사용합니다. 웹 컨트롤의 Font, ForeColorBackColor 속성은 WebControl 클래스의 최상위 속성으로 노출되지만 ControlStyle 속성의 하위 속성입니다.

Style 클래스에는 하위 속성이 있으므로 사용자 지정 속성 상태 관리 예제에서 설명한 대로 Style 형식의 속성에 사용자 지정 상태 관리가 필요합니다. 형식화된 클래스의 세부적인 구현 정보에 대해서는 이 항목의 뒷부분에 나오는 "코드 설명" 단원을 참조하십시오.

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


StyledRegister 컨트롤에서는 다음과 같이 자식 컨트롤에 대해 형식화된 스타일을 구현하는 데 필요한 주요 단계를 보여 줍니다.

  • Style 형식 또는 Style에서 파생된 형식의 속성을 정의합니다.

  • 스타일 속성에 대한 상태 관리를 구현합니다.

  • 자식 컨트롤에 스타일을 적용합니다.

Style 형식은 IStateManager 인터페이스를 구현하므로 Style 형식의 속성을 정의하는 패턴은 사용자 지정 속성 상태 관리 예제에서 설명하는 패턴과 같습니다. 형식화된 스타일 속성을 전용 필드에 저장되는 읽기 전용 속성으로 정의합니다. 속성 접근자에서 속성에 해당하는 필드가 null(Visual Basic에서는 Nothing)인 경우에만 새 Style 인스턴스를 만듭니다. 컨트롤에서 상태 추적을 시작한 경우에는 새로 만든 Style 인스턴스의 TrackViewState 메서드를 호출합니다. StyledRegisterButtonStyleTextBoxStyle 속성은 이 방법을 사용하여 정의됩니다.

마찬가지로 StyledRegister에서 상태 관리 메서드의 구현도 사용자 지정 속성 상태 관리 예제에서 설명하는 구현과 같습니다. 컨트롤의 TrackViewState 메서드를 재정의할 때 기본 클래스에서 TrackViewState 메서드를 호출하고 각 스타일 속성에서 TrackViewState 메서드를 호출합니다. 또한 컨트롤의 SaveViewState 메서드를 재정의할 때 기본 클래스에서 SaveViewState 메서드를 호출하고 각 스타일 속성에서 SaveViewState 메서드를 호출합니다. SaveViewState에서 반환하는 상태는 기본 클래스와 스타일 속성의 상태 조합을 나타냅니다. LoadViewState 메서드에서 쉽게 검색할 수 있도록 StyledRegister 컨트롤은 Triplet 개체를 사용하여 이 상태 조합을 저장합니다. LoadViewState 메서드는 SaveViewState 메서드와 반대의 작업을 수행하고 포스트백을 수행할 때 기본 클래스 및 형식화된 스타일에 상태를 로드합니다.

형식화된 스타일 구현의 마지막 단계에서는 자식 컨트롤에 스타일을 적용합니다. StyledRegister 컨트롤은 형식화된 스타일에 해당하는 상태가 자식 컨트롤의 뷰 상태에 저장되지 않도록 하기 위해 렌더링 단계에서 뷰 상태가 저장된 후에 이 작업을 수행합니다. 상태 추적이 설정되어 있는 경우 형식화된 스타일을 자식 컨트롤에 적용하면 이러한 스타일이 자식 컨트롤의 뷰 상태에 저장됩니다. 형식화된 스타일 속성은 자신의 상태를 관리하므로 이는 비효율적입니다. StyledRegister 컨트롤의 Render 메서드에서 인용한 다음 코드에서는 렌더링 단계에서 스타일을 적용하기 위한 구현 패턴을 보여 줍니다.

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

다음 예제에서는 StyledRegister 컨트롤을 사용하는 .aspx 페이지를 보여 줍니다. Submit 이벤트 처리기에서 데이터베이스에 등록 데이터를 입력하는 코드를 추가하거나 사용자의 컴퓨터에 쿠키를 작성합니다. 프로덕션 응용 프로그램의 경우에는 스크립트 삽입 공격 검사도 필요합니다. 자세한 내용은 스크립트 악용 개요를 참조하십시오.

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


사용자 지정 컨트롤 예제의 컴파일 및 사용에 대한 자세한 내용은 사용자 지정 서버 컨트롤 예제 빌드를 참조하십시오.

표시: