SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

不快感を与えるテキストデータの入力を排除する

テキストエディタとコンパイラによる複合コントロールの作成

複合コントロールの作成

 今回のアプリケーションでは、ユーザーがテキストを入力するテキストボックスと、それに対応する送信ボタン、およびユーザーにフィードバックを表示するラベルを用意します。今回作成するカスタムコントロールは、次のような構造のXMLファイルを参照することで、ユーザーから送信されたテキストに不適切な言葉(不適ワード)が含まれているかどうかをチェックします。

<?xml version="1.0"?>
<words>
   <word>word root 1</word>
   <word>word root 2</word>
</words>

 サンプルを期待どおりに動作させるには、このファイルの内容をあらかじめ変更しておく必要があります。本稿で実装するバージョンでは、送信されたテキストに不適ワードが含まれていないかどうかだけを報告します。必要に応じて、この機能を拡張してください。

 以降では、Textboxコントロール、Labelコントロール、Buttonコントロールという3つのASP.NETサーバーコントロールを組み合わせた複合コントロール(Composite)について説明します。ユーザーが子Buttonコントロールをクリックすると、Compositeは送信されたテキストを分析し、XMLファイル(カスタムプロパティで指定。デフォルトは「bad_words.xml」)内の不適ワードがテキスト中に含まれていないかをチェックし、カスタムイベントを発生させます。Compositeは子LabelコントロールのTextプロパティもトップレベルプロパティとして公開しています。

 複合コントロールの子コントロールはカプセル化されていることに注意してください。既定では、子コントロールを親の外部から見ることはできません。ページ開発者が親のControlsコレクションを使って子コントロールにアクセスしようとしても、リテラルコントロール(サーバー側の処理を必要としない基本的なHTML要素を出力するのに使用)が介在するため、特定の子コントロールのインデックスを取得するのは困難です。

 複合コントロールでは、子コントロールをプロパティとして公開するかどうかを選択できるほか、子コントロールのどのプロパティ/イベントをトップレベルのプロパティ/イベントとして公開するかを選択できます。複合コントロールが子コントロールのプロパティを公開する場合は、次の例に示すように、単純に処理を子コントロールに委譲します。

' Delegate to label, which is an instance of
' System.Web.UI.WebControls.Label

Public Property Text() As String
   Get
      EnsureChildControls()
      Return label.Text
   End Get
   Set
      EnsureChildControls()
      label.Text = value
   End Set
End Property

 それでは、複合コントロールをインスタンス化するWebフォームのコードから見ていきましょう。

リスト1 composite.aspx
<%@ Page Language="vb" debug="true" trace="true" %>
<%@ Register TagPrefix="Custom" Namespace="CustomControls" Assembly =
"CustomControls" %>

<html>
<script language="VB" runat=server>
   Private Sub CheckText(sender As Object, e As CheckEventArgs)
      If e.Match = false Then
         Composite.Text = "<h2>Clean your dirty mind out!</h2>"
      Else
         Composite.Text = "Text validated OK."
      End If
   End Sub
</script>
<body>

<h1>Anti-Swear Composite Control Example</h1><br>

<form runat=server>
<Custom:Composite id = "Composite" OnCheck = "CheckText"
                  filename = "bad_words.xml" runat = server/>
</form>
</body>
</html>

 このコードでは、名前空間とアセンブリ名を指定して複合コントロールを登録しています。後で、複合コントロールを.dllにコンパイルし、アプリケーションの「bin」ディレクトリに保存します。このディレクトリは、ASP.NETが最初に検索する場所です。前述の通り、再利用性を高めるために複合コントロールのdllをGACに配置することもできます。これについては、また別の記事で説明します。

 Webフォームのユーザーインターフェイス内では、次の情報を指定してカスタムコントロールをインスタンス化しています。

  • 複合コントロールのOnCheckイベント発生時に実行されるローカルサブルーチン。このサブルーチンでは、パブリックプロパティを通じて複合コントロールのラベルのテキスト値を書き換えます。このテキストの内容は、OnCheckイベントで設定される別のパブリックプロパティの値によって異なります。
  • 不適ワードが定義されているXMLファイルの名前。

 複合コントロールが呼び出すCheckTextサブルーチンの内容については、前述のコードを参照してください。

 次は、複合コントロールの実装を見ていきましょう。今回のサンプルでは、2つのクラスをそれぞれ「composite.vb」と「checkevent.vb」という別々のVBソースファイルとして実装します。

