WebSurfer's Home

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

ACT の AutoComplete

by WebSurfer 2012年4月14日 15:48

AJAX Control Toolkit (ACT) の AutoComplete を使ってみました。

AJAX Control Toolkit の AutoComplete

実装は特に難しいことはなく、アットマーク・アイティの AutoComplete コントロールで Google サジェスト風なオートコンプリート機能を実装するには? を参考にすれば機能的には問題なく動くと思います。

ただし、スタイルを何も設定しないと、TextBox と候補リスト(AutoCompleteExtender が生成する ul, li 要素を使ったリスト)の間にスペースができてしまいます。理由は、ul 要素の Margin のトップが何故か 40px になっているからです。(IE9 の開発者ツールで「レイアウト」を見ると分かります)

スペースのできるのが気に入らない場合は、CompletionListCssClass プロパティに自分でスタイルを定義する必要があります。以下に、その例を書いておきます。

Web Form(.aspx)

CompletionListCssClass プロパティに設定するスタイルは、margin: 0px !important; とするだけではダメで、以下のように他の要素も定義しなおす必要があります。

下の例で、CompletionListItemCssClass と CompletionListHighlightedItemCssClass も設定してありますが、TextBox と候補リストの間のスペースの調整には必要ないです(オマケです)。

<%@ Page Language="C#" %>

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

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <style type="text/css">
    .autocomplete
    {
        margin: 0px !important;
        padding: 0px !important;
        text-align: left;
        color: WindowText;
        overflow: auto;
        border-color: ButtonShadow;
        border-width: 1px;
        border-style: solid;
        list-style-type: none;
        background-color: inherit;
        height: 200px; 
    }

    .autocomplete_item
    {
        color: WindowText;
        background-color: Window;
        padding: 1px;
    }

    .autocomplete_highlightedItem
    {
        color: Black;
        background-color: rgb(255, 255, 153);
        padding: 1px;
    }
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <ajaxToolkit:ToolkitScriptManager 
        ID="ToolkitScriptManager1" 
        runat="server">
    </ajaxToolkit:ToolkitScriptManager>
    <asp:TextBox ID="TextBox1" 
        runat="server" Columns="50">
    </asp:TextBox>
    <ajaxToolkit:AutoCompleteExtender 
        ID="TextBox1_AutoCompleteExtender" 
        runat="server"
        MinimumPrefixLength="2" 
        ServicePath="~/142-ACTAutoComplete.asmx" 
        TargetControlID="TextBox1"
        ServiceMethod="GetList"
        CompletionSetCount="20"
        CompletionListCssClass="autocomplete"
        CompletionListItemCssClass="autocomplete_item" 
        CompletionListHighlightedItemCssClass=
            "autocomplete_highlightedItem">
    </ajaxToolkit:AutoCompleteExtender>
    </form>
</body>
</html>

Web サービス(.asmx)

Web サービスは、クライアントスクリプトから呼び出して、JSON 形式のデータを返すように、クラスに ScriptService 属性を追加するところがポイントです。

以下の例で、データベースは Microsoft が無償で提供している Northwind サンプルデータベースの Products テーブルを使用しています。

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

using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Collections.Generic;
using System.Web.Configuration;
using System.Data;
using System.Data.SqlClient;

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class _142_ACTAutoComplete  : WebService 
{
  [WebMethod]
  public string[] GetList(string prefixText, int count)
  {
    List<string> list = new List<string>();
    string connString = 
      WebConfigurationManager.
        ConnectionStrings["Northwind"].ConnectionString;
    string query = 
      String.Format(
        "SELECT TOP {0} ProductName FROM Products " + 
        "WHERE ProductName LIKE @ProductName", count);
    SqlConnection connection = new SqlConnection(connString);
    SqlCommand command = new SqlCommand(query, connection);
    SqlParameter param = 
      new SqlParameter("@ProductName", SqlDbType.NVarChar, 40);
    param.Value = "%" + prefixText + "%";
    command.Parameters.Add(param);

    try
    {
      connection.Open();
      SqlDataReader reader = command.ExecuteReader();

      while (reader.Read())
      {
        list.Add(reader.GetString(0));
      }
    }
    finally
    {
      connection.Close();
    } 

    return list.ToArray();
  }    
}

Tags: , ,

AJAX

Razor 構文の Web ヘルパー/ページ

by WebSurfer 2012年3月20日 16:12

ASP.NET Web サイトプロジェクト(MVC でない)でも Razor 構文の Web ページ、Web ヘルパーが使用できるということを知ったので、備忘録として書いておきます。

Web サイトプロジェクトで[ヘルパー(Razor)] を追加

Web サイトプロジェクト(MVC でない)で、ソリューションエクスプローラーから[新しい項目の追加(W)]を選ぶと、上の画像のダイアログが表示され、Razor 構文の Web ページや Web ヘルパーが選択できるのが分かります。(ちなみに、Web アプリケーションプロジェクトでは Razor 関係のメニューは表示されません)

ここで、[ヘルパー(Razor)] をクリックすると、デフォルトで Helper.cshtml という名前のヘルパーが Add_Code フォルダに追加されます。その時同時に、web.config の appSettings 要素に以下の key が追加されます。(何故か、[Web ページ (Razor)]その他ではこの key は追加されません)

<appSettings>
  <add key="webpages:Enabled" value="true" />
</appSettings>

MSDN の ASP.NET のフォーラム LoginStatus コントロールで loginUrl が反映されない の原因を調べる過程で分かったことですが、< key="webpages:Enabled" value="true" /> が設定してあると、forms 要素の loginUrl に設定したリダイレクト先(ログインページ)が無視されて、/Account/Login になってしまうという問題があります。

これを回避するためには、以下のように loginUrl キーを appSettings に追加してやります。

<appSettings>
  <add key="webpages:Enabled" value="true" />
  <add key="loginUrl" value="~/login.aspx" />
</appSettings>

< key="webpages:Enabled" value="true" /> を削除すると、追加した Web ヘルパーのソースの @helper HelperName( ... ) の下に青の波線が出て、"拡張 '.cshtml' に対して登録されたビルド プロバイダーはありません。" というエラーメッセージが表示されます。

でも、自分が試した限りでは、最終的には < key="webpages:Enabled" value="true" /> を削除しても、Razor 構文の Web ページ、Web ヘルパーはビルドでき、問題なく動きました。また、ビルド後は青の波線とエラーメッセージは出なくなりました。

Razor 構文の Web ページ、Web ヘルパーの実行結果

左の画像が、自分が試した結果です。その時に使ったコードを以下にアップしておきます。

ただし、< key="webpages:Enabled" value="true" /> を削除しても、相変わらずログインのリダイレクト先が /Account/Login になってしまいます。

このあたり、訳が分かりませんが、Razor 構文の Web ページを追加すると、< key="webpages:Enabled" value="true" /> がデフォルトで設定されるのではないかと推測しています。(Web ヘルパーだけではデフォルトでは webpages:Enabled の value は ture にならない?)

webpages:Enabled は、MVC 3 Project Upgrade Tool の記事によると、Razor の MVC3 で、"to prevent .cshtml or .vbhtml files in the Views folder from being directly accessible from a web browser" のため、View フォルダの web.config に value="false" として設定するものだそうです。それ以上の説明は見つけられませんでした。

Razor Web ヘルパー (App_Code/QR.cshtml)

@helper GetHtml(string url, int pixelSize=150){
  // Google のチャートAPIを使って QR コード ( 二次元バーコード ) 
  // を生成する
  string sizeUrl = string.Format("{0}x{0}", pixelSize);
  string requestUrl = 
    string.Format(
      "http://chart.apis.google.com/chart?chs={0}&cht=qr&chl={1}",
      sizeUrl, 
      HttpUtility.UrlEncode(url));
    
  <img src="@requestUrl" alt="QR コード" />
}

Razor Web ページ (WebPage.cshtml)

<!DOCTYPE html>
<html>
  <head>
    <title></title>
  </head>
  <body>
    <p>@QR.GetHtml("http://surferonwww.info/")</p>
  </body>
</html>

Tags: ,

ASP.NET

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

About this blog

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

Calendar

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

View posts in large calendar