SHOEISHA iD

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

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

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

japan.internet.com翻訳記事

Webカスタムコントロールの作成

カテゴリ別に表示可能なチェックボックスリストの作成

コントロールによるHTML出力

 ヘルパーメソッドであるGetTableTagOutputCategoryRow、およびOutputCheckBoxを詳しく調べてみれば、TABLEタグ、TRおよびTDタグ、それにチェックボックスフィールドタグにCSSの「クラス」属性が含まれていることにおそらく気がつくでしょう。コントロールのパブリックプロパティにおいて指定されたCSSクラスをこうしたタグのために利用します。そうすれば、CategorizedCheckBoxListコントロールの外観と印象をASPXページによって管理できます。

 また、TABLE、TR、およびTDの各タグのHTMLに改行(\n)とタブ(\t)が追加されていることにも気が付いたかもしれません。こうした記号の追加は、どうしても必要なものではありませんし、かえってWebブラウザがダウンロードするHTMLのサイズを増やすことになります。しかし、結果として得られるHTMLは非常に読みやすいものになります(このページのソースをご覧になればわかります)。ただし、製品版アプリケーションでこのコントロールを実装する場合には、こうした余白文字は削除しておくのがよいでしょう。

デザイン時サポート

 さて、ここまでは順調です。しかし依然として、Visual Studio .NETのページデザイナでは、このコントロールは汎用的なグレーボックスとして表示されたままです。さらに、プロパティウィンドウには、このコントロールのプロパティが1つも表示されません。このコントロールにデザイン時サポートを加えるためには、まずクラスおよびプロパティの宣言部にコード属性を追加する必要があります。

 Visual Studioのデザイン時サポートを加える際には、パブリックプロパティにコード属性を設定します。

[Category("Appearance"),
DefaultValue(""),
Description("The CSS Class name for the table row" 
    + "containing each item.")]
