XMLDB周辺技術
HOME  >  XMLDB周辺技術  >  InfoPath徹底解析 3.フォーム設計(応用編)

InfoPath徹底解析 3.フォーム設計(応用編)

2004年7月1日 更新
※この記事はDigital Xpress 2004 Vol.19(2-3月号)に掲載されたものです。

ソリューション関連記事InfoPath徹底解析

Section1:フォーム設計(応用編)

このセクションでは、バックエンドシステムとしてWebサービスを利用したシステムを構築します。 このサンプル用のWebサービスとして、マイクロソフト社のWebサイトからダウンロードできる受注・出荷業務Webサービスを利用します。このWebサービスはVisual Studio Tools for Office向けのサンプルとして公開されているものですが、InfoPathから直接利用することもできます。

このWebサービスはローカルコンピュータ上で稼動するものなので、InfoPath上での開発を開始する前に、ファイルのインストールやデータベースのセットアップ、Webサービスのセットアップなどの作業が必要になります。必要なファイルのダウンロード、動作環境の確認も含め、このWebサービスの詳細についてはマイクロソフト社のWebサイトにある「スマートクライアント デモキット1:Visual Studio Tools for Officeの実現するスマートクライアント
http://www.microsoft.com/japan/msdn/vstudio/office/productinfo/IntelliNet/)」をご参照ください。

このセクションでは、いよいよInfoPathとWebサービスを組み合わせたシステムを構築してみましょう。

今回作成するのは、オフィス用品の販売を行っている会社で受注した情報を登録するためのフロントエンドです。オペレータが受注した情報を登録し、サーバーに送信すると、Webサービスがその情報を受け取り、在庫情報の更新などの処理を行います。さらに、スクリプトを使った動的なデータ更新や既定値の設定などの方法も確認していきます。

今回作成するフォームの主な機能を図14に示します。

今回作成する受注登録フォームの主な機能イメージ
図14:今回作成する受注登録フォームの主な機能

WSDLを使ったフォーム作成

今回のように送受信するためのWebサービスがすでに存在している場合、そのWebサービスのWSDLを起点としてInfoPathのフォーム(データソース)を作成できます。

サンプルWebサービスのインストールが完了していれば、WSDLは、http://localhost/IntelliNetWS/EntryPoint.asmx?WSDLからアクセスできます(インストール先やIISの設定などによって異なる場合があります。そのときは適宜変更してください)。ブラウザからWSDLにアクセスできるURIを確認し、次のステップに進みます。

InfoPathを起動し、新しいフォームのデザインを開始します。[フォームのデザイン]作業ウィンドウで[データソースから新規作成]を選択し、[データソースセットアップウィザード]を使って設定していきます(図15を参照)。

Webサービスを利用したデータソースのセットアップの画面イメージ
図15:Webサービスを利用したデータソースのセットアップ

  • データソースセットアップウィザードで、データソースをWebサービスから作成するよう指定します。
  • Webサービスから作成する場合、InfoPathとWebサービスの間で、(1)InfoPathからWebサービスへの送信、(2)WebサービスからInfoPathへの受信、(3)送受信の両方、のいずれの操作を行うかを設定します。今回はサーバーへの送信のみを行いますので、[データの送信]を選択します。
  • 次に、利用するWebサービスのWSDLを指定します。先ほどhttp://localhost/IntelliNetWS/EntryPoint.asmx?WSDLにアクセスできることを確認したので、このURIを入力します。
  • InfoPathから送信されたデータを受け取るWebサービスのメソッドを指定します。今回のWebサービスでは「OrderConfirm」メソッドを選択します。このWebサービスに他にどんなメソッドがあるのかについては、付属ドキュメントを参照してください。
  • データソースが正常に作成された旨を示すメッセージが表示されます。[完了]を押してウィザードを完了します。

ウィザードによってWebサービスのWSDLが解析され、このフォームで使用されるデータソースが自動的に作成されます。ウィザードが完了した段階で、データソースが図16のように作成されているはずです。いかがですか?

WSDLから自動的に作成されたデータソースの画面イメージ
図16:WSDLから自動的に作成されたデータソース

データソースのdataFieldsの中にあるs0:Orderは、InfoPathから送信される注文情報をあらわしています。その中にはOrderIdやCustomerId、CustomerNameなど、注文情報のフィールドが含まれています。フィールド名の横に赤いアスタリスク(*)が表示されているフィールドは入力必須であることを示しています。

S0:OrdersにはItemsグループが含まれており、その中にはItemグループが含まれています。Itemグループのアイコンには小さな青い四角と白い三角のアイコンがついていることにお気づきでしょうか。これは、ItemグループがItemsグループ内で繰り返し出現可能であることを示しています。これにより、一度の注文で複数の商品を注文できるようになっていることがわかります。

WSDLの中には、各メソッドを呼び出すときに必要な引数の情報が含まれています。また、その引数がどういったデータ構造になっているのかという情報も含んでいます。InfoPathは選択したWebサービスのメソッド(今回であればOrderConfirmメソッド)の引数に関する情報をWSDLから読み込み、メソッドの呼び出しに必要なデータをデータソースとして自動的に定義してくれるのです。

さて、データソースの定義はあっけないほど簡単に終わりました。

しかし、このデータソースの中にあるCustomerIdとCustomerName、EmployeeIdとEmployeeNameのようなフィールドから、顧客や担当者に関するマスターデータが存在していることが推察できます。商品のProductIdとProductNameも同様ですし、おそらく標準価格についても商品マスターに含まれていることでしょう。

では、フォームの設計に入る前に、このWebサービスで利用するデータベースを確認し、どのようにデータが扱われているのかを確認しておきましょう。

データベースを確認する

今回のサンプルでは、データベースとしてSQL Server(またはMSDE)を使用しています。サンプルWebサービスのインストール時に「ASPNETKIT」という名前のデータベースが作成され、データベーススキーマやサンプルデータが作成されています。

このデータベースのテーブル設計についてはダウンロードしてきたファイルに含まれる「開発者ガイド」にも記載されていますが、このフォームで扱う発注処理で関係しそうなのは「Customers」、「Employees」、「Products」の3つのマスターテーブルです。

Customersテーブル

顧客マスターです。以下のようなテーブル構成になっており、サンプルデータとして2件の顧客が登録されています。

CustomerID int 4 NULL 不可
CustomerName varchar 64 NULL 可
ContactName varchar 32 NULL 可
Title varchar 32 NULL 可
Post varchar 32 NULL 可
Address varchar 128 NULL 可
Phone varchar 16 NULL 可
Fax varchar 16 NULL 可
EMail varchar 64 NULL 可
EmployeeID int 4 NULL 不可

Employeesテーブル

担当者(従業員)マスターです。以下のようなテーブル構成になっており、サンプルデータとして3人の担当者が登録されています。

EmployeeID int 4 NULL 不可
Name varchar 64 NULL 可
Title varchar 32 NULL 可
Post varchar 32 NULL 可
Phone varchar 16 NULL 可
FAX varchar 16 NULL 可
EMail varchar 64 NULL 可
EmployeeID int 4 NULL 不可
Name varchar 64 NULL 可
Title varchar 32 NULL 可

Productsテーブル

商品マスターです。以下のようなテーブル構成になっており、サンプルデータとして13の商品が登録されています。

ProductId int 4 NULL 不可
ProductName varchar 64 NULL 不可
Price money 8 NULL 不可
Stock int 4 NULL 不可

これらのテーブルはマスターテーブルであり、フォーム上でのデータ入力の際に利用されます(「機能2:選択肢をデータベースから取得し、対応するデータを自動的に入力する」で取り上げます)。一般的に、ほとんどのシステムではデータベースを利用していますし、大半のシステムではマスターテーブルが存在します。事前にマスターテーブルの構成を確認しておくとフォームの作成がスムーズに進むので、前もってチェックしておくことをお勧めします。

