2006年10月18日 (水)

Atlasを使った、じゃらんWebサービスサンプル

昨日、じゃらんWebサービスのエリア情報をTreeViewにバインドするところまで作りましたので、 ついでにエリア内の宿情報を取得して表示するところまで作ってみました。
サンプルページ
サンプルコード

せっかくですので、このサンプルではAtlasのUpdatePanelを使ってみました。
JavaScriptは1行も書いてなく、AjaxはUpdatePanelにおまかせです。
開発手順はAtlasを使わない場合と同じですので、開発時の違和感もないと思います。
また、サーバー側のコードも3行のみで、実質、コントロールを画面に配置して若干表示を整えた程度です。

宿情報は、XmlDataSourceでデータを取得しDataListにバインドしていて、TreeViewの場合とほとんど同じです。

| | コメント (0) | トラックバック (0)

2006年10月17日 (火)

ASP.NETのTreeViewにXMLをバインドする その2

昨日、TreeViewにXMLデータをバインドするサンプルを書きましたが、XMLが名前空間で修飾されている場合、 あれだけではうまくバインド出来ません。

XmlDataSourceのXPathを設定してバインドする部分を指定していますので、
/ns:Data/ns:Node
のように指定したいところですが、XmlDataSourceには名前空間を指定するプロパティなどが用意されていないようで、 こういった指定が出来そうもありません。
仕方ないので他の方法を探したのですが、Transformプロパティに以下のようなXSLを設定して名前空間を取り除くことで、 とりあえずバインドする事が出来るようです。
(もっと良い方法があれば、コメントください)

<asp:XmlDataSource ID="dataSource" DataFile="~/Data.xml"
    XPath="/Data/Node" runat="server">
    <Transform>
        <?xml version="1.0" encoding="UTF-8"?>
        <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
           <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
           <xsl:template match="*">
              <xsl:element name="{local-name()}">
                  <xsl:for-each select="@*">
                     <xsl:attribute name="{local-name()}">
                        <xsl:value-of select="."/>
                     </xsl:attribute>
                  </xsl:for-each>
              <xsl:apply-templates/>
              </xsl:element>
           </xsl:template>
        </xsl:stylesheet>
    </Transform>
</asp:XmlDataSource>


実際の動作例として、じゃらんWebサービスエリアデータをバインドするサンプルを作ってみました。
じゃらんWebサービスでは、温泉や宿の情報を地域別に取得できるのですが、返されるXMLは、
<?xml version="1.0" encoding="UTF-8" ?>
<Results xmlns="jws">
    .....
</Results>
のように、名前空間が付加されています。
このデータを、上記の方法でTreeViewにバインドするためのaspxファイルは以下のようになっています。
<%@ 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>TreeView Example 2</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>じゃらん Webサービス エリアデータ</div>
    <div style="border:solid 1px black;width:300px;">
        <asp:TreeView ID="areaTree" ExpandDepth="0"
            DataSourceID="dataSource" runat="server">
            <DataBindings>
                <asp:TreeNodeBinding DataMember="Region"
                    TextField="name" ValueField="cd" SelectAction="Expand"
                    PopulateOnDemand="True" />
                <asp:TreeNodeBinding DataMember="Prefecture"
                    TextField="name" ValueField="cd" SelectAction="Expand"
                    PopulateOnDemand="True" />
                <asp:TreeNodeBinding DataMember="LargeArea"
                    TextField="name" ValueField="cd" SelectAction="Expand"
                    PopulateOnDemand="True" />
                <asp:TreeNodeBinding DataMember="SmallArea"
                    TextField="name" ValueField="cd" />
            </DataBindings>
        </asp:TreeView>
    </div>
    
    <asp:XmlDataSource ID="dataSource" 
        DataFile="http://jws.jalan.net/APICommon/AreaSearch/V1/?key=APIキー"
        XPath="/Results/Area/Region" runat="server">
        <Transform>
            <?xml version="1.0" encoding="UTF-8"?>
            <xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
               <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
               <xsl:template match="*">
                  <xsl:element name="{local-name()}">
                      <xsl:for-each select="@*">
                         <xsl:attribute name="{local-name()}">
                            <xsl:value-of select="."/>
                         </xsl:attribute>
                      </xsl:for-each>
                  <xsl:apply-templates/>
                  </xsl:element>
               </xsl:template>
            </xsl:stylesheet>
        </Transform>
    </asp:XmlDataSource>
    </form>