リスト2 composite.vb
Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Xml
Imports System.Collections

Namespace CustomControls
   Public Class Composite
      Inherits Control
      Implements INamingContainer
      Private _filename As String = "bad_words.xml"
      Private label As Label
      Public box1 As TextBox

      Public Property filename() As String
         Get
            Return _filename
         End Get
         Set
            _filename = value
         End Set
      End Property

      'takes as input the submitted text and returns 
      'the sanitisied version if naughty words found,
      'or a copy of the original string if not

      Public Function CheckString(InputString as String) as string
         Dim alWordList As new ArrayList
         dim xmlDocPath as string = mappathsecure("bad_words.xml")
         dim xmlReader as XmlTextreader = _
            new xmlTextReader(xmlDocPath)
         dim element as string
         dim output as string
         dim asterisks as string = "*************************"

         'load the naughty word roots into an arraylist
         while (xmlReader.Read())
            if xmlReader.NodeType=xmlNodeType.Text then
               alWordList.Add(xmlReader.Value)
            end if
         end while
         xmlReader.Close()

         'check the string, replacing naughty roots with the
         'appropriate number of asterisks.
         For Each element in alWordList
            InputString=InputString.Replace(element, _
               asterisks.substring(1, (element.length)))
         Next

         Return InputString

      End Function

      Public Property Text() As String
         Get
            'This method first checks the current value of the
            'ChildControlsCreated property.
            'If this value is false,
            'the CreateChildControls method is called.
            EnsureChildControls()
            Return label.Text
         End Get
         Set
            EnsureChildControls()
            label.Text = value
         End Set
      End Property

      Public Event Check As CheckEventHandler

      Protected Overridable Sub OnCheck(ce As CheckEventArgs)
         RaiseEvent Check(Me, ce)
      End Sub

      'create the child controls of the composite control
      Protected Overrides Sub CreateChildControls()

         Controls.Add(New LiteralControl("<h3>Enter some text: "))

         'the text box
         Dim box1 As New Textbox()
         box1.Text = ""
         Controls.Add(box1)

         Controls.Add(New LiteralControl("</h3>"))

         'the button
         Dim button1 As New Button()
         button1.Text = "Submit"
         Controls.Add(New LiteralControl("<br>"))
         Controls.Add(button1)

         'dynamically add an event handler 
         'to the newly created button object
         AddHandler button1.Click, AddressOf Me.ButtonClicked

         Controls.Add(New LiteralControl("<br><br>"))
         label = New Label()
         label.Height = Unit.Pixel(50)
         label.Width = Unit.Pixel(500)
         label.Text = ""
         Controls.Add(label)
      End Sub

      Protected Overrides Sub OnPreRender(e As EventArgs)
         CType(Controls(1), TextBox).Text = ""
      End Sub

      Private Sub ButtonClicked(sender As [Object], e As EventArgs)
         OnCheck(New CheckEventArgs(CType(Controls(1), _
            TextBox).Text,CheckString(CType(Controls(1), _
            TextBox).Text)))
      End Sub
   End Class
