WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

ACT ComboBox の日本語対応

by WebSurfer 2012年3月18日 16:50

AJAX Control Toolkit (ACT) の ComboBox は日本語に対応していませんが、それを無理やり(?)、ブラウザが IE のみの場合に限り、日本語対応させてみました。

日本語対応させた AJAX Control Toolkit の ComboBox

ヒントは MDSN の Visual Studio 共通フォーラムのスレッド「AJAX ComboBoxコントロール 日本語」にありました。

ComboBox は、テキストボックス(html コードで言うと <input type="text" ... />)の onkeypress イベントで、入力された文字列をリスト(html で言うと ul, li 要素を使った一覧)の中の文字列と比較していますが、IME が ON の時は onkeypress イベントが発生しないのがうまくいかない原因のようです。

その Workaround は、IE のみの場合ですが、以下の通りです。

  1. IME モードが ON のときは onkeypress イベントが発生しないので、代わりに onkeyup イベントを利用する。そのため _onTextBoxKeyUp イベントハンドラを作成し、テキストボックスの onkeyup イベントにアタッチする。
  2. _onTextBoxKeyDown イベントハンドラで、Keycode を取得しておく(IME モード ON の時は 229 になる)。
  3. _onTextBoxKeyUp イベントハンドラで、上記 ② で取得した Keycode が 229 で、かつ KeyCode が 13(ENTER) のとき(文字変 換を確定)に、_onTextBoxKeyPress イベントハンドラを実行する。

AJAX Control Toolkit のソースコードは CodePlex のサイトから入手できます。今回の場合は、JavaScript のコード ComboBox.pre.js を入手して、関係する部分を書き換えて差し替えてやります。

具体的には以下の通りです。実際に動かして試せるよう 実験室 にアップしましたので、興味のある方は試してみてください。