</body>
</html>
データ量が結構多いので、TreeViewのExpandDepthを0にして初期表示では要素を展開しないようにし、 asp:TreeNodeBindingのPopulateOnDemandをTrueにして動的にバインドさせています。

| | コメント (0) | トラックバック (0)

2006年10月16日 (月)

ASP.NETのTreeViewにXMLをバインドする

ASP.NETのTreeViewにXMLをバインドする サーバーコントロールにXMLデータをバインドする際は、XmlDataSourceコントロールを併用します。

例えば、次のようなXMLデータがあるとします。
<?xml version="1.0" encoding="utf-8" ?>
<data>
  <WebSites>
    <WebSite name="Microsoft" url="http://www.microsoft.com/japan/">
      <Contents title="MSDN" url="http://www.microsoft.com/japan/msdn/" />
      <Contents title="Visual Studio"
        url="http://www.microsoft.com/japan/msdn/vstudio/" />
    </WebSite>
    <WebSite name="Google" url="http://www.microsoft.com/japan/">
      <Contents title="Google Map" url="http://maps.google.co.jp/" />
      <Contents title="Google Calendar" url="http://www.google.com/calendar/" />
    </WebSite>
    <WebSite name="Yahoo! Japan" url="http://www.yahoo.co.jp/">
      <Contents title="Yahoo! Developer Network" url="http://developer.yahoo.co.jp/" />
    </WebSite>
  </WebSites>
</data>
これをTreeViewにバインドする場合、ASPXファイルは以下のような記述となります。
<%@ 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>TreeView Example</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:TreeView ID="siteTree" DataSourceID="siteDataSource" runat="server">
            <DataBindings>
                <asp:TreeNodeBinding DataMember="WebSite"
                    TextField="name" ValueField="url" />
                <asp:TreeNodeBinding DataMember="Contents"
                    TextField="title" ValueField="url" />
            </DataBindings>
        </asp:TreeView>
    </div>
    
    <asp:XmlDataSource ID="siteDataSource" 
        DataFile="~/Data.xml"
        XPath="/data/WebSites/WebSite"
        runat="server"></asp:XmlDataSource>
    </form>
</body>
</html>
まずXmlDataSourceの方では、DataFileプロパティにデータへのパスを設定します。
また、XPathプロパティには、データ内のバインドする部分を取得するXPathを設定します。

TreeViewの方では、DataSourceIDプロパティにXmlDataSourceのIDを設定し、DataBindingsプロパティに 画面に表示する部分とクリック時にサーバーに送信される値の部分を指定します。
asp:TreeNodeBindingの指定方法は、DataMemberにはタグ名、TextFieldにはツリーの項目として表示される文字を持つ属性、 ValueFieldにはサーバーに送信する値を持つ属性といった具合です。

今回の場合では、
  • WebSiteというタグの場合は、name属性を表示しurl属性をサーバーへ送信する値に設定
  • Contentsというタグの場合は、name属性を表示しurl属性をサーバーへ送信する値に設定
という感じになっています。


以上で、TreeViewにXMLデータをバインドできるようになります。
ただ、XMLが名前空間で修飾されている場合は、うまくいきません。
それについては、また後日メモしたいと思います。

| | コメント (0) | トラックバック (0)

2006年10月 4日 (水)

ASP.NETとAtlasでAjax

現在Microsoftが開発中のASP.NET用AjaxフレームワークであるAtlasを試してみましょう。
どうやら年内にリリースされるようですので、まだチェックしていない方は是非試してみて下さい。
ちなみに、Atlasはコードネームで、少し前に正式名称が決まったみたいです。
JavaScriptライブラリは「Microsoft AJAX Library」、サーバーサイドのクラスライブラリ・サーバーコントロール群は「ASP.NET 2.0 AJAX Extensions」、Atlas Control Toolkitは「ASP.NET AJAX Control Toolkit」なんだそうです。


さて、まずはAtlasのサイトからインストーラーをダウンロードしましょう。
現時点でのバージョンはJuly CTPとなっています。

インストールは、インストーラーに指示にしたがって次へ進むだけでOKです。
Visual Studio 2005(Visual Web Developer ExpressもOK)のプロジェクトテンプレートも、一緒にインストールできます。


インストールが完了したら、Visual Studioを起動してWebプロジェクトを作成します。
プロジェクトテンプレートも一緒にセットアップすると、下図のようなテンプレートが追加されていますので、これを選択して作成します。
(下の画像はVisual Web Developer Expressのものです)


今回は簡単な例としてサーバーで実装したメソッドをJavaScriptから呼び出すサンプルを作成してみます。
まず、既に作成されているDefault.aspx.csに以下の太字のコードを追加します。
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Web.Services;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

	
    [WebMethod]
    public string TestMethod()
    {
        return "Hello Atlas";
    }
}
WebMethodという属性はWebサービスで公開するメソッドにつける属性ですが、Atlasではaspxでこの属性の付いたメソッドをクライアントの JavaScriptから呼び出せるようです。
(通常のasmxで公開されているWebサービスも呼び出せます)


次に、Default.aspの<head runat="server">~</head>間に以下のJavaScriptを追加します。
<script type="text/javascript">
window.onload = function()
{
    PageMethods.TestMethod(RequestComplete);
}

function RequestComplete(data)
{
    alert(data);
}
</script>
PageMethods.TestMethod(RequestComplete);
の部分がサーバー側のメソッド呼び出し部分です。
サーバー側で定義してWebMethod属性をつけたメソッド名と同じ名前でJavaScriptのメソッドがPageMethodsに自動的に作成されていますので、 違和感なく呼び出しが出来ます。
引数には呼び出しが完了した際のコールバック関数を指定しています。
コールバック関数には、サーバー側のメソッドの戻り値が引数として渡ってきます。

このプログラムを実行してみると、ページがロードされた時に "Hello Atlas"が表示されるのが確認できます。


通信部分のJavaScriptコードを一切書かずに、簡単にサーバーのメソッドが呼び出せますね。
通信やその他の処理に関するスクリプトは、既に記述されている
<atlas:ScriptManager ID="ScriptManager1" runat="server" />
の部分によってクライアントにJavaScriptが出力されています。
この中には、Ajaxに関するものだけでなく他にも便利な機能が提供されていますので、それらも含めて色々とAtlasに関するサンプルなどを書いてみようと思います。

| | コメント (0) | トラックバック (0)

2006年9月14日 (木)

ClientCallback機能を使ったTreeViewの展開

ASP.NET 2.0から追加されたTreeViewコントロールですが、ClientCallbackを使ってポストバックなしでデータを動的にバインドする機能があります。

まず、ページにTreeViewを配置し、TreeViewノードエディタでノードを追加します。
追加したノードのプロパティで、下図のようにExpandedをFalseに、PopulateOnDemandをTrueに設定します。
また、TreeView自体のPopulateNodesFromClientプロパティもTrueにします。




次に、TreeViewを配置したページに TreeNodePopulate イベントハンドラを作成します。
ツリーを展開すると、このイベントハンドラが呼び出されます。