public string RowCssClass
{

 プロパティ宣言部の上に記述した大カッコ内のコードは、このプロパティをプロパティウィンドウにどのように表示するかをVisual Studio .NETに指示しています。

一般的な属性
Categoryプロパティが現れるカテゴリを定めます。次の共通オプションがあります。
●・Appearance
Behavior
Data
Descriptionプログラマがプロパティの設定を行う際の参考としてプロパティボックスの最下部に表示される説明です。
DefaultValueデフォルト値を表します。
Browsableプロパティをプロパティボックスに表示するかどうかを指定します。次のいずれかの値をとります。
true
false

 ご覧のとおり、プロパティウィンドウのサポートは簡単に実現できます。

 しかし、コントロール自身のデザイン時サポート、つまりブラウザ上でのコントロールの描画をより正確に示すためのデザイン時サポートを追加するには、もう少し複雑な作業が必要です。そのためには、System.Web.UI.Design.ControlDesigner型から派生させた新しいクラスを作成し、CategorizedCheckBoxListクラスにDesigner属性を追加しなければなりません。Designer属性は、このコントロールのデザイン時HTMLを出力するときに特別なクラスを使用するようVisual Studioに指示します。

 ControlDesigner基底クラスには、GetDesignTimeHtmlというメソッドが用意されています。このメソッドは、Visual Studio .NETによって使用されるHTMLを作成し、デザインビューのページ上にコントロールを描画します。このメソッドをオーバーライドすることによって、独自のHTMLを書き出し、コントロールの外観を自在に制御することができます。

 ただし、Visual Studio .NET IDE内でのCategorizedCheckBoxListの外観と実際のWebアプリケーション上での外観を完全に一致させようとは考えないでください。そうすると、必要以上にコードが複雑になってしまうからです。しかし、何百行もコードを書かなくても、コントロールの基本的なルックアンドフィールを再現することは可能です。Microsoftお気に入りの言葉に従い、「Do more with less(最小のコストで最大の効果を得よ)」という路線で考えてみましょう。

 ControlDesignerのコードを次に示します。

/// <summary>
/// Provides a moderate level of fidelity
/// for the CategorizedCheckBoxList control
/// in the VS.net IDE.
/// </summary>
internal class CategorizedCheckBoxListControlDesigner :
    System.Web.UI.Design.ControlDesigner
{
    /// <summary>
    /// Provides easy access the properties set in the IDE.
    /// </summary>
    protected CategorizedCheckBoxList ccbl;
    /// <summary>
    /// Initializes the designer
    /// </summary>
    /// <param name="component"></param>
    public override void Initialize(IComponent component)
    {
        // Make sure that this designer is attached to
        // a CategorizedCheckBoxList
        if(component is CategorizedCheckBoxList)
        {
            base.Initialize (component);
            ccbl = (CategorizedCheckBoxList)component;
        }
    }

 このクラスでinternalというアクセス修飾子を使用していることに注意してください。このCategorizedCheckBoxLisControlDesignerクラスは大幅にカスタマイズされているので、CategorizedCheckBoxListクラスでのみ使用されると考えてよいでしょう。ControlDesignerinternalとして宣言すると、同じ.NETアセンブリ内のファイルからしか利用できなくなります。

 最初に、Visual Studioで設定されたプロパティにアクセスするためのCategorizedCheckBoxList変数を生成します。次に、Initializeメソッドをオーバーライドします。このデザイナを使用するコンポーネントがCategorizedCheckBoxListである場合は、そのコンポーネントをロードして、CategorizedCheckBoxListプロテクト変数を初期化します。

 GetDesignTimeHtmlメソッドを実行すると、CategorizedCheckBoxListをサンプルデータと一緒に表示するためのHTMLが次のように生成されます。なお、太字のコードはCategorizedCheckBoxListから取得されるプロパティを示しています。

/// <summary>
/// Writes the HTML used be VS.net to display
/// the control at design-time.
/// </summary>
/// <returns></returns>
public override string GetDesignTimeHtml()
{
    try
    {
        // Start building the HTML
        StringBuilder Sb = new StringBuilder();

        // Table
        Sb.Append("<table");
        Sb.Append(" cellspacing=\"");
        Sb.Append(ccbl.CellSpacing.ToString());
        Sb.Append("\"");
        Sb.Append(" cellpadding=\"");
        Sb.Append(ccbl.CellPadding.ToString());
        Sb.Append("\"");
        Sb.Append(" border=\"0\">");
        // Category Row
        Sb.Append("<tr><td colspan=\"4\"");
        Sb.Append(" class=\"");
        Sb.Append(ccbl.CategoryCssClass);
        Sb.Append("\"");
        Sb.Append(">Fruit</td></tr>");
        // Item Row
        Sb.Append("<tr valign=\"top\">");
        Sb.Append("<td><input type=\"checkbox\" "
            + "name=\"1\" value=\"1\" class=\"");
        Sb.Append(ccbl.CheckBoxCssClass);
        Sb.Append("\"></td>");
        Sb.Append("<td class=\"");
        Sb.Append(ccbl.TextCssClass);
        Sb.Append("\">Apples</td>");
        Sb.Append("<td><input type=\"checkbox\" "
            + "name=\"1\" value=\"2\" class=\"");
        Sb.Append(ccbl.CheckBoxCssClass);
        Sb.Append("\"></td>");
        Sb.Append("<td class=\"");
        Sb.Append(ccbl.TextCssClass);
        Sb.Append("\">Oranges</td>");
        Sb.Append("</tr>");
        // Item Row
        Sb.Append("<tr valign=\"top\">");
        Sb.Append("<td><input type=\"checkbox\" "
            + "name=\"1\" value=\"3\" class=\"");
        Sb.Append(ccbl.CheckBoxCssClass);
        Sb.Append("\"></td>");
        Sb.Append("<td class=\"");
        Sb.Append(ccbl.TextCssClass);
        Sb.Append("\">Tangerines</td>");
        Sb.Append("<td></td>");
        Sb.Append("<td class=\"");
        Sb.Append(ccbl.TextCssClass);
        Sb.Append("\"></td>");
        Sb.Append("</tr>");
        // Category Row
        Sb.Append("<tr><td colspan=\"4\"");
        Sb.Append(" class=\"");
        Sb.Append(ccbl.CategoryCssClass);
        Sb.Append("\"");
        Sb.Append(">Vegetables</td></tr>");
        // Item Row
        Sb.Append("<tr valign=\"top\">");
        Sb.Append("<td><input type=\"checkbox\" "
            + "name=\"2\" value=\"1\" class=\"");
        Sb.Append(ccbl.CheckBoxCssClass);
        Sb.Append("\"></td>");
        Sb.Append("<td class=\"");
        Sb.Append(ccbl.TextCssClass);
        Sb.Append("\">Broccoli</td>");
        Sb.Append("<td><input type=\"checkbox\" "
            + "name=\"2\" value=\"2\" class=\"");
        Sb.Append(ccbl.CheckBoxCssClass);
        Sb.Append("\"></td>");
        Sb.Append("<td class=\"");
        Sb.Append(ccbl.TextCssClass);
        Sb.Append("\">Green Beans</td>");
        Sb.Append("</tr>");
        // Item Row
        Sb.Append("<tr valign=\"top\">");
        Sb.Append("<td><input type=\"checkbox\" "
            + "name=\"2\" value=\"3\" class=\"");
        Sb.Append(ccbl.CheckBoxCssClass);
        Sb.Append("\"></td>");
        Sb.Append("<td class=\"");
        Sb.Append(ccbl.TextCssClass);
        Sb.Append("\">Potatoes</td>");
        Sb.Append("<td><input type=\"checkbox\" "
            + "name=\"2\" value=\"4\" class=\"");
        Sb.Append(ccbl.CheckBoxCssClass);
        Sb.Append("\"></td>");
        Sb.Append("<td class=\"");
        Sb.Append(ccbl.TextCssClass);
        Sb.Append("\">Tomatoes</td>");
        Sb.Append("</tr>");
        // End the table
        Sb.Append("</table>");
        return Sb.ToString();
    }
    catch(Exception ex)
    {
        // Display the error in VS.net, in Design view
        return String
            .Concat("<h3>Error</h3>Stack Trace:<br>", ex.StackTrace);
    }
}

 ここまでくれば、後はこのControlDesignerCategorizedCheckBoxListコントロールから利用できるように指定するだけです。そのために、もう一度コード属性を使います。

/// <summary>
/// The CategorizedCheckBoxList is like a CheckBoxList,
/// but with the ability to categorize the display of items.
/// </summary>
[Designer(
"UsefulControls.CategorizedCheckBoxListControlDesigner"),
DefaultProperty("DataTable"),
ToolboxData("<{0}:ccbl runat=server></{0}:ccbl>")]
public class CategorizedCheckBoxList :
    System.Web.UI.WebControls.WebControl
{
    /// <summary>
    /// Initializes a new instance of
    /// the CategorizedCheckBoxList class.
    /// </summary>
    public CategorizedCheckBoxList()
    {
        // Init the class
    }

 以上でCategorizedCheckBoxListコントロールのコーディングは終わりです。ここから先は、このコントロールの使い方を見ていきましょう。

Webカスタムコントロールの利用

 カスタムコントロールの使い方としては、ツールボックスからドラッグアンドドロップしてページに配置するのが最も簡単な方法です。作成したコントロールをツールボックスに追加するには、まずツールボックス上で右クリックして[Add/Remove Items]を選択します。

 するとダイアログボックスが表示されるので、そこでローカルPCの.NET Framework Componentsフォルダを選択し、Web Control LibraryプロジェクトのDLLを指定します。ただし、DLLの場所は、プロジェクトをデバッグ構成でビルドした場合はプロジェクトフォルダの下の「bin\Debug」フォルダに、リリース構成でビルドした場合は「bin\Release」になっています。

 DLLを追加すると、CategorizedCheckBoxListというコンポーネントが表示されるので選択します。

 [OK]をクリックするとツールボックス上にCategorizedCheckBoxListが表示されるはずです。

 ASPXページでCategorizedCheckBoxListコントロールを利用するために、このコントロールをツールボックスからVisual Studio .NETのデザインビュー内のページにドラッグアンドドロップします。コントロールをページに配置すると、表示される列数やCSSプロパティを設定できるようになります。

 このコントロールのデータソースとなるDataTableは、分離コードで指定することをお勧めします。これは、用意できていないうちにCategorizedCheckBoxListからアクセスされるのを防ぐためです。一方、チェックボックスのカテゴリ、値、およびラベルを示す列の名前は、分離コードファイルとASPXページのどちらで指定してもかまいません。

 それでは、この記事のダウンロードサンプルに収録されている「Default.aspx」ページの内容を見ていきましょう。Page_Loadメソッドは次のようになっています。

private void Page_Load(object sender, System.EventArgs e)
{
    // If the page has not posted-back,
    // or if it has but the "Show List" checkbox is checked,
    // get the data for our CategorizedCheckBoxList.
    if(!IsPostBack || chkShowList.Checked == true)
    {
        // Get the data
        GetMdbData();
    }
    else
    {
        // Hide the CategorizedCheckBoxList
        CategorizedCheckBoxList1.Visible = false;
        // Hide the "Show List" checkbox
        chkShowList.Visible = false;
        // Hide the submit button, too
        btnTestValues.Visible = false;
    }
}

 ここでは、コントロールが表示される場合にのみコントロール用データの取得を行っています(CheckBoxListコントロールのインスタンスを動的に生成する場合は、コントロールの表示と非表示に関係なく、データ取得を毎回行う必要があります)。

 次に、GetMdbDataメソッドを呼び出し、Accessデータベースからデータを選択します。GetMdbDataメソッドのコードは次のとおりです。

protected void GetMdbData()
{
    // Create a connetion
    OleDbConnection Conn = new OleDbConnection();
    Conn.ConnectionString = String.Concat(
        "Provider=Microsoft.Jet.OleDb.4.0;data source=",
        Server.MapPath("SampleData.mdb"));
    // Build a data adapter that selects all of the columns
    // and rows in a saved query called qryCarModelCarMaker,
    // in the Access database
    OleDbDataAdapter Adp = new OleDbDataAdapter(
        "SELECT * FROM qryCarModelCarMaker", Conn);

    // Create an instance of our Cars typed dataset
    Cars TypedSampleData = new Cars();
    // Use the adapter to fill the CarTable
    Adp.Fill(TypedSampleData, "CarTable");
    // Specify the data properties for
    // our CategorizedCheckBoxList control
    CategorizedCheckBoxList1.DataTable = TypedSampleData.CarTable;
    CategorizedCheckBoxList1.DataTextColumn = "Model";
    CategorizedCheckBoxList1.DataValueColumn = "CarModelPK";
    CategorizedCheckBoxList1.DataCategoryColumn = "Make";
    // Clean-up
    Conn.Dispose();
    Adp.Dispose();
}

 ここではダイナミックデータベースクエリの結果を格納した型付きのDataSetを使用していますが、CategorizedCheckBoxListコントロールは、DataTableと、および必要なデータを格納している列の名前だけを受け付けます。サンプルプロジェクトの他のページでは、型付きのデータとそうでないデータの両方をXMLファイルから読み込んでいます。

 いくつかのアイテムを選択したうえで[Test Checkbox Values]ボタンをクリックすると、選択したチェックボックスの値が表示されます。このサンプルで表示されるのは、選択された車種のプライマリキーの値です。では、選択したアイテムの値の取得方法を見てみましょう。

private void btnTestValues_Click(object sender, System.EventArgs e)
{
    // Were any checkboxes checked?
    if(CategorizedCheckBoxList1.Selections.Count > 0)
    {
        // Yes. Let's use a string builder to tell
        // the user what we find.
        StringBuilder Sb = new StringBuilder();
        Sb.Append("The following values were selected:");
        
        // Use an HTML un-ordered list to display the values
        Sb.Append("<ul>");

        // Loop through the selections
        foreach(string check in CategorizedCheckBoxList1.Selections)
        {
             // Add this item to our HTML list
             Sb.Append("<li>");
             Sb.Append(check);
             Sb.Append("</li>");
        }
        // End the list
        Sb.Append("</ul>");
        // Set the text of our label
        Label1.Text = Sb.ToString();
    }
    else
    {
        // Use our label to tell the user
        // that nothing was picked.
        Label1.Text = "No checkboxes were selected.";
    }
}

 このメソッドを実行すると、選択したチェックボックスの値のリストが次のように表示されます。

 以上でCategorizedCheckBoxListコントロールの基本的な説明はほぼ終わりですが、デフォルト選択の指定方法の説明をまだしていませんでした。このコントロールでは、ArrayListに基づくSelectionsというパブリックプロパティを参照することで、事前選択オプションを簡単に指定できます。

// If the page has not posted-back,
// or if it has but the "Show List" checkbox is checked,
// get the data for our CategorizedCheckBoxList.
if(!IsPostBack || chkShowList.Checked == true)
{
    // Get the data
    GetMdbData();
    // Select all of the Audi models
    CategorizedCheckBoxList1.Selections.Add("37");
    CategorizedCheckBoxList1.Selections.Add("38");
    CategorizedCheckBoxList1.Selections.Add("39");
    CategorizedCheckBoxList1.Selections.Add("40");
    CategorizedCheckBoxList1.Selections.Add("41");
}

 なお、このArrayListには必ず文字列値を指定するということを忘れないでください。

まとめ

 .NET Frameworkには、さまざまなWebコントロールを含んだツールボックスが用意されています。カスタムコントロールを自分で開発しようとする前に、必ず標準のコントロールの利用を検討してみるべきです。標準のWebコントロールは、ここで紹介した例よりもかなり複雑なものです。上位レベルおよび下位レベルのブラウザのサポートや、豊富なイベントモデルが用意されています。しかも、こうしたWebコントロールについては、ASP.NETのリリースごとにMicrosoftによるアップグレードが期待ができます。

 しかし、あなたの要求を満たすコントロールが見つからないときには、どんどん自作してください。ユーザーコントロールに比べれば複雑ですが、Webカスタムコントロールは手に負えないほど難解なものではありませんし、数多くのメリットをもたらしてくれます。あらゆるコントロールの描画に利用されるHTMLについても同じことが言えます。想い描いたアイデアをHTMLで表現する方法が理解できれば、Webカスタムコントロールを構築することができます。

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

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

もっと読む

この記事の著者

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

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

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

Conrad Jalali(Conrad Jalali)

ユーザビリティに重点を置く設計会社、Useful Studiosの共同創立者。ここ5年間はActive Server Pages(ASP)に取り組んでおり、C#とSQL Server 2000を使用したASP.NET開発を専門とする。Sarah Lawrence Collegeを卒業し、教養課程の学士号を取得。Microsoft認定プロフェッショナル(MCP)の資格を所有。妻Elizab...

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

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

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/162 2005/11/08 17:41

イベント

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

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

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

メールバックナンバー