CodeZine(コードジン)

特集ページ一覧

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

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

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2005/09/14 18:00

ASP.NETページで利用できる独自のサーバーコントロールを設計したいと思ったことはありませんか? 本稿では、カテゴリ別に表示できるチェックボックスリストを作成して、Webカスタムコントロールの利用方法について解説します。

目次

目次

前提条件

  • C#、Visual Studio .Net、およびASP.NETアプリケーション作成に詳しいこと
  • ADO.NETおよびデータバインディングの基本を理解していること
  • HTMLおよびカスケーディングスタイルシート(CSS)の基本を理解していること

はじめに

 ASP.NETページで利用できるようなサーバーコントロールを設計したいと思ったことはありませんか? 私があるページを手がけていたとき、CheckBoxListのようなコントロールが必要になりました。一連のチェックボックスをグループにまとめ、それぞれのグループにカテゴリ見出しを付ける必要がありました。このコントロールに配置すべきアイテムとアイテムカテゴリはデータベースに格納されていました。しかし、標準のCheckBoxListコントロールでは、チェックボックスをカテゴリ別に表示することができません。

 もちろん、CheckBoxListコントロールをうまく使いながらこうした問題の解決を試みる方法は数多くあります。簡単に思いつく方法は2つあります。1つは、カテゴリまたはグループごとに個別のCheckBoxListコントロールをASPXページに追加し、カテゴリ見出しはハードコーディングするという方法です。もう1つは、分離コードファイルのPage_Loadメソッドを使用して、実行時にCheckBoxListコントロールをプログラム的にページに追加するという方法です。

 私のプロジェクトでは、チェックボックスとして表示されたアイテムのなかからユーザーが任意の数のアイテムを選択できるフォームを作成する必要がありました。ここでは仮に、カテゴリは自動車メーカー、アイテムは車種を表すものとして話を進めます。アイテムとカテゴリはデータベースに格納されていたので、新たなメーカーや車種の追加のほか、既存エントリの変更や削除も行うことができました。

 「カテゴリ名とCheckBoxListコントロールをWebフォーム上にハードコーディングする」という選択肢はすぐに除外しました。メーカーの追加、削除、名前変更が起こるたびにASPXページとC#の分離コードファイルの更新を行うのは非現実的だったからです。

 また、実行時にコントロールをフォームに追加するという選択肢も賢明とは言えません。プログラミングによってWebコントロールをプレースホルダや他のコンテナコントロールに追加するのはとても容易です。しかし問題は、ユーザーの指定した値を読み取るためには、ポストバックの状態やチェックボックスの表示と非表示に関わらず、ページを読み込むたびにコントロールの「リビルド」(プログラムによるコントロールの再作成)を行わなければならないという点です。これではサーバーに相当な負荷がかかってしまいます。

 私が本当に欲しかったのは、フォームにドラッグアンドドロップしたり、いくつかのプロパティを設定したり、データバインディングコードを追加したりでき、デザイン時にきちんと形ができているコントロールでした。標準のWebコントロールにはぴったり来るものがなさそうだったので、自分の手でカスタムサーバーコントロールを作ることにし、これを「CategorizedCheckBoxList」と名付けました。コントロールを一から作り上げると思うと気力が萎えてしまうかもしれませんが、実際にはそれほど難しいものではないことがわかりました。

コントロールの背景知識

 Webカスタムコントロールは、いくつかの点でユーザーコントロールよりも優れています。まず、ユーザーコントロールは、それを利用するプロジェクトごとにASCXやクラスファイルをコピーしなければならないので、再利用に適していません。また、Visual Studio .NETでデザイナサポートが用意されていないという点でも不利です。ユーザーコントロールをWebフォームに配置してみれば、私の言いたいことがわかるはずです。ユーザーコントロールは汎用的なグレーボックスで表示され、ユーザーコントロールのプロパティ値を示すヒントはIDEから一切提供されません。ユーザーコントロールの主な利点と言えば、作成が容易なので迅速なアプリケーション開発ができるということくらいです。

 一方、Webカスタムコントロールには大きな魅力があります。サーバーコントロールにはASCXファイルが存在しないので、非常に簡単に再利用できます。DLLへのプロジェクト参照を設定しさえすれば、すぐに動作させることができます。WebカスタムコントロールはVisual Studio .NETツールボックスに追加でき、こうすることによって再利用は極めて容易になります。さらに、グローバルアセンブリキャッシュ(GAC)にWebカスタムコントロールをインストールすることも可能です。なお、GACの詳細については、本稿の範囲を超えるのでこれ以上は触れません。ともかく、私がWebカスタムコントロールを気に入っている最大の理由は、申し分のないカスタマイズが可能で、デザイン時サポートがあるということです。

 Webカスタムコントロールを作成するにあたっての最大の障壁は、コントロール用のHTMLを記述しなければならないという点でしょう。Visual Studio .NETにはWebカスタムコントロールのためのビジュアルデザイナがないので、ツールボックスのアイテムを自作のコントロールにドラッグアンドドロップすることができません。しかし、C#プログラムを習得できるくらいなら、HTMLを覚えるのも苦ではないでしょう。