End Namespace

 このコードの概要を順番に説明していきます。

  • 使用するクラスに必要な名前空間をインポートし、このクラスが存在する名前空間を定義します。
  • 複合クラスCompositeを定義します。基本コントロールクラスを継承し、INamingContainerインターフェイスを実装することを指定します。これによって、ポストバックイベントを子Buttonコントロールにルーティングできます。
  • Compositeは、OnInitや自身のコンストラクタではなく、CreateChildControls()メソッド内で子コントロールを作成します。CreateChildControls()は、コード内で子コントロールを使用する場面でChildControlsCreatedを通じて呼び出されます。
  • Compositeは、子ButtonコントロールのClickイベントを公開しません。代わりにClickイベントを処理し、カスタムイベントCheckを発生させます。複合コントロールに子コントロール上のイベントを処理させる場合は、CreateChildControlsの中でイベントハンドラを関連付ける必要があります。
  • Compositeは、次のパブリックプロパティを公開します。
    • Text――基本の値は、子LabelコントロールのTextプロパティです。
    • Filename――不適ワードリストの読み込み元となるXMLファイル名を取得または設定できます。
  • メインのパブリック関数はCheckStringです。この関数は、指定のXMLファイルから不適ワードを配列リストに読み込み、配列リスト内の1つ1つのワードを、受け取ったテキスト文字列の中で検索します。不適ワードが見つかった場合は、適当な数のアスタリスクに置き換えます。
  • OnPreRenderでは、子TextBoxコントロールのテキストをクリアします。
  • ButtonClickedが実行されるタイミングは、子Buttonコントロールがクリックされて、onCheckが適切な引数と共に呼び出されたときです(新しいCheckEventArgsオブジェクトには、チェック済みテキストと未チェックのテキストがコンストラクタパラメータとして渡されます)。ButtonClickedが呼び出されると、OnCheckが親.aspxページ内のコードで処理されるイベントを発生させます。このイベントが子コントロールからコンテナへと「バブルアップ」され、コンテナオブジェクトのトップレベルイベントとして公開されます。
リスト3 checkevent.vb
' CheckEvent.vb
' Contains the code for the custom event data class CheckEventArgs.
' Also defines the event handler for the Check event.
Imports System
Namespace CustomControls
   Public Class CheckEventArgs
      Inherits EventArgs
      Private _match As Boolean = False

      Public Sub New(string1 As String, string2 as String)
         If string1=string2 Then
            _match = True
         End If
      End Sub

      Public ReadOnly Property Match() As Boolean
         Get
            Return _match
         End Get
      End Property
   End Class

   Public Delegate Sub CheckEventHandler(sender As Object, _
      ce As CheckEventArgs)
End Namespace

 基本は「composite.vb」と同じです。CheckEventArgsのコンストラクタは2つの文字列をパラメータとして受け取り、ブール値matchの値を適切に設定します。ここではイベントハンドラCheckEventHandlerも定義します。

 最後に、今回のようにIDEの助けを借りずに作業を進めてきた場合は、次のようにしてコマンドラインからコードをコンパイルできます(Webアプリケーションのルート内に「bin」サブディレクトリを作成しておき、ルートから下記のコマンドを実行します)。

vbc /t:library /out:./bin/CustomControls.dll /r:System.dll
 /r:System.web.dll /r:System.drawing.dll /r:System.Data.dll
 /r:System.xml.dll *.vb

 ここで説明した内容が、実用的な小規模アプリケーションで複合コントロールを使用する際の参考になれば幸いです。ここで紹介した例は、自分の環境に合わせて変更していただいてかまいません。今後記事で取り上げてほしい内容や、私自身または私の会社が興味を持ちそうなプロジェクトの可能性の問い合わせなど、ご意見ご要望がありましたらぜひお寄せください。

参考資料

  • .NET Framework SDKドキュメンテーション
  • ASP.NET: Tips, Tutorials, and Code』 Stephen Walther・Doug Seven・Donny Mack・Chris Payne・Billy Anders・Adam Nathan・Dan Wahlin 著、Scott Mitchell 編集、Sams、2001年8月
  • Professional ASP.NET 1.0』 Richard Anderson・Brian Francis・Alex Homer・Ron Howard・David Sussman・Karli Watson 著、Wrox Pr Inc、2002年2月
  • 各種オンラインリソース

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Chris Sully(Chris Sully)

英国在住のMicrosoft Web開発者。最近は.NETとASP.NETを使った開発に特に力を注ぐ。ウェールズ出身。Web設計/開発会社Cymru-Web.net(http://www.cymru-web.net/)のテクニカルディレクタを務める。同社は.NETおよび関連するMicrosoftテクノロジを...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/344 2006/04/24 16:32

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー