コントロールの状態とビューステートの例

更新 : 2007 年 11 月

この例では、ページ要求の前後で重要な状態情報を維持するためにコントロールの状態を使用する IndexButton というカスタム コントロールを作成する方法を示します。ASP.NET バージョン 2.0 で導入されたコントロールの状態はビューステートに似ていますが、ビューステートとは機能的に独立しています。ページの開発者は、パフォーマンスを向上するためにページまたは個々のコントロールのビューステートを無効にできます。ただし、コントロールの状態を無効にすることはできません。コントロールの状態は、ポストバック時に必要なコントロールの重要なデータ (ページャ コントロールのページ番号など) を格納し、ビューステートが無効になっている場合にもコントロールが機能するように設計されています。既定では、ASP.NET のページ フレームワークは、ページのコントロールの状態をビューステートと同様に隠し要素に格納します。ビューステートが無効になっている場合、または Session を使用して状態が管理されている場合にも、コントロールの状態はクライアントに送信され、ページのサーバーに返されます。ASP.NET はポストバック時に隠し要素の内容を逆シリアル化し、コントロールの状態に対して登録されている各コントロールにコントロールの状態を読み込みます。

1whwt1k7.alert_note(ja-jp,VS.90).gifメモ :

コントロールの状態は、ポストバックの前後でコントロールが必要とする少量の重要なデータのみに使用します。ビューステートの代わりにコントロールの状態は使用しないでください。

この例では、コントロールの状態とビューステートの両方に状態を保存するカスタム コントロールを示します。この例の IndexButton コントロールは Button クラスから派生し、コントロールの状態に保存する Index プロパティを定義します。比較のために、IndexButton は、ViewState 辞書に保存する IndexInViewState プロパティも定義します。コントロールの状態とビューステートの違いを確認するために、この後の「IndexButton コントロールのテスト ページ」に記載されている .aspx ページの IndexButton コントロールを使用します。

IndexButton コントロールのコード リスト

' IndexButton.vb
Option Strict On
Imports System
Imports System.ComponentModel
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), _
    ToolboxData("<{0}:IndexButton runat=""server""> </{0}:IndexButton>") _
    > _
    Public Class IndexButton
        Inherits Button
        Private indexValue As Integer

        < _
        Bindable(True), _
        Category("Behavior"), _
        DefaultValue(0), _
        Description("The index stored in control state.") _
        > _
        Public Property Index() As Integer
            Get
                Return indexValue
            End Get
            Set(ByVal value As Integer)
                indexValue = value
            End Set
        End Property

        < _
        Bindable(True), _
        Category("Behavior"), _
        DefaultValue(0), _
        Description("The index stored in view state.") _
        > _
        Public Property IndexInViewState() As Integer
            Get
                Dim obj As Object = ViewState("IndexInViewState")
                If obj Is Nothing Then obj = 0
                Return CInt(obj)
            End Get
            Set(ByVal value As Integer)
                ViewState("IndexInViewState") = value
            End Set
        End Property

        Protected Overrides Sub OnInit(ByVal e As EventArgs)
            MyBase.OnInit(e)
            Page.RegisterRequiresControlState(Me)
        End Sub

        Protected Overrides Function SaveControlState() As Object
            ' Invoke the base class's method and
            ' get the contribution to control state
            ' from the base class.
            ' If the indexValue field is not zero
            ' and the base class's control state is not null,
            ' use Pair as a convenient data structure
            ' to efficiently save 
            ' (and restore in LoadControlState)
            ' the two-part control state
            ' and restore it in LoadControlState.

            Dim obj As Object = MyBase.SaveControlState()

            If indexValue <> 0 Then
                If obj IsNot Nothing Then
                    Return New Pair(obj, indexValue)
                Else
                    Return indexValue
                End If
            Else
                Return obj
            End If
        End Function

        Protected Overrides Sub LoadControlState(ByVal state As Object)
            If (state IsNot Nothing) Then
                Dim p As Pair = TryCast(state, Pair)
                If p IsNot Nothing Then
                    MyBase.LoadControlState(p.First)
                    indexValue = CInt(p.Second)
                Else
                    If (TypeOf (state) Is Integer) Then
                        indexValue = CInt(state)
                    Else
                        MyBase.LoadControlState(state)
                    End If
                End If
            End If
        End Sub

    End Class