ここでは、子ノードを10個追加するコードを記述してみます。
protected void TreeView1_TreeNodePopulate(object sender, TreeNodeEventArgs e)
{
    for (int i = 0; i < 10; i++)
    {
        TreeNode node = new TreeNode("Node" + i);
        e.Node.ChildNodes.Add(node);
    }
}
これで終わりです。
JavaScriptを1行も書くことなく、ClientCallbackを使ってポストバックなしにノードを追加出来るようになりました。




HTMLソースを見ると、JavaScriptはWebResource.axdというファイルをインクルードするscriptタグが出力されていることがわかると思います。
これにより、JavaScriptをまったく書かずに実現できたわけですね。
興味のある方は、どんなスクリプトが出力されているか覗いてみたら面白いと思います。


なお、このファイルは実際に存在しているものではなく、WebResource.axd?d=xxxxxx&t=xxxxxxにリクエストするとAssemblyResourceLoaderクラスにより アセンブリに埋め込まれているリソース(スクリプトや画像ファイルなど)をレスポンスとして返す仕組みになっていて、ファイルの識別はd=xxxxxxの部分で 行われています。
ツリーの横に表示されている + - の画像も、同じようにリソースから読み込まれています。

リソースの埋め込み、読み出しについては、また後日サンプルを書いてみたいと思います。

| | コメント (0) | トラックバック (0)

2006年9月11日 (月)

ASP.NETのClientCallback

ASP.NETの開発でAjaxを使う機会も増えているんじゃないかと思います。
今日は、ASP.NET2.0から追加された機能である、ClientCallbackの使い方などを書いてみたいと思います。

ClientCallbackとは、文字通りクライアントからサーバーのメソッドがコールバックされる仕組みで、通信にはXMLHttpRequestが使われています。
通信部分のスクリプトはASP.NETが勝手に出力してくれますので、私達は簡単なスクリプトを少し書くだけでOKといった感じですね。

もちろん、Ajaxでゴリゴリやる場合はそうもいきませんが、例えば郵便番号が入力されたら住所を自動入力するといった補助的なものなら、割と簡単に行うことができます。


ClientCallback使うには、いくつかの決まり事があります。
まず1つ目の決まりとして、そのページまたはコントロールが ICallbackEventHandler インターフェイスを実装していなければなりません。
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class Example : System.Web.UI.Page, ICallbackEventHandler
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    public string GetCallbackResult()
    {
        //ここに、クライアントに値を返すコードを書く
        return "Hello World";
    }

    public void RaiseCallbackEvent(string eventArgument)
    {
        //このメソッドが、クライアントから呼び出される
    }
}
このRaiseCallbackEventがクライアントからコールバックされ、GetCallbackResultメソッドにクライアントに値を返すコードを実装します。
しかし、これだけではまだコールバックイベントは発生しません。

2つ目の決まりとして、当然ではありますが、コールバックイベントを発生させるための関数とサーバーから値を受け取る 関数がそれぞれJavaScriptで定義されていなければなりません。
そこで、まず aspx に値を受け取る関数を記述します。
<script type="text/javascript">
function ReceiveData(data)
{
    alert(data);
}
</script>
名前は何でも構いませんが、ここではReceiveDataという名前の関数を作成しました。
引数は、サーバーから返される値になります。

次に、コールバックを発生させるJavaScriptの関数を作成する必要がありますが、これはサーバー側で作成して出力します。
とりあえず、Page_Loadメソッドに記述してみましょう。
protected void Page_Load(object sender, EventArgs e)
{
    string callBackRef =
        Page.ClientScript.GetCallbackEventReference(
            this, "arg", "ReceiveData", "context");

    string script = "function CallServerMethod(arg, context){" + callBackRef + ";}";
    
    Page.ClientScript.RegisterClientScriptBlock(
        this.GetType(), "CallbackScript", script, true);
}
まず、ClientScriptManagerのGetCallbackEventReferenceメソッドを使って、ASP.NETが自動生成するJavaScriptの関数を呼び出すコードを得ます。
GetCallbackEventReferenceメソッドの引数は、
第1引数:ICallbackEventHandlerを実装しているページまたはコントロール
第2引数:コールバックを実行するJavaScriptの関数の第1引数の名前
第3引数:結果を受け取るJavaScriptの関数名
第4引数:コールバックを実行するJavaScriptの関数の第2引数の名前
となっています。

恐らく第2引数と第4引数がわかり辛いと思います。
ココの説明はMSDNの記述も意味がよくわかりませんが、単に、その後に記述してある
string script = "function CallServerMethod(arg, context){" + callBackRef + ";}";
の呼び出し側JavaScript関数の引数である "arg"、"context"の変数名を渡せばOKです。
ちなみに、このJavaScript関数の第1引数はサーバーに渡される値、第2引数はサーバーのメソッドをコールバックする前に実行するJavaScriptの関数またはコードになります。

あとは、このスクリプトを RegisterClientScriptBlock メソッドでページに登録して完了です。
ただ、この RegisterClientScriptBlock メソッドはASP.NET 2.0から変更されています。
以前はPageクラスのRegisterClientScriptBlockメソッドを呼び出して登録していましたが、2.0からはこのメソッドは使わずにClientScriptManagerの RegisterClientScriptBlockメソッドを使って登録します。

最後に、以下のようボタンを追加して登録したJavaScriptの関数を実際に呼び出してみましょう。
<button onclick="CallServerMethod();">Call</button>
Hello Worldのalertが表示されると思います。

| | コメント (0) | トラックバック (0)

2006年8月 9日 (水)

ASP.NETでページの多言語化 Part2

先日はリソース名と登録名を指定して多言語化するサンプルを書きましたが、コントロールのプロパティに設定することを前提に リソースの名前を設定しておくことで暗黙的に行う方法を書いてみます。

リソースに関しては同じものを使用するのですが、"Name.Property"という規則にしたがって名前をつけておきます。
例えば、Label のTextプロパティに反映したい場合は、"Label1.Text" という名前(Label1の部分は何でも構いません)にしておくと、
<asp:Label Id="Label1" runat="server" meta:resourcekey="Label1" />
と記述して先日のサンプルと同様の結果を得ることが出来ます。
meta:resourcekey に指定する値は、"Name.Property" のNameの部分のみで、Propertyの部分に記述したプロパティに値が反映されるという仕組みです。
ですので、仮に開発の途中で言語によってCSSのクラス名を変えなくてはならなくなった場合でも、Label1.CssClassという名前でリソースを追加するだけで済む、 というメリットがあります。

リソースの値をコードで取得しなければならない場合もありますが、もちろんそれも可能で、ローカルリソースの場合はGetLocalResourceObjectメソッドを、 グローバルリソースの場合はGetGlobalResourceObjectメソッドを使用します。
string text1 = GetLocalResourceObject("Label1.Text").ToString();
string text2 = GetGlobalResourceObject("WebResources", "Label1.CssClass").ToString();

また、ブラウザの設定に関わらずユーザーが言語を選択できるようにしておく必要がありますが、そのような場合はPageクラスのInitializeCultureメソッドを オーバーライドして選択されたカルチャをページに反映させます。
protected override void InitializeCulture()
{
    this.Culture = "en-us";
    this.UICulture = "en-us";

    base.InitializeCulture();
}

最後に、先日のサンプルではPageディレクティブにUICulture="auto" Culture="auto" と書きましたが、全てのページこれを記述するのは面倒だという場合は、 Web.congifファイルに、
<configuration>
    ............
    <system.web>
        ................
        <globalization uiCulture="auto" culture="auto" />
    </system.web>
</configuration>
のように記述すればOKです。

| | コメント (0) | トラックバック (0)

2006年8月 6日 (日)

ASP.NETでページの多言語化