プロジェクト再利用のための計画

 これから作成するコントロールを簡単に再利用できるようにするために、新しいWebコントロールライブラリのプロジェクトを用意することにしましょう。

 Visual Studio .NETによって、「WebCustomeControl1.cs」というクラスが作成されます。この名前を「CategorizedCheckBoxList.cs」に変更します(なお、クラスファイル内の記述もWebCustomControl1CategorizedCheckBoxListに置き換えるようにしてください)。

データバインドプロパティ

 今回作成するコントロールにはデータソースをバインドする必要があります。問題を簡単にするために、このコントロールではADO.NETのDataTableをデータソースとして使用することにしました。DataTable型を選んだのは、コントロールにはデータやデータの取得方法に関する情報をあまり持たせたくなかったからです。今回のコントロールが必要とするデータは、DataTable、カテゴリ名として使われるフィールド名、チェックボックスの値、それに各チェックボックスのラベルとなるテキストだけです。それぞれに関するデータバインドプロパティを次に示します。

  • DataTable
  • System.Data.DataTable型。データソースとして利用されるテーブルを表します。このテーブルには、カテゴリ名の列、チェックボックス値の列、および各チェックボックスのラベルの列を必ず含めなければなりません。今回のコントロールは、カテゴリを第一キー、ラベルを第二キーとしてこのテーブルを自動的にソートします。
  • DataCategoryColumn
  • 文字列型。DataTableのテーブル内で、アイテムの所属カテゴリ名を格納している列の名前を表します。
  • DataValueColumn
  • 文字列型。DataTableのテーブル内で、チェックボックスの値を格納している列の名前を表します。DataValueColumnDataTextColumnに同じ列名を指定できます。DataValueColumnには任意の型のデータを格納できますが、文字データを使用する場合、カンマを含めることはできません。
  • DataTextColumn
  • 文字列型。DataTableのテーブル内で、チェックボックスの隣に表示されるラベルを格納している列の名前を表します。

 また、ページを読み込むときにチェックマークを入れるべきチェックボックスを指定する手段も必要になります。この機能を用意しておくと、フォームのデフォルト値を設定したり、以前に保存しておいた値をユーザーが編集するようなフォームを実現したりできます。そのためのプロパティを、ArrayListオブジェクトを使って用意することにしました。

  • Selections
  • 文字列値を格納するArrayListオブジェクト。選択状態(チェックマークがオンの状態)としてマークすべき値のリストです。このプロパティを利用して、選択状態の値を指定し、ポストバック後に選択状態の値のリストを取得することができます。

CSSとその他の視覚的プロパティ

 CSSクラスセレクタのための追加プロパティを公開することで、CategorizedCheckBoxListコントロールの視覚的スタイルをASPXページから制御することができます。

  • TableCssClass
  • 文字列型。一連のチェックボックスを配置するテーブルのスタイルを指定するCSSクラスです。
  • RowCssClass
  • 文字列型。チェックボックスを含む各テーブル列のスタイルを指定するCSSクラスです。
  • CategoryCssClass
  • 文字列型。カテゴリ見出しを含むセルのスタイルを指定するCSSクラスです。
  • CheckBoxCssClass
  • 文字列型。チェックボックスを含むセルのスタイルを指定するCSSクラスです。
  • TextCssClass
  • 文字列型。チェックボックのラベルを含むテーブルセルのスタイルを指定するCSSクラスです。

 さらに次のようなプロパティを追加しました。

  • TableWidth
  • 文字列型。テーブルの幅をピクセル数またはパーセント(%記号を付ける)で指定します。
  • CellPadding
  • 整数型。テーブルセルの内側の余白を指定します。
  • CellSpacing
  • 整数型。テーブルセルの間隔を指定します。
  • Columns
  • 整数型。チェックボックスの表示に用いる列数を指定します。
  • SharedTable
  • ブール型。Trueの場合は、すべてのチェックボックスを1つの同じテーブルに表示します。Falseの場合は、一連のチェックボックスをカテゴリごとに独立したテーブルに表示します。このプロパティを用意したのは、チェックボックスのラベルの長さがカテゴリごとに大きく異なる場合に、ページの見た目が悪くなってしまうからです。

Renderメソッド

 Webカスタムコントロールを扱うときには、Renderメソッドが大活躍します。簡単に言うと、Renderメソッドは、Webブラウザ上にコントロールを表示するためのHTMLを出力します。ちょうどクラシックASPのResponse.Writeを使うのと同じです。ただし、Renderメソッドでは、ホストASPXページから自動的に渡されるHtmlTextWriterオブジェクトを利用する点が異なります。

 細かい処理はすべてVisual Studio .Netが面倒を見てくれるので、このWebカスタムコントロールをページ上で使用するための特別なコーディングをする必要はありません。ページにWebカスタムコントロールを配置するだけで、互いにどのようにやりとりをすればよいかをページとコントロールが理解してくれるのです。

 では、今回のWebカスタムコントロールのRenderメソッドを詳しく見てみましょう。

/// <summary>
/// Writes out the HTML needed to render this control.
/// </summary>
/// <param name="output">The HTML text writer the we will utilize.
/// This is passed to the control automatically,
/// by the host ASPX page.</param>
protected override void Render(HtmlTextWriter output)
{
    try
    {
        // No need for ViewState
        this.EnableViewState = false;
        // Make sure that the htmlFieldName is set.
        GetHtmlFieldName();
        // Should the control be visible?
        if(this.Visible == true)
        {
             // Yes. Render the html.
             BuildCategorizedCheckBoxList(output);
        }
    }
    catch(Exception ex)
    {
        // Something bad happened. 
        // Let's tell the user what that was.
        output.Write("Error building CategorizedCheckBoxList:<br>");
        output.Write(ex.Message);
    }
}

 ご覧のとおり、最初にコントロールのViewStateを無効にします。これにより、Webブラウザに送られるHTMLの負荷が軽減されます。次に、ReadPostBackというメソッドを呼び出します。このメソッドにより、フォームが発行された場合に、どのチェックボックスが選択されていたかを把握します。最後に、コントロールを可視化する場合にはBuildCategorizedCheckBoxListメソッドを呼び出して、コントロールを表示するためのHTMLを出力させます。

 ReadPostBackメソッドは、チェックボックスフィールドに割り当てている名前を取得し、Request.Formコレクションに同じフィールド値がないか探します。コントロールを含むWebフォームがポストバックされると、ASP.NETはフォーム上のすべてのフィールドを含むNameValueCollectionの形でそのFormオブジェクトを公開します。フィールドがない場合はそのFormオブジェクトはnullになり、フィールドがある場合はif文内のコードが実行されます。

/// <summary>
/// Retrieves a list of the checkbox values that were
/// selected (checked), if the form was submitted.
/// This is kind of a poor-man's view state implementation.
/// But unlike view state,
/// it doesn't add anything to the page weight.
/// </summary>
protected void ReadPostBack()
{
    // See what field name we are assigning to the checkboxes
    GetHtmlFieldName();
    // Were any checkboxes checked?
    if(HttpContext.Current.Request.Form[htmlFieldName] != null)
    {
        // Since we assigned the same field name to all of
        // the checkboxes, ASP.NET will give us
        // a comma-delimited list of the selections.
        // First, conver the list to a string array.
        string [] Input = HttpContext.Current
            .Request.Form[htmlFieldName].Split(',');
        // Then, iterate through the array and add
        // each value to our ArrayList.
        for(int i = 0; i < Input.Length; i++)
        {
            selections.Add(Input[i]);
        }
    }
}

 詳しくは後述しますが、チェックボックスフィールドのHTMLを作成すると、各フィールドに同じ名前が割り当てられていることがわかるでしょう。ASP.NETがRequest.Formコレクションにフィールドを格納するときには、複数の値を持つフィールドをカンマ区切りリストにまとめます。あとはこのカンマ区切りリストを文字列の配列に変換し、それぞれの値を選択状態変数(つまりArrayList)に追加するだけです。

 ReadPostBackメソッドは、パブリックプロパティであるSelectionにアクセスするときにも呼び出されます。この呼び出しが必要なのは、ページ読み込み時に発生するイベントのシーケンス上の理由からです。これにより、ホストページ側は、どのチェックボックスが選択されていたかをコントロールの描画に先立って把握できるのです。

 次に示すのは、GetHtmlFieldNameメソッドです。

/// <summary>
/// Returns the unique field name
/// that we will assogn to the checkboxes, later.
/// </summary>
protected void GetHtmlFieldName()
{
    // Pickup the ID assigned to the control
    // in the consuming ASPX page
    htmlFieldName = this.ID;
}

 GetHtmlFieldNameメソッドは、利用側のASPXページによってコントロールに割り当てられたIDの値をhtmlFieldNameというプロテクト変数に割り当てます。なぜこのIDをわざわざ取り上げるのか不思議に思われるかもしれません。ここでは、チェックボックスフィールドの作成時に使う名前を自分の都合に合わせてハードコーディングすることを避けたかったので、このIDを利用しています。

 フィールド名が確実に一意であれば、複数のCategorizedCheckBoxListコントロールを同じWebフォーム上に配置し、どのチェックボックスが選択されているのかを各コントロールに適切に判断させることもできます。Visual Studio .NETは、プログラマが1つのページ上の複数のコントロールに同一IDを割り当てないようにしてくれます(あるいは少なくともその手助けをしてくれます)。ですから、このIDプロパティは私たちの目的にかなったものだと言えるでしょう。

 BuildCategorizedCheckBoxListは、少々複雑なメソッドです。このメソッドは、出力ストリームに直接書き込みを行うためにRenderメソッドからHtmlTextWriterオブジェクトを取得します。このオブジェクトは、コントロール用HTMLを出力するために必要となる面倒な作業をすべて行ってくれます。それでは、このメソッドの処理内容をいくつかのセクションに分けて見ていきましょう。

 まず、DataTableにデータが入っているか否かの確認から始めます。データが入っていなければ、キーワードreturnによってこのメソッドから抜け出します。