機能1:既定値を動的に設定する

InfoPathのフォーム設計において、既定値として固定の値を設定するのは非常に簡単です。データソースのフィールドを追加(または編集)するときに、[既定値]フィールドにその値を指定するだけです。

しかし、この方法では可変の値を設定できません。たとえば、データを入力した日や現在のユーザー名、ユニークなIDなど、そのときによって値が変わる場合には[既定値]フィールドを使って自動的に入力することができません。となると、作成日などの簡単なデータもオペレータが毎回入力しなければならないことになり、フォームの使い勝手がかなり悪くなってしまいます。

このような場合には、InfoPathのスクリプトを使って既定値を設定します。では、一例として、「注文コード」フィールドにユニークなIDを自動的に入力する方法を確認してみましょう。

まず、「注文コード」フィールドに対応するデータソースの「OrderId」をダブルクリックし、フィールドのプロパティを確認します。すると、このフィールドは単純な連番ではなく、GUIDを使っていることがわかりますね。では、GUIDを生成し、その値を「注文コード」フィールドに設定するスクリプトを作成しましょう。

柔軟に既定値を設定するには、フォームのOnLoadイベントを使うべし!

可変の値を既定値として設定するには、フォームのOnLoadイベントを記述します。このイベントはフォームの読み込み時に動作しますので、ユーザーが入力を開始する前に既定値を設定することができます。

InfoPathのメニューから[ツール]-[スクリプト]-[読み込み時のイベント]を選択すると、Script Editorが起動して、XDocument::OnLoadの処理を記述できるようになります。

フィールドを操作するスクリプトを作成するときは、XML(DOM)を意識せよ!

さて、フォームのOnlLoadイベントを使うとなれば、読み込み時に特定のフィールドに値を設定するスクリプトを書けばよい、というところまではすぐに結びつきます。では、実際にはどのようにスクリプトを記述するのでしょうか

実はInfoPathのスクリプトでは、フォーム上のデータはXDocumentオブジェクトというXMLデータとして扱い、各フィールドへのアクセスは DOMを使って行います。したがって、「フィールドの値を設定する」という一見単純な動作においても、対象となるフィールドにはDOMを使ってアクセスしなければなりません。

そのため、フィールドの値を参照したり、設定したりするだけの単純な動作であっても、記述するコードの量は比較的多くなります。それで、そのような面倒で定型的な処理は関数にしておき、必要な箇所でその関数を呼び出すようにするのが得策です。

実際、InfoPathの標準サンプルの中でも、ノードの基本的な操作は関数として記述されており、各機能から必要に応じて呼び出されています。ノードの操作において、特に使用頻度が高いと思われる関数をリスト2に示します(これらの関数はInfoPathの標準サンプルから抜き出したものです)。

リスト2:InfoPathでフィールドを操作する場合に使用される主な関数

// ----------------------------------------------------------------
// initializeNodeValue(): フィールドの既定値を設定する。
// ----------------------------------------------------------------
function initializeNodeValue(xpath, strValue)
{
var xmlNode = getNode(xpath);
// そのフィールドの値が入っていない場合にのみ、新しい値を設定する
if (xmlNode.text == "") setNodeValue(xmlNode, strValue);
}
// ----------------------------------------------------------------
// setNodeValue (xpath, value): XML のノードの値を設定する。
// ----------------------------------------------------------------
function setNodeValue(xpath, value)
{
var xmlNode = getNode(xpath);
if (!xmlNode) return;
// 値を設定する前に、xsi:nil を削除しておく必要があります。
if (value != "" && xmlNode.getAttribute("xsi:nil"))
  xmlNode.removeAttribute("xsi:nil");
// 以前の値と変わった場合にのみ、実際に値を設定します。
If (xmlNode.text != value)
  xmlNode.text = value;
}
// ----------------------------------------------------------------
// getNode(xpath): XML のノードを取得する。
// ----------------------------------------------------------------
function getNode(xpath)
{
// XML Node、XPath のどちらでも対応できます。
if (typeof(xpath) == "string")
return XDocument.DOM.selectSingleNode(xpath);
else
return xpath;
}

これらの関数を利用して、注文コードを自動的に設定する処理をリスト3に示します。ここではActiveXオブジェクトを使ってGUIDを生成し、initializeNodeValueを使ってOrderIdに値を設定します。ノードの特定にXPathを使用していることに注意してください。

リスト3:注文コード(OrderID)を自動的に設定する

//=======
// 次の関数ハンドラが自動的に作成されます。
// 関数の名前や引数の名前と数を変更しないでください。
// この関数は次のフィールドまたはグループ(XPath) に
// 関連しています: /dfs:myFields/dfs:dataFields/s0:Order/s0:OrderId
// 注意: このコメントの情報は、関数ハンドラの作成後、更新されません。
//=======
function XDocument::OnLoad(eventObj)
{
// 読み取り専用の場合はそのまま終了する。
if ( XDocument.IsDOMReadOnly ) return;

initializeNodeValue( "/dfs: マイフィールド /dfs:dataFields/s0:Order/s0:OrderId", getGUID() );
}

// -------------------------------------------------------------------------
// getGUID
// GUID(8 桁-4 桁-4 桁-4 桁-12 桁)を生成します。
// -------------------------------------------------------------------------
function getGUID()
{
var ax = new ActiveXObject("Scriptlet.Typelib") ;
// ここでActiveX オブジェクトを使用しているため、セキュリティの確認ダイアログが表示されます。

var reg = / ¥{| ¥}/g ;
var guid = ax.guid ;

return ( guid.replace( reg, "" ) ) ;
}

今回のように既定値を設定する場合だけでなく、あるフィールドの入力値に応じて別のフィールドの値を変更するとき、また計算などを行うときなど、フィールドを扱う操作はすべてこのDOMを使った方法が基本になります。ぜひマスターしておかれることをお勧めします。

機能2:選択肢をデータベースから取得し、対応するデータを自動的に入力する

次に取り組むのは、リストボックスの選択肢をデータベース内のマスターから取得し、その値に応じて別のフィールドを設定する、という機能です。たとえば、「顧客」フィールドで顧客マスターから顧客名を選択すると、選択した顧客の「顧客コード」フィールドに顧客コードが自動的に設定される、といった動作です。

リストボックスの選択肢や関連した値をデータベースから取得するには、セカンダリデータソースを設定する必要があります。

セカンダリデータソースを活用すべし!

フォームの(メインの)データソースは、フォームに記入したデータそのものを送受信する際に利用されるデータ構造です。それに対し、ドロップダウンリストボックスの選択肢など、フォームで二次的に利用されるデータソースを「セカンダリデータソース」と呼びます。セカンダリデータソースには、メインのデータソースと同様、Webサービスやデータベースを利用できます。

では、今回のサンプルフォームを使い、「CustomerName」フィールドで顧客マスター内の顧客をリストする方法を確認しましょう。

先ほど、ASPNETKITデータベース内のCustomersテーブルが顧客マスターであることを確認しました。このテーブルをセカンダリデータソースとして設定します。

この作業の前提として、「CustomerName」フィールドをドロップダウンリストボックスとして設定する必要があります。「CustomerName」フィールドのようなテキスト(string)のフィールドは既定のままだとテキストボックスになりますので、ドロップダウンリストボックスに変更しておいてください。その後、「CustomerName」フィールドをダブルクリックし、フィールドのプロパティを開いて、リストボックスのデータ設定を開始します(図17を参照)。