開発するアプリケーションが多言語に対応しなれければならないこと、結構あるんじゃないでしょうか。
ブラウザの言語設定を英語に設定して、例えば、IEならMSN、FirefoxならGoogle にアクセスしてみると、自動的に英語のページが表示されます。
(ブラウザの言語設定は、IEの場合はインターネットオプション→全般タブ→言語、Firefoxの場合はオプション→詳細の一般タブ→言語設定からそれぞれ出来ます。)

ASP.NET(.NET Framework)は、このような多言語に対応する仕組みを備えています。
日本語だけでよいなら、ページに直接文字を書き込んだりすることも多いと思いますが、多言語化する場合はリソースを使用します。
ASP.NET 2.0 からは、App_CodeやApp_Dataなどシステムが使用する特殊なフォルダがありますが、同じ種類のフォルダとして App_GlobalResources と App_LocalResources があります。
ここにリソースファイル(.resxファイル)を作成し aspx ファイルに細工をすることで、特別なコードを書かなくとも割りと簡単に対応できるようになります。

まず、テスト用にDefault.aspxというページを作成します。
(Visual StudioでASP.NETのプロジェクトを作成すると初期ページとしてDefault.aspxというページがあると思います。)
次に、App_LocalResourcesフォルダを作成します。
この中に、ページと同じ名前のリソースファイル、この場合はDefault.aspx.resxを作成します。

このファイルにページに表示する単語や文章を登録していくのですが、このファイルは既定のリソースファイルとなりクライアントの言語設定に 一致するリソースファイルが見つからない場合に使用されます。
既定で日本語を表示したい場合は日本語を登録します。

単語を登録する場合は名前と値のペアで登録します。
ここでは、"Language"という名前、"日本語"という値を登録してみます。
さらに、もう1つ、Default.aspx.en-us.resx というファイルをApp_LocalResourcesに作成し、"Language"という名前、"English"という値を 同様に登録しておきます。
ファイル名の en-us 英語(米国)を意味し、クライアントのブラウザが英語の設定ならこちらのリソースが使われます。

次にDefault.aspxを開いて、
<asp:Label ID="Label1" Text="<%$ Resources:Language %>" runat="server" />
と書きます。
リソースファイルの指定の値を取り出すには、<%$ Resources:ResourceFile,Name %>と記述します。
ResourceFileにはリソースファイル名を指定するのですが必須ではなく、指定しなかった場合は、今回のようにページと同じ名前のローカルリソースが ある場合はそれが使われます。
最後に、Pageディレクティブに UICulture="auto" を追加します。
auto にしておくと、クライアントのブラウザの設定に応じて自動的にリソースが選ばれます。
このページをブラウザの言語設定を日本語及び英語に設定してそれぞれ見てみると、適切に言語が選択されているのが確認できると思います。

ただ、言語が変わって影響を受けるのは単純に言葉だけではありません。
例えば日付を表示する場合、日本語では yyyy/mm/dd という書式が一般的ですが、英語の場合は mm/dd/yyyy という書式になります。
これらについても、自動的に処理するために Default.aspxの Page ディレクティブに Culture="auto" も追加して、
<asp:Label ID="Label2" runat="server"><%= DateTime.Now %></asp:Label>
と書き加えてみてください。
日本語と英語の両方で見比べてみると、ちゃんと処理されているのが確認できると思います。

このように、特別にコードを書くことなく対応することが出来ます。
ただ、ページとリソースがペアになっているので、リソースファイルが大量に出来てしまうことになります。
まあ、多言語となると単語の管理が大変なのはある程度は仕方ないとは思いますが。

今回は、Text=<%$ Resources:Language %> という記述方法で単語を設定しましたが、もう1つ指定方法があります。
これについては、また後日書きたいと思います。

また、ローカルリソースはそのページ内でしか使用できませんので、例えばエラーメッセージなど複数のページで 使いまわしたいものを登録するのには向いていません。
そういった場合のためにグローバルリソースというものがあります。
使い方はローカルリソースと同じですので、それについても一緒に次回簡単に書いてみたいと思います。

| | コメント (0) | トラックバック (0)