/// <summary>
/// Outputs the HTML for this control.
/// </summary>
/// <param name="output"></param>
protected void BuildCategorizedCheckBoxList(HtmlTextWriter output)
{
    // Do we have any data?
    if(dataTable == null || dataTable.Rows.Count < 1)
    {
        // There is no data, so there's nothing to render.
        return;
    }

 次に、チェックボックスのカテゴリ、値、ラベルを示す列を探します。これらの列が見つかれば、ローカル変数にインデックスを割り当てます。この値は後で使用します。テーブルの列を名前で参照するのは、インデックス番号で参照するよりもずっと時間がかかるので、このコードではインデックスで参照することにしました。

 初期化時に、これらの列のインデックス値をわざと-1に設定します。すべてそうしておく必要があります。というのは、DataRawにおいて列のインデックス番号が-1の列の値を取得しようすると、.NETフレームワークが例外を発行してくれるからです。

// First retrieve the column indexes of
// the specified columns. Later, we'll get
// the values that we need using these indexes.
// This is faster than referencing a column by name.
int CategoryColumnIndex = -1;
int TextColumnIndex = -1;
int ValueColumnIndex = -1;
for(int i = 0; i < dataTable.Columns.Count; i++)
{
    if(dataTextColumn == dataTable.Columns[i].ColumnName)
    {
        TextColumnIndex = i;
    }

    if(dataValueColumn == dataTable.Columns[i].ColumnName)
    {
        ValueColumnIndex = i;
    }
    if(dataCategoryColumn == dataTable.Columns[i].ColumnName)
    {
        CategoryColumnIndex = i;
    }
}

 次に、すべてのカテゴリとそのチェックボックスを同じテーブル上に表示するかどうかを判断します。同じテーブルに表示する場合は、次のようにテーブルの開始タグを書き出します。

/**********************************/
/* Build the html to display of the items */
/**********************************/
// If the consuming page wants one single, shared table,
// write the opening tag, now.
if(this.sharedTable == true)
{
    output.Write(GetTableTag());
}

 GetTableTagは、テーブルの開始タグ用HTMLを生成するヘルパーメソッドです。より正確に言えば、このメソッドは、tableTagというプライベート変数の値がもしnullであれば値を割り当てたうえで、その値を返すものです。話を簡単にするため、GetTableTagの詳細についてここでは詳しく触れません。ただ、文字列は不変のデータ型なので、あとで何度も文字列に追加していくようなときは必ずStringBuilderオブジェクトを使うということを覚えておけばいいでしょう。

 話を元に戻しましょう。続いて、DataTableに含まれるカテゴリのリストを取得する必要があります。そこでまず、このDataTableからDataViewを生成し、カテゴリ列に基づいてソートを行います。LastCategoryというローカル変数を用意し、これを使ってDataViewの各列を参照しながら新たなカテゴリ名が現れていないか繰り返しチェックしていきます。新しいカテゴリが現れるたびに、そのカテゴリをCategoriesというArrayListに追加します。

// Create a string for the "previous" category
string LastCategory = string.Empty;
// Sort the data by category
DataView Category = dataTable.DefaultView;
Category.Sort = this.dataCategoryColumn;
// Assemble a distinct list of the categories
// found in the data
ArrayList Categories = new ArrayList();
for(int i = 0; i < Category.Count; i++)
{
    if(LastCategory != Category[i][CategoryColumnIndex].ToString())
    {
        Categories.Add(Category[i][CategoryColumnIndex].ToString());
        LastCategory = Category[i][CategoryColumnIndex].ToString();
    }
}

 これでようやくカテゴリのリストができあがったので、このリストに対してループ処理を行い、各カテゴリ(および対応するチェックボックス)のHTMLを出力します。これを行うために、DataViewを作成し、そのビューの列数を対象カテゴリの列数以内に制限するRowFilterを設定します。

// Loop through the categories
for(int i = 0; i < Categories.Count; i++)
{
    // Get the rows for this category only
    DataView CategoryItems = new DataView(dataTable);
    CategoryItems.RowFilter = String.Format("{0}=\'{1}\'",
        this.dataCategoryColumn,
        Categories[i].ToString().Replace("\'","\'\'"));
    CategoryItems.Sort = this.dataTextColumn;

 カテゴリごとに別々のHTMLテーブルを作成する場合は、この時点でテーブルの開始タグを出力します。

// If the consuming page wants a separate table
// for each category, write the opening tabel tag
// for the current category, now.
if(this.sharedTable == false)
{
     output.Write(GetTableTag());
}

 今度は、現在処理しているカテゴリ用のHTMLを出力します。ヘルパーメソッドであるOutputCategoryRowを使用して、必要なHTMLを出力します。

// Add the category heading to the html
OutputCategoryRow(output, (string)Categories[i]);

 この段階になると、話が少し複雑になってきます。現在処理しているカテゴリに属するアイテム数や、チェックボックスの表示に使用する列数はわかっています。しかし、すべてのアイテムの表示に必要な列数が明確になっていません。

 そこで、カテゴリ内の全アイテム数を表示列数で割ることによって、全アイテムの表示に必要な列数を求めます。余りが出た場合には、求めた列数に1を加えます。

// Calculate the total number of rows based on
// the item count and the number of columns
totalItems = CategoryItems.Count;
totalRows = totalItems / columns;
// If there was anything left-over as a result of
// the division, we need to add another row
if(totalItems % columns > 0)
{
    totalRows++;
}

 次に、列を処理するループに入ります。現在処理中のアイテムのインデックス番号を保持するためのカウンタを用意します。このカウンタの値は0から始まります。各アイテムのHTMLを書き出すたびに、最後のアイテムでないかチェックします(CurrentItemIndexの値が「全アイテム数-1」に等しければ、最後のアイテムの列を作成し終えたことになります)。

 現在のカテゴリにおける最後のアイテムのHTML出力が終わったら、CurrentItemIndexを-1に設定します。CurrentItemIndexの値が-1なのに表示すべき列がまだ残っている場合には、チェックボックスとその隣のテキストの両方に対して空のテーブルセル用HTMLを出力します。

// Create an integer to hold the index number
// for the current item
int CurrentItemIndex = 0;
// Now loop through the rows
for(int Row = 0; Row < totalRows; Row++)
{
    // Determine the starting index for this row.
    // This is the same calculation that we would perform
    // to handle paging in a grid.
    int Start = (Row * columns);
    // Create an integer to hold the index number
    // for the current item
    int CurrentItemIndex = Start;
    // Start the row
    output.Write("\t");
    output.Write("<tr class=\"");
    output.Write(this.rowCssClass);
    output.Write("\">");
    output.Write("\n");
    // Column loop
    for(int Col = 0; Col < columns; Col++)
    {
        // Make sure that we haven't hit a blank entry.
        if(CurrentItemIndex == -1)
        {
            // Add an empty cell (two, actually)
            AddEmptyCells(output);
        }
        else
        {
            // Now add the checkbox and text
            OutputCheckBox(output,
                htmlFieldName,
                CategoryItems[CurrentItemIndex]
                    [TextColumnIndex].ToString(),
                CategoryItems[CurrentItemIndex]
                    [ValueColumnIndex].ToString(),
                IsChecked(CategoryItems[CurrentItemIndex]
                    [ValueColumnIndex].ToString())
                );
            // If we have more data left,
            // increment the current index counter.
            if(CurrentItemIndex < (totalItems - 1)
                && CurrentItemIndex != -1)
            {
                // increment the current index
                CurrentItemIndex++;
            }
            else
            {
                // We're at the end of the items
                // in the data table. Set the value of
                // the current index to -1,
                // which our rendering code
                // ignores (creates empty table cells)
                CurrentItemIndex = -1;
            }
        }
        // Add a line break
        output.Write("\n");
    }

 この時点で列内のすべての列の処理が終わっているので、列の終わりに必要な処理を行います。

    // End the row
    output.Write("\t");
    output.Write("</tr>\n");
}

 このループは、すべての列に対する処理が終了するまで繰り返されます。最後に、カテゴリごとに別々のテーブルを使う場合には、現在のカテゴリテーブルを次のようにして終了させます。

    // Table tag
    if(this.sharedTable == false)
    {
        output.Write("</table>\n");
    }
}

 このループは、すべてのカテゴリの処理が終了するまで繰り返されます。共有テーブルを使用する場合は、ループの最後でテーブルの終了タグを出力します。

    // Finish the table
    if(this.sharedTable == true)
    {
        output.Write("</table>\n");
    }

    /**********************************/
}

 これまで説明していなかったヘルパーメソッドのOutputCheckboxIsCheckedについて、ここで述べておきましょう。OutputCheckboxは、チェックボックスフィールドを含むテーブルセルと、その隣のラベルを含むテーブルセルのためのHTMLを出力します。ここでは、ラベルが2番目の行に重ならないようにするために、チェックボックスとそのラベルを別々のセルに配置しました。これにより、すべての要素をきれいに配置することができます。

 IsCheckedメソッドは、チェックボックスに「チェック」を付けるか付けないかを決めるために用いられます。ここで使っているSelectionsという変数はArrayListオブジェクトなので、その実装は容易です。

/// <summary>
/// Looks for a match between the current value and
/// the list of selected values.
/// </summary>
/// <param name="currentValue">The value that we want to look for.
/// </param>
/// <returns>True if the current value is contained in
/// the selected list. Otherwise, false.</returns>
protected bool IsChecked(string currentValue)
{
    // If we have selections, continue
    if(selections != null && selections.Count > 0)
    {
        // Can we find the current value?
        if(selections.IndexOf(currentValue) > -1)
        {
            // Yes, so this item should be marked
            // as selected (checked)
            return true;
        }
        else
        {
            // No, so this item should not be selected
            return false;
        }
    }
    else
    {
        // Nothing at all was selected, so return false
        return false;
    }
}

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

あなたにオススメ

著者プロフィール

  • Conrad Jalali(Conrad Jalali)

    ユーザビリティに重点を置く設計会社、Useful Studiosの共同創立者。ここ5年間はActive Server Pages(ASP)に取り組んでおり、C#とSQL Server 2000を使用したASP.NET開発を専門とする。Sarah Lawrence Collegeを卒業し、教養課程の学...

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

    japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.com や EarthWeb.c...

バックナンバー

連載:japan.internet.com翻訳記事

もっと読む

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5