<%@ Page Language="C#" %>
<%@ Import Namespace="AjaxControlToolkit" %>
<%@ Import Namespace="System.Data" %>
<%@ Register
    Assembly="AjaxControlToolkit"
    Namespace="AjaxControlToolkit"
    TagPrefix="ajaxToolkit" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

  private static string[] japaneseWordListText;
  public string[] GetJapaneseWordListText()
  {
    if (null == japaneseWordListText)
    {
      string[] tempWordListText = new string[] {
        "あいうえお",
        "かきくけこ",
        "さしすせそ",
        "たちつてと",
        "なにぬねの",
        "はひふへほ",
        "まみむめも",
        "abcdef",
        "日本語",
        "東京都",
        "神奈川県"
      };            
      japaneseWordListText = tempWordListText;
    }
    return japaneseWordListText;
  }

  private static string[] wordListText;
  public string[] GetWordListText()
  {
    if (null == wordListText)
    {
      string[] tempWordListText = new string[] {
        "Alfa",
        "Alpha",
        "Bravo",
        "Charlie",
        "Delta",
        "Echo",
        "Foxtrot",
        "Golf",
        "Hotel",
        "India",
        "Juliett",
        "Juliet",
        "Kilo",
        "Lima",
        "Mike",
        "November",
        "Oscar",
        "Papa",
        "Quebec",
        "Romeo",
        "Sierra",
        "Tango",
        "Uniform",
        "Victor",
        "Whiskey",
        "X-ray",
        "Xray",
        "Yankee",
        "Zulu",
        "Zero",
        "Nadazero",
        "One",
        "Unaone",
        "Two",
        "Bissotwo",
        "Three",
        "Terrathree",
        "Four",
        "Kartefour",
        "Five",
        "Pantafive",
        "Six",
        "Soxisix",
        "Seven",
        "Setteseven",
        "Eight",
        "Oktoeight",
        "Nine",
        "Novenine"
      };
      Array.Sort(tempWordListText);
      wordListText = tempWordListText;
    }
    return wordListText;
  }
    
  protected void Page_Load(object sender, EventArgs e)
  {
    if (!Page.IsPostBack)
    {
      ComboBox1.DataSource = GetWordListText();
      ComboBox1.DataBind();

      ComboBox3.DataSource = GetJapaneseWordListText();
      ComboBox3.DataBind();
    }
  }

  protected void Button1_Click(object sender, EventArgs e)
  {
    Label1.Text = ComboBox1.SelectedItem.Text;
  }

  protected void Button3_Click(object sender, EventArgs e)
  {
    Label3.Text = ComboBox3.SelectedItem.Text;
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
  <title>AJAX Control Toolkit ComboBox の日本語対応</title>
</head>
<body>
  <form id="form1" runat="server">
  <ajaxToolkit:ToolkitScriptManager 
    ID="ToolkitScriptManager1" 
    runat="server">
  </ajaxToolkit:ToolkitScriptManager>

  <div>
    <ajaxToolkit:ComboBox ID="ComboBox1" 
      runat="server"
      AutoCompleteMode="SuggestAppend">
    </ajaxToolkit:ComboBox>
    <asp:Button ID="Button1" 
      runat="server" 
      Text="Button" 
      OnClick="Button1_Click" />
    <asp:Label ID="Label1" runat="server">
    </asp:Label>        
  </div>

  <div>
    <ajaxToolkit:ComboBox ID="ComboBox3" 
      runat="server"
      AutoCompleteMode="SuggestAppend">
    </ajaxToolkit:ComboBox>
    <asp:Button ID="Button3" 
      runat="server" 
      Text="Button" 
      OnClick="Button3_Click" />
    <asp:Label ID="Label3" runat="server">
    </asp:Label>        
  </div>
  </form>

  <script type="text/javascript">
  //<![CDATA[

  Sys.Extended.UI.ComboBox.prototype.createDelegates = function () {
    // ①
    // IME モードが ON のときは onkeypress イベントが発生
    // しない為、代わりに onkeyup イベントを利用。
    this._textBoxKeyUpHandler = 
      Function.createDelegate(this, this._onTextBoxKeyUp);

    // ここから下はオリジナルのコードそのまま。
    this._listMouseOverHandler = 
      Function.createDelegate(this, this._onListMouseOver);
    this._listMouseOutHandler = 
      Function.createDelegate(this, this._onListMouseOut);
    this._listMouseDownHandler = 
      Function.createDelegate(this, this._onListMouseDown);
    this._listClickHandler = 
      Function.createDelegate(this, this._onListClick);
    this._listDragHandler = 
      Function.createDelegate(this, this._onListDrag);
    this._listSelectStartHandler = 
      Function.createDelegate(this, this._onListSelectStart);
    this._listMouseWheelHandler = 
      Function.createDelegate(this, this._onListMouseWheel);
    this._textBoxClickHandler = 
      Function.createDelegate(this, this._onTextBoxClick);
    this._textBoxFocusHandler = 
      Function.createDelegate(this, this._onTextBoxFocus);
    this._textBoxBlurHandler = 
      Function.createDelegate(this, this._onTextBoxBlur);
    this._textBoxKeyPressHandler = 
      Function.createDelegate(this, this._onTextBoxKeyPress);
    this._textBoxKeyDownHandler = 
      Function.createDelegate(this, this._onTextBoxKeyDown);
    this._buttonClickHandler = 
      Function.createDelegate(this, this._onButtonClick);
    this._buttonBlurHandler = 
      Function.createDelegate(this, this._onButtonBlur);
    this._buttonKeyDownHandler = 
      Function.createDelegate(this, this._onButtonKeyDown);

    this._buttonKeyPressHandler = 
      Function.createDelegate(this, this._onButtonKeyPress);
    this._documentClickHandler = 
      Function.createDelegate(this, this._onDocumentClick);
    this._documentMouseWheelHandler = 
      Function.createDelegate(this, this._onDocumentMouseWheel);
    this._popupShowingHandler = 
      Function.createDelegate(this, this._popupShowing);
    this._popupShownHandler = 
      Function.createDelegate(this, this._popupShown);
    this._popupHidingHandler = 
      Function.createDelegate(this, this._popupHiding);
  };

  Sys.Extended.UI.ComboBox.prototype.clearDelegates = function () {
    // ①
    // IME モードが ON のときは onkeypress イベントが発生
    // しない為、代わりに onkeyup イベントを利用。
    this._textBoxKeyUpHandler = null;

    // ここから下はオリジナルのコードそのまま。
    this._listMouseOverHandler = null;
    this._listMouseOutHandler = null;
    this._listMouseDownHandler = null;
    this._listClickHandler = null;
    this._listDragHandler = null;
    this._listSelectStartHandler = null;
    this._listMouseWheelHandler = null;
    this._textBoxClickHandler = null;
    this._textBoxFocusHandler = null;
    this._textBoxBlurHandler = null;
    this._textBoxKeyPressHandler = null;
    this._textBoxKeyDownHandler = null;
    this._buttonClickHandler = null;
    this._buttonBlurHandler = null;
    this._buttonKeyDownHandler = null;
    this._buttonKeyPressHandler = null;
    this._documentClickHandler = null;
    this._documentMouseWheelHandler = null;
    this._popupShowingHandler = null;
    this._popupShownHandler = null;
    this._popupHidingHandler = null;
  };

  Sys.Extended.UI.ComboBox.prototype.addHandlers = function () {

    var optionListControl = this.get_optionListControl();

    $addHandlers(
      optionListControl,
      {
        'mouseover': this._listMouseOverHandler,
        'mouseout': this._listMouseOutHandler,
        'mousedown': this._listMouseDownHandler,
        'click': this._listClickHandler,
        'drag': this._listDragHandler,
        'selectstart': this._listSelectStartHandler
      },
      this);

    $addHandlers(
      this.get_textBoxControl(),
      {
        // ①
        // IME モードが ON のときは onkeypress イベントが発生
        // しない為、代わりに onkeyup イベントを利用。
        // これ以外はオリジナルのコードそのまま。
        "keyup": this._textBoxKeyUpHandler,

        "click": this._textBoxClickHandler,
        "focus": this._textBoxFocusHandler,
        "blur": this._textBoxBlurHandler,
        "keypress": this._textBoxKeyPressHandler
      }, 
      this);

    if (Sys.Browser.agent == Sys.Browser.InternetExplorer ||
      Sys.Browser.agent === Sys.Browser.Safari ||
      Sys.Browser.agent === Sys.Browser.WebKit) {
      $addHandler(
        this.get_textBoxControl(),
        "keydown", 
        this._textBoxKeyDownHandler);
    }

    $addHandlers(
      this.get_buttonControl(),
      {
        'click': this._buttonClickHandler,
        'blur': this._buttonBlurHandler,
        'keydown': this._buttonKeyDownHandler,
        'keypress': this._buttonKeyPressHandler
      }, 
      this);

    $addHandler(
      document,
      'click', 
      this._documentClickHandler);

    if (typeof (optionListControl.onmousewheel) === 'undefined') {
      $addHandler(
        optionListControl,
        'DOMMouseScroll', 
        this._listMouseWheelHandler);
      $addHandler(
        document,
        'DOMMouseScroll', 
        this._documentMouseWheelHandler);
    }
    else {
      $addHandler(
        optionListControl,
        'mousewheel', 
        this._listMouseWheelHandler);
      $addHandler(
        document,
        'mousewheel', 
        this._documentMouseWheelHandler);
    }
  };

  var keycodeOnTextBoxKeyDown = "";

  Sys.Extended.UI.ComboBox.prototype._onTextBoxKeyDown = function (e) {

    // ②
    // _onTextBoxKeyDown 時に Keycode を取得
    // (IME モード ON の時は 229)
    keycodeOnTextBoxKeyDown = this._getKeyboardCode(e);

    // ここから下はオリジナルのコードそのまま。
    var enterResult = this._handleEnterKey(e);
    if (enterResult != null) {
      return enterResult;
    }

    this._handleArrowKey(e);

    var erasureKeyResult = this._handleErasureKeys(e);
    if (erasureKeyResult != null) {
      return erasureKeyResult;
    }

    return true;

  };

  // ①
  // IME モードが ON のときは onkeypress イベントが発生
  // しない為、代わりに onkeyup イベントを利用。
  Sys.Extended.UI.ComboBox.prototype._onTextBoxKeyUp = function (e) {

    // ③
    // onkeyup時に②で取得した Keycode が 229 で
    // かつ KeyCode が 13(ENTER)のときに(文字変
    // 換を確定)、_onTextBoxKeyPress を Call する。
    if (keycodeOnTextBoxKeyDown == 229 && e.keyCode == 13) {
      this._onTextBoxKeyPress(e);
    }
  };

  //]]>  
  </script>
</body>
</html>

Tags: , ,

AJAX

jQuery AJAX と Web サービス

by WebSurfer 2011年6月4日 21:05

注意:
今頃になって何ですが、 jQuery Ajax の仕様が変わって、バージョン 1.5 以降では dataFilter に指定したメソッドで JSON 文字列を JavaScript オブジェクトにパースできなくなりました。詳しい説明はこの記事の下の方にある「2013/8/9 追記」を見てください。

先の記事 ASP.NET AJAX と Web サービス では、AJAX 対応 ASP.NET Web ページから Web サービスにアクセスする例を書きましたが、この記事では jQuery を使って AJAX 通信を行う例を書きます。

ASP.NET .aspx ファイルでない Web ページでは、ScriptManager コントロールを使用できません。従って、先の記事に書きましたプロキシクラスは利用できません。

この記事では、静的な html ページから、jQuery を使った AJAX 通信を行い、Web サービスからデータを JSON 形式で取得する方法について説明します。

基本的には、先の記事で紹介したページ Handling JSON Arrays ... に記載されていたサンプルとほぼ同じです。

ただし、d パラメータ有り(.NET 3.5 以降)/無し(.NET 2.0 以前)の両方に対応するためのコードと、ブラウザに実装されているネイティブの JSON パーサーを使えるように手を加えました。

それらの機能を追加しても、先の記事で書いた ScriptManager を利用してプロキシを自動生成する場合と同等以上に簡単と思いますがいかがでしょう。マイクロソフトが jQuery を Visual Studio 2010 に統合した理由が分かるような気がします。

以下のコードは、実際に動かして試せるよう 実験室 にアップしましたので、興味のある方は試してみてください。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>jQUery AJAX and Web Service</title>
  <script src="Scripts/jquery-1.4.1.js" type="text/javascript">
  </script>
  <script type="text/javascript">
  //<![CDATA[
  function getCars() {
    $.ajax({
      type: "POST",
      url: "097_jQueryAjaxAndWebService.asmx/GetCarsByDoors",
      data: '{"doors":' + $("#ddlDoors").val() + '}',

      // JSON を期待する場合必須(下の追記参照)
      contentType: "application/json; charset=utf-8",

      // success ハンドラで d の有無を判定しないで済むように
      // dataFilter を使う。
      // その際、dataType: "json" は不用。
      // dataType は応答データをどのように deserialize する
      // かを jQuery に指示するもの。"json" が指定されてい
      // ると jQuery は内部的に eval メソッドを使って 
      // deserialize する。dataType: "json" が指定してある
      // と  dataFilter と二重に deserialize されてしまう。

      dataFilter: function (data) {
        // ブラウザの native JSON パーサがあればそれを
        // 使う(セキュリティ対策)  
        var msg = "";            
        if (typeof (JSON) !== 'undefined' &&
          typeof (JSON.parse) === 'function') {
          msg = JSON.parse(data);
        } else {
          msg = eval('(' + data + ')');
        }

        // .NET 3.5 で追加された d パラメータの処置。
        if (msg.hasOwnProperty('d')) {
          return msg.d;
        } else {
          return msg;
        }
      },
      success: function (cars) {
        $('#output').empty();
        $.each(cars, function (index, car) {
          $('#output').append(
            '<p><strong>' + car.Make + ' ' +
            car.Model + '</strong><br /> Year: ' +
            car.Year + '<br />Doors: ' +
            car.Doors + '<br />Colour: ' +
            car.Colour + '<br />Price: £' +
            car.Price + '</p>');
          });
        },
        // failure を error に訂正。下の追記参照。
        // failure: function (msg) {
        //   $('#output').text(msg);
        // }
        error: function (jqXHR, textStatus, errorThrown) {
          $('#output').text('textStatus: ' + textStatus + 
            ', errorThrown: ' + errorThrown);
        }
    });
  }
  //]]>
  </script>
</head>
<body>
  <div>
    Number of doors: 
    <select name="ddlDoors" id="ddlDoors">
	  <option value="2">2</option>
	  <option value="3">3</option>
	  <option value="4">4</option>
	  <option value="5">5</option>
    </select>   
  </div>
  <input type="button" 
    id="Button1" 
    value="Get Cars" 
    onclick="getCars();" /> 
  <div id="output"></div>
</body>
</html>

------ 2011/9/3 追記 ------

上記コードの $.ajax のオプション設定の中の failure は、参考にしたサイト Handling JSON Arrays returned from ASP.NET Web Services with jQuery のコードをコピペしたものですが、どうも間違っているようです。

jQuery の本家のサイトの jQuery.ajax() の説明を見ると、オプション設定の中に failure というのはなく、error が正しいはずです。実際に試してみましたが、失敗しても制御は failure に設定した function には飛ばず、error に設定した function に飛びました。

という訳で、訂正しました。やはり、何も考えずにコピペして、しかも検証もしないというのはダメですね。(汗)

----- 2012/7/16 追記(2014/3/24 一部訂正) -----

jQuery Ajax で contentType: "application/json; charset=utf-8" を設定しないと、Web サービスのメソッドから返されるデータは xml 形式になってしまいます。結果、パースエラーになり error に設定したメソッドに制御が飛びます。

jQuery のサイトの API Documentation の説明によると、jQuery.ajax() の contentType のデフォルトは 'application/x-www-form-urlencoded; charset=UTF-8' とのことですが、何も設定しないと要求ヘッダには Content-Type: そのものが設定されません(1.4.1 でしか試してませんが)。その場合でも、Web サービスのメソッドが返すデータ合は Xml 形式になります。

なお、要求ヘッダが Content-Type: application/x-www-form-urlencoded もしくは何も指定しない場合は ScriptMethodAttribute クラスResponseFormat プロパティ を Json に指定しても無視されます。

jQuery.post() や jQuery.get() では contentType が指定できないので注意してください。

----- 2012/12/23 追記 -----

POST する JSON 文字列が {doors:5} となっており、正しく {"doors":5} となってなかったので以下のように訂正しました。

data: "{doors: " + $('#ddlDoors').val() + "}",
 ↓↓↓
data: '{"doors":' + $("#ddlDoors").val() + '}',

以前の形式でも問題なく Web サービスのメソッドは呼び出され、応答は正しく帰ってきましたが、気分の問題ということで直しました。(笑)

------ 2013/8/9 追記 ------

jQuery バージョン 1.4(多分それ以前のバージョンも)では、dataFilter のメソッドの戻り値が string 型以外のときは、そのまま結果を success のメソッドの引数に渡します(string 型の時に限り戻り値をパースしてから success のメソッドに渡します)。

なので、上のサンプルコードのように、dataFilter のメソッドで JSON をパースして JavaScript オブジェクトに変換すると、string 型ではないので、JavaScript オブジェクトがそのまま success のメソッドの引数 cars に渡され、問題なく結果が表示されます。

ところが、バージョン 1.5 以降では、上のサンプルコードのように dataFilter のメソッドで JSON 文字列を JavaScript オブジェクトにパースすると、 success のメソッドの引数 cars に渡されるのは null になり、結果スクリプトエラーとなってしまいます。その理由は以下の通りです。

  1. サーバーからの応答の Content-Type が application/json の場合、dataFilter の結果を parseJSON: function(data){...} で定義されているメソッドの引数 data に渡す。
  2. parseJSON: function(data){...} メソッドは、引数として受け取った data をパースして JavaScript オブジェクトを戻り地として返すようになっているが、typeof data !== "string" の場合は、パース等何もしないで null を返す。
  3. dataFilter のメソッドで JSON 文字列をパースして JavaScript オブジェクトに変換してしまうと、data は string 型ではないので null が返される。
  4. 結果、success のメソッドの引数 cars に null が渡され、スクリプトエラーとなる。

従って、バージョン 1.5 以降では、パースは jQuery に任せて(dataFilter のメソッドは記述しないで)、d パラメータの処置のみを success のメソッドで行うことになります。上記のサンプルコードの function getCars() は以下のように修正してください。

function getCars() {
  $.ajax({
    type: "POST",

    url: "097_jQueryAjaxAndWebService.asmx/GetCarsByDoors",

    data: '{"doors":' + $("#ddlDoors").val() + '}',

    contentType: "application/json; charset=utf-8",

    success: function (cars) {
      if (cars.hasOwnProperty('d')) {
          cars = cars.d;
      }
      $('#output').empty();
      $.each(cars, function (index, car) {
        $('#output').append(
          '<p><strong>' + car.Make + ' ' +
          car.Model + '</strong><br /> Year: ' +
          car.Year + '<br />Doors: ' +
          car.Doors + '<br />Colour: ' +
          car.Colour + '<br />Price: £' +
          car.Price + '</p>');
        });
      },

      error: function (jqXHR, textStatus, errorThrown) {
        $('#output').text('textStatus: ' + textStatus + 
          ', errorThrown: ' + errorThrown);
      }
  });
}

Tags: ,

AJAX

ASP.NET AJAX と Web サービス

by WebSurfer 2011年6月4日 17:52

ASP.NET AJAX Web ページから Web サービスにアクセス

AJAX 対応 ASP.NET Web ページからクライアントスクリプトを使って Web サービスにアクセスする方法を、MSDN ライブラリの ASP.NET AJAX での Web サービスの使用 を参考に実装してみました。

参考にした MSDN ライブラリのページに記載されていたサンプルコードではあまり面白くないし、jQuery AJAX を利用してアクセスする方法と比較してみたかったので、ググって探したページ Handling JSON Arrays returned from ASP.NET ... に記載されていたサンプルを使って、呼び出し側を ASP.NET AJAX Web ページに変更して実装してみました。

非同期要求の要となるのが、プロキシクラスに定義されている JavaScript のメソッド類です。クライアントとサーバーの通信におけるプロキシクラスの位置づけなどの説明は上に紹介した MSDN ライブラリを参照してください。

ASP.NET Web ページに ScriptManager コントロールを追加し、その Services 要素に asp:ServiceReference 要素を追加し、Path 属性に Web サービスの URL 設定することにより、自動的にプロキシクラスが生成されます。

自動生成されたプロキシクラスがページの読み込み時にブラウザにダウンロードされるように、初期画面の html ソースに以下のような外部スクリプトファイルへの参照が含まれます。(下記で js は web.config の設定がデバッグモードになっていると jsdebug になります)

<script src="WebService.asmx/js" type="text/javascript">
</script>

このプロキシクラスに定義されたメソッドを使用して Web サービスメソッドに対して JavaScript の非同期要求を行います。

ブラウザとサーバー間で交換されるデータのシリアル化形式としては JSON が使用されます。Web サービスが JSON 形式のデータを返すようにするには、Web サービスクラスに、System.Web.Script.Services 名前空間の ScriptService 属性を付与します。

(1) Web サービス、(2) aspx ページでプロキシクラスを操作するための JavaScript、(3) aspx ページのコードを、その順に以下にアップしておきます。Web サービスのコードは、上に紹介したページのサンプルと同じです。(リンク切れになると困るのでここに貼っておきます)

jQuery AJAX を利用した場合と比較してのメリットは、.NET 3.5 で追加された d パラメータがプロキシで適切に処置されるところと、コードが若干少なくなる点でしょうか。

以下のコードは、実際に動かして試せるよう 実験室 にアップしましたので、興味のある方は試してみてください。

(1) Web サービス (097_jQueryAjaxAndWebService.asmx)

クライアントスクリプトから呼び出すことができるように、クラスに ScriptService 属性を追加しているところがポイントです。

<%@ WebService Language="C#" Class="CarService" %>

using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Web.Script.Services;
using System.Collections.Generic;
using System.Linq;

public class Car
{
    public string Make;
    public string Model;
    public int Year;
    public int Doors;
    public string Colour;
    public float Price;
}

/// <summary>
/// Summary description for CarService
/// </summary>

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class CarService : WebService
{
    List<Car> Cars = new List<Car>{
        new Car{Make="Audi",Model="A4",Year=1995,
            Doors=5,Colour="Red",Price=2995f},
        new Car{Make="Ford",Model="Focus",Year=2002,
            Doors=5,Colour="Black",Price=3250f},
        new Car{Make="BMW",Model="5 Series",Year=2006,
            Doors=4,Colour="Grey",Price=24950f},
        new Car{Make="Renault",Model="Laguna",Year=2000,
            Doors=5,Colour="Red",Price=3995f},
        new Car{Make="Toyota",Model="Previa",Year=1998,
            Doors=5,Colour="Green",Price=2695f},
        new Car{Make="Mini",Model="Cooper",Year=2005,
            Doors=2,Colour="Grey",Price=9850f},
        new Car{Make="Mazda",Model="MX 5",Year=2003,
            Doors=2,Colour="Silver",Price=6995f},
        new Car{Make="Ford",Model="Fiesta",Year=2004,
            Doors=3,Colour="Red",Price=3759f},
        new Car{Make="Honda",Model="Accord",Year=1997,
            Doors=4,Colour="Silver",Price=1995f}
    };
    
    [WebMethod]
    public List<Car> GetAllCars()
    {
        return Cars;
    }

    [WebMethod]
    public List<Car> GetCarsByDoors(int doors)
    {
        var query = from c in Cars
                    where c.Doors == doors
                    select c;

        return query.ToList();
    }
}

(2) JavaScript (097_ASPNETAjaxAndWebService.js)

Succeeded コールバック関数の引数に渡されるのは JSON の文字列ではなく、パース済みの JavaScript オブジェクトとなる点に注意してください。セキュリティ対策のため .NET 3.5 で JSON 文字列に追加された d パラメータはプロキシクラスで適切に除去されるようです。

var serviceProxy;

// プロキシの初期化とコールバック関数の設定
function pageLoad() {
    serviceProxy = new CarService();
    serviceProxy.set_defaultSucceededCallback(Succeeded);
    serviceProxy.set_defaultFailedCallback(Failed);
}

// ボタンクリックで呼び出されるサービスメソッド  
function getCars(doors) {
    serviceProxy.GetCarsByDoors(doors);
}

// AJAX 通信が成功したときに呼び出され、戻ってきたデータ
// を処置するコールバック関数。
// 引数 cars は JSON 文字列ではなく、パース済みのオブジェ
// クト。.NET 3.5 で追加された d パラメータはプロキシで
// 適切に処置されるらしい。
function Succeeded(cars) {
    $('#output').empty();
    $.each(cars, function (index, car) {
        $('#output').append(
            '<p><strong>' + car.Make + ' ' +
            car.Model + '</strong><br /> Year: ' +
            car.Year + '<br />Doors: ' +
            car.Doors + '<br />Colour: ' +
            car.Colour + '<br />Price: £' +
            car.Price + '</p>');
    });
}

// 通信に失敗したとき呼び出されるコールバック関数。 
function Failed(error, userContext, methodName) {
    if (error !== null) {
        var msg = "An error occurred: " +
            error.get_message();
        $('#output').text(msg);
    }
}

if (typeof (Sys) !== "undefined") {
    Sys.Application.notifyScriptLoaded();
}

(3) apsx ページ (097_ASPNETAjaxAndWebService.aspx)

プロキシクラスを自動生成するために、ScriptManager コントロールを追加し、その Services 要素に asp:ServiceReference 要素を追加し、Path 属性に Web サービスの URL 設定するところがポイントです。

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

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>ASP.NET AJAX and Web Service</title>
  <script src="Scripts/jquery-1.4.1.js" type="text/javascript">
  </script>
</head>
<body>
  <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="scriptManager">
      <Services>
        <asp:ServiceReference 
          Path="~/097_jQueryAjaxAndWebService.asmx" />
      </Services>
      <Scripts>
        <asp:ScriptReference 
          Path="~/097_ASPNETAjaxAndWebService.js" />
      </Scripts>
    </asp:ScriptManager>
    <div>
      Number of doors: 
      <asp:DropDownList ID="ddlDoors" runat="server">
        <asp:ListItem>2</asp:ListItem> 
        <asp:ListItem>3</asp:ListItem>
        <asp:ListItem>4</asp:ListItem>
        <asp:ListItem>5</asp:ListItem>
      </asp:DropDownList>   
    </div>
    <input 
      type="button" 
      id="Button1" 
      value="Get Cars" 
      onclick="getCars($('#<%= ddlDoors.ClientID %>').val());" /> 
    <div id="output"></div>
  </form>
</body>
</html>

----- 2012/7/16 追記(2014/3/24 一部訂正) -----

Web サービスのメソッドに ScriptService 属性を追加することにより JSON 形式のデータが返されるというわけではありません。MSDN ライブラリの ScriptServiceAttribute クラス にもそのようなことは書いてなくて、"Web サービス メソッドを ECMAScript (JavaScript) から起動する" ためだけのようです。

Web サービスのメソッドが返すのは、Json か Xml 形式のいずれかになりますが、それを決めるのは要求ヘッダの Content-Type の設定のようです。

MSDN ライブラリには Content-Type で決まるというような記述は見つけられませんでしたが、自分が試した限りでは以下の通りでした。

  1. Content-Type: application/x-www-form-urlencoded もしくは指定しない場合は Xml
  2. Content-Type: application/json の場合は Json

この記事のように、プロキシクラスを利用する場合は、自動的に要求ヘッダに Content-Type: application/json と指定され、Json 形式でデータが帰ってきます。

jQuery AJAX と Web サービス のページで紹介した jQuery Ajax を使う場合は、contentType に application/json を指定しないと、Web サービスのメソッドが返すデータ合は Xml 形式になってしまうので注意が必要です。

(注) jQuery のサイトの API Documentation の説明によると、jQuery.ajax() の contentType のデフォルトは 'application/x-www-form-urlencoded; charset=UTF-8' とのことですが、何も設定しないと要求ヘッダには Content-Type: そのものが設定されません(1.4.1 でしか試してませんが)。その場合でも、Web サービスのメソッドが返すデータ合は Xml 形式になります。

なお、要求ヘッダが Content-Type: application/x-www-form-urlencoded もしくは何も指定しない場合は ScriptMethodAttribute クラスResponseFormat プロパティ を Json に指定しても無視されます。

ただし、要求ヘッダが Content-Type: application/json の場合は ResponseFormat プロパティの設定は意味があって、Xml に設定すると Web サービスのメソッドが返すデータは Xml 形式になります。

Tags: ,

AJAX

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年3月  >>
252627282912
3456789
10111213141516
17181920212223
24252627282930
31123456

View posts in large calendar