リストボックスの選択肢をデータベースから取得するイメージ
図17:リストボックスの選択肢をデータベースから取得する

  • プロパティダイアログの下のほうに「リストボックス項目」があり、リストボックス内の選択肢を設定できます。ここで「データベース、Webサービス、またはファイルから設定する」を選択し、さらに「セカンダリデータソース」をクリックします。
  • 最初、セカンダリデータソースは定義されていませんので、[追加]ボタンをクリックしてセカンダリデータソースを追加する必要があります。ここからは図14でも説明したデータソースセットアップウィザードに進みますが、今回はデータソースの種類を「データベース」に設定します。
  • データソースの種類をデータベースにすると、データファイル(.odcファイル)を選択するダイアログボックスが表示されます。すでにデータファイルが作られていれば底から選択できますが、今回は新規に作成するので「新しいソース」をクリックします。
  • ここからデータ接続ウィザードが開始されます。データベースの種類として「Microsoft SQL Server」を選択します。
  • サーバーに接続するため、データベースサーバーと、そのデータベースにアクセスできるユーザーを設定します。
  • 接続先データベースとして「ASPNETKIT」を選択し、テーブルはCustomersテーブルを選択します。
  • データファイルを保存したところで、データ接続ウィザードは完了です。
  • またデータソースセットアップウィザードに戻り、取得するデータ構造を設定します。今回はCustomersテーブルのみを取得しますので、そのまま「次へ」をクリックします。
  • データソース名を設定すればデータソースセットアップウィザードは完了です。
  • ドロップダウンリストのプロパティダイアログで、[データソース]は今作成した「Customers」を選択し、[項目]は「d:Customers」を、[値]と[表示名]はそれぞれ「@CustomerName」に設定します。

これで、データベースの値をドロップダウンリストの選択肢として表示できるようになりました。

データベースの値を参照するにも、セカンダリデータソースを活用するべし!

ドロップダウンリストで選択した値によって、データベース内の値を参照し、フィールドに設定するにはどうすればいいでしょうか? たとえば、「CustomerName」フィールドを選択したとき、自動的にその顧客のIDを「CustomerId」フィールドに入れるような場合です。この場合には、スクリプトとセカンダリデータソースを組み合わせて活用します。

まず、「CustomerName」フィールドが更新された時にスクリプトが呼び出されるようにします。それには、「CustomerName」フィールドのプロパティを開き、[データの入力規則]をクリックしてから、[スクリプト]の[イベント]で「OnAfterChange」を選択して[編集]をクリックします。

すると、msoxd_s0_CustomerName::OnAfterChangeの処理を記述できるようになりますので、リスト4のように入力します。

リスト4:「CustomerName」フィールドの値を切り替えると、自動的に「CustomerID」フィールドを更新する

function msoxd_s0_CustomerName::OnAfterChange(eventObj)
{
if (eventObj.IsUndoRedo)
  {
return;
  }
var CustomerName = getNode("/dfs: マイフィールド/dfs:dataFields/s0:Order/s0:CustomerName").text ;
UpdateCustomerId( CustomerName ) ;
}

function UpdateCustomerId( CustomerName )
{
var dataObject = XDocument.DataObjects.Item("Customers") ;

dataObject.DOM.setProperty("SelectionNamespaces",
'xmlns:dfs="http://schemas.microsoft.com/of.ce/infopath/2003/dataFormSolution"' +
'xmlns:d="http://schemas.microsoft.com/of.ce/infopath/2003/ado/dataFields"');

var queryNode = dataObject.DOM.selectSingleNode(
"/dfs: マイフィールド/dfs:dataFields/d:Customers[@CustomerName='"+CustomerName + "']" );

var queryValue = queryNode.getAttribute( "CustomerID" ) ;
setNodeValue("/dfs: マイフィールド/dfs:dataFields/s0:Order/s0:CustomerId",queryValue ) ;
} 

フィールドの値の設定方法は、既定値を設定するときの方法と大きくは異なりませんが、ここでの最大のポイントは、UpdateCustomerIdの最初にあるXDocument.DataObjectsです。

XDocument.DataObjectsを使うと、フォームで定義されているセカンダリデータソースのデータを参照できます。ここでは先ほど定義した「Customers」という名前のセカンダリデータソースを参照しています。

セカンダリデータソースを取得できれば、あとはXPathを使って条件に合致するノードの値を取り出すだけです。データベースへの再接続などの処理は一切必要ありません。InfoPathがセカンダリデータソースに接続したときに、そのテーブルの内容をXMLとして保持しているため、それを再度参照するだけなのです。こんなところでもXMLが活躍していますね。

今回は「CustomerName」と「CustomerId」の組み合わせのみを取り上げましたが、「EmployeeName」と「EmployeeId」、さらには「ProductName」と「ProductId」、「Price」に関しても同じ仕組みで応用できます。データソースをそれぞれ設定し、あとはスクリプトを使って対応するフィールドを更新します。

セカンダリデータソースとスクリプトをフル活用して、使いやすいフォームを作成してください。

機能3:個数と単価から小計を自動計算する

読者の方もお気づきのとおり、これは機能1の応用編です。商品ごとに個数(Quantity)と単価(Price)を掛けて、小計を設定します。リストは掲載しませんので、ぜひ自力で取り組んでみてください。重要なヒントを2つ、お教えしましょう。

→ どのイベントで動作させるか、はお分かりですよね? 「ProductName」フィールド、「Quantity」フィールド、「Price」フィールドのOnAfterChangeイベントで動作させましょう。

→ Itemのような繰り返しフィールドの場合、XPathでノードの位置を特定するには、OnAfterChangeの引数(eventObj)を活用できます。eventObjを起点にして、parentなどを使って相対的に取得することになります。ブレークポイントなどを活用して、eventObjの内容も探ってみてください。

フォームが完成したら...

フォームが完成したら、あとはテストです。Webサービスに接続し、注文処理が正しく行われるかどうか、確認してみてください。もし注文処理が正しく処理されていれば、データベース内の商品の在庫数が更新されるはずです。

このセクションでは、Webサービスを利用する場合の基本的な作業手順と、スクリプトを利用したフォームデータの操作を扱いました。利用する用途やWebサービスによって細部は異なりますが、基本的には同じような手順で進めていきます。

まとめ

今回の特集記事はいかがだったでしょうか?
紙面の都合上、扱えなかった機能はまだまだたくさんあります。データを別のレイアウトで見せるための「ビュー」、特定の条件に当てはまるデータを取り出すための「クエリビュー」などは、残念ながら今回取り上げることができませんでした。これらの機能についてはヘルプやサンプルの中で頻繁に取り上げられていますので、ぜひ参考にしてみてください。

最後のセクションのスクリプトを見ていくとわかるように、InfoPathは内部的には完全にXMLをベースにしています。 しかし、データを入力するエンドユーザーやオペレータにはそのことはまったく見えません。他のシステムとの連携性などのXMLのメリットを生かしつつ、エンドユーザーの敷居は低く抑えることができていますし、開発作業も極力抑えることができています。この点には注目できるでしょう。

今回のOfficeから新しく加わった生まれたてのアプリケーションとして、InfoPathにはさらに使いやすくなってほしい部分も確かにあります。可変の既定値をもう少し手軽に設定できたら楽なのに(これはInfoPathのデータを極力シンプルなXMLデータファイルにまとめあげるために切り捨てられたのかもしれませんが)、とか、SOAP RPCが利用できたらいろいろな処理系とつなぎやすくなるのに、といった要望はありますが、現時点でもInfoPathは強力なポテンシャルを秘めており、非常に興味深く、取り組み甲斐のあるアプリケーションといえるでしょう。

今後、Office製品の一角として企業内にどのように導入されていくのか、非常に楽しみです。

▲このページのTOPへ

  • XMLとは?IT初心者でもすぐわかるXML超入門
  • 無償で使える!XMLDB「NeoCore」
  • サイバーテック求人情報
  • メールマガジン申し込み
  • TEchScore

  • ▲NeoCoreについて記載されています!

  • ▲XMLマスター教則本です。試験対策はこれでばっちり!
Copyright (c) CyberTech corporation ltd. All ights Reserved. | サイバーテックについて | ご利用ガイド