End Namespace
// IndexButton.cs
using System;
using System.ComponentModel;
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),
    ToolboxData("<{0}:IndexButton runat=\"server\"> </{0}:IndexButton>")
    ]
    public class IndexButton : Button
    {
        private int indexValue;

        [
        Bindable(true),
        Category("Behavior"),
        DefaultValue(0),
        Description("The index stored in control state.")
        ]
        public int Index
        {
            get
            {
                return indexValue;
            }
            set
            {
                indexValue = value;
            }
        }

        [
        Bindable(true),
        Category("Behavior"),
        DefaultValue(0),
        Description("The index stored in view state.")
        ]
        public int IndexInViewState
        {
            get
            {
                object obj = ViewState["IndexInViewState"];
                return (obj == null) ? 0 : (int)obj;
            }
            set
            {
                ViewState["IndexInViewState"] = value;
            }
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            Page.RegisterRequiresControlState(this);
        }

        protected override object SaveControlState()
        {
            // Invoke the base class's method and
            // get the contribution to control state
            // from the base class.
            // If the indexValue field is not zero
            // and the base class's control state is not null,
            // use Pair as a convenient data structure
            // to efficiently save 
            // (and restore in LoadControlState)
            // the two-part control state
            // and restore it in LoadControlState.

            object obj = base.SaveControlState();

            if (indexValue != 0)
            {
                if (obj != null)
                {
                    return new Pair(obj, indexValue);
                }
                else
                {
                    return (indexValue);
                }
            }
            else
            {
                return obj;
            }
        }

        protected override void LoadControlState(object state)
        {
            if (state != null)
            {
                Pair p = state as Pair;
                if (p != null)
                {
                    base.LoadControlState(p.First);
                    indexValue = (int)p.Second;
                }
                else
                {
                    if (state is int)
                    {
                        indexValue = (int)state;
                    }
                    else
                    {
                        base.LoadControlState(state);
                    }
                }
            }
        }

    }
}

コードの説明

IndexButton コントロールの実装は、コントロールがコントロールの状態に参加するために次の 3 つのタスクを実行する必要があります。

  • OnInit メソッドをオーバーライドし、RegisterRequiresControlState メソッドを呼び出して、コントロールの状態に参加するためにページを登録します。これは、各要求に対して実行する必要があります。

  • SaveControlState メソッドをオーバーライドしてコントロールの状態にデータを保存します。

  • LoadControlState メソッドをオーバーライドしてコントロールの状態からデータを読み込みます。このメソッドは、基本クラスのメソッドを呼び出し、基本クラスの機能をコントロールの状態に提供します。indexValue フィールドが 0 以外で、基本クラスのコントロールの状態が null 以外の場合、Pair クラスは 2 つの部分で構成されるコントロールの状態を保存して復元するために使用できる便利なデータ構造です。

IndexButton コントロールのテスト ページ

@ Page ディレクティブの EnableViewState 属性を false に設定してビューステートを無効にするページの例を次に示します。このページは IndexButton コントロールを使用し、Page_Load イベント ハンドラのコントロールの Index プロパティと IndexInViewState プロパティの値に 1 を加えます。ページのラベルには、Index プロパティと IndexInViewState プロパティの値が表示されます。

Index プロパティは無効にできないコントロールの状態に格納されるため、Index プロパティの値はポストバック時に維持され、ページがサーバーにポストバックされるたびに 1 だけ加算されます。これとは異なり、IndexInViewState プロパティはこのページに対して無効になっているビューステートに格納されるため、IndexInViewState プロパティの値は常に既定値の 0 になります。

<%@ Page Language="VB" Trace="true" EnableViewState="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
        Label1.Text = IndexButton1.Index.ToString()
        Label2.Text = IndexButton1.IndexInViewState.ToString()
        IndexButton1.Index += 1
        IndexButton1.IndexInViewState += 1
    End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
  <head id="Head1" runat="server">
    <title>IndexButton test page</title>
  </head>
  <body>
    <form id="form1" runat="server">
        Click the button:
        <aspSample:IndexButton Text="IndexButton" 
            ID="IndexButton1" runat="server"/>
      <br />
      <br />
      The value of the Index property of IndexButton is:<br />
      <asp:Label ID="Label1" Runat="server" Text="Label">
      </asp:Label>
      <br />
      <br />
      The value of the IndexInViewState property of IndexButton is:
      <br />
      <asp:Label ID="Label2" Runat="server" Text="Label">
      </asp:Label>
      <br />
    </form>
  </body>
</html>
<%@ Page Language="C#" Trace="true" EnableViewState="false" %>
<!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.Text = (IndexButton1.Index++).ToString();
    Label2.Text = (IndexButton1.IndexInViewState++).ToString();
  }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
  <head id="Head1" runat="server">
    <title>IndexButton test page</title>
  </head>
  <body>
    <form id="form1" runat="server">
        Click the button:
        <aspSample:IndexButton Text="IndexButton" 
            ID="IndexButton1" runat="server"/>
      <br />
      <br />
      The value of the Index property of IndexButton is:<br />
      <asp:Label ID="Label1" Runat="server" Text="Label">
      </asp:Label>
      <br />
      <br />
      The value of the IndexInViewState property of IndexButton is:
      <br />
      <asp:Label ID="Label2" Runat="server" Text="Label">
      </asp:Label>
      <br />
    </form>
  </body>
</html>

例のビルドと使用

カスタム コントロールの例のコンパイルと使用については、「カスタム サーバー コントロールの例のビルド」を参照してください。

参照

概念

ASP.NET ビューステートの概要

ASP.NET の状態管理に関する推奨事項

パフォーマンスの概要

その他の技術情報

ASP.NET カスタム サーバー コントロールの開発