クライアント オブジェクト モデルを使用したプログラミング 第 3 回 : Silverlight アプリケーションの作成
こんにちは。SharePoint サポートチームの多田です。
第 3 回では、.NET マネージ API を使用して、Silverlight アプリケーションを作成する手順と tips をご紹介します。
- 環境
・SharePoint Server 2010
・Visual Studio 2010
- 参照する DLL ファイル
Silverlight アプリケーションを作成する際に使用する DLL ファイルは以下の 2 つのファイルです。
%ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\TEMPLATE\LAYOUTS\ClientBin\Microsoft.SharePoint.Client.Silverlight.dll
%ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\TEMPLATE\LAYOUTS\ClientBin\Microsoft.SharePoint.Client.Silverlight.Runtime.dll
- フォーム アプリと Silverlight アプリの違いは?
フォーム アプリケーションと Silverlight アプリケーションの違いは基本的には 1 つだけです。
Silverlight アプリケーションは SharePoint サーバーと通信する際に非同期通信を行う必要があります。 前回、フォーム アプリケーションの例では ExecuteQuery メソッドを使用してサーバー側と同期通信をしてました。Sliverlight アプリケーションでは ExecuteQueryAsync メソッドを使用して非同期通信を行います。
- Hello World
最初に Hello World ということで、Silverlight の DataGrid にカスタムリストのデータを表示するサンプルを書いてみたいと思います。
まずはデータ元となるリストを作成します。今回はカスタム リストをもとに「取引先一覧」というリストを作成しました。
<列データ>
表示名 |
内部名 |
列の種類 |
ID |
ID |
ID |
名前 |
Title |
一行テキスト |
会社名 |
_x4f1a__x793e__x540d_ |
一行テキスト |
役職 |
_x5f79__x8077_ |
一行テキスト |
訪問済み |
_x8a2a__x554f__x6e08__x307f_ |
はい/いいえ |
最終訪問日 |
_x6700__x7d42__x8a2a__x554f__x65 |
日付と時刻 |
上記のリストをもとにリストのデータの参照、追加、削除を行う Silverlight アプリケーションを作成し、Silverlight Web パーツで表示しました。
それでは実際に内部のコードを見ていきましょう。
- リストアイテムのデータ取得
以下では、まだ ”訪問済み” でない取引先相手を抽出するプログラムです。データの取得は CamlQuery を使用して取得しています。
private ListItemCollection _list;
private string _listTitle= "取引先一覧";
//DataGrid のもととなるデータ型を定義
public class BusinessPartner
{
public string ID { get; set; } //ID
public string Name { get; set; } //名前
public string CompanyName { get; set; } //会社名
public string Post { get; set; } //役職
public bool IsVisited { get; set; } //訪問済み
public DateTime? LastVisitedDate { get; set; } //最終訪問日
}
//コンストラクタ
public MainPage()
{
InitializeComponent();
GetListData(_listTitle);
}
//データ取得メソッド
private void GetListData(string listTitle)
{
ClientContext context = new ClientContext(ApplicationContext.Current.Url);
context.Load(context.Web);
List list = context.Web.Lists.GetByTitle(listTitle);
context.Load(list);
CamlQuery query = new CamlQuery();
string camlQueryXml = "<View><Query><Where><Eq>" +
"<FieldRef Name='_x8a2a__x554f__x6e08__x307f_' />" +
"<Value Type='Boolean'>false</Value>" + //訪問済みかどうか
"</Eq></Where></Query></View>";
query.ViewXml = camlQueryXml;
_list = list.GetItems(query);
context.Load(_list);
context.ExecuteQueryAsync(new ClientRequestSucceededEventHandler(OnRequestSucceeded), new ClientRequestFailedEventHandler(OnRequestFailed));
}
//コールバック成功時
private void OnRequestSucceeded(Object sender, ClientRequestSucceededEventArgs args)
{
Dispatcher.BeginInvoke(() =>
{
List<BusinessPartner> partners = new List<BusinessPartner>();
foreach (ListItem li in _list)
{
partners.Add(new BusinessPartner()
{
ID = li["ID"].ToString(),
Name = li["Title"].ToString(),
CompanyName = li["_x4f1a__x793e__x540d_"].ToString(),
Post = li["_x5f79__x8077_"].ToString(),
IsVisited = (bool)li["_x8a2a__x554f__x6e08__x307f_"],
LastVisitedDate = (DateTime?)li["_x6700__x7d42__x8a2a__x554f__x65"]
});
}
this.dataGrid1.ItemsSource = partners;
});
}
//コールバック失敗時
private void OnRequestFailed(Object sender, ClientRequestFailedEventArgs args)
{
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(args.Message + "\r\n" + args.ErrorDetails + "\r\n" + args.StackTrace);
});
}
上記のサンプルではデータ取得メソッド内で、ClientContext.ExecuteQueryAsync メソッドを使用して非同期でデータを取得しています。引数にてそれぞれデータ取得成功時に実行するメソッド : OnRequestSucceeded メソッド、およびデータ取得失敗時に実行するメソッド : OnRequestFailed メソッドを指定しています。これらのメソッドはラムダ式の記述を使うことで一つのメソッド内に簡潔に記述することが可能です。以下にサンプルをご案内します。
//リスト取得
private void GetListData(string listTitle)
{
ClientContext context = new ClientContext(ApplicationContext.Current.Url);
context.Load(context.Web);
List list = context.Web.Lists.GetByTitle(listTitle);
context.Load(list);
CamlQuery query = new CamlQuery();
string camlQueryXml = "<View><Query><Where><Eq>" +
"<FieldRef Name='_x8a2a__x554f__x6e08__x307f_' />" +
"<Value Type='Boolean'>false</Value>" + //訪問済みかどうか
"</Eq></Where></Query></View>";
query.ViewXml = camlQueryXml;
_list = list.GetItems(query);
context.Load(_list);
context.ExecuteQueryAsync((sender, args) => //正常系処理
{
Dispatcher.BeginInvoke(() =>
{
List<BusinessPartner> partners = new List<BusinessPartner>();
foreach (ListItem li in _list)
{
partners.Add(new BusinessPartner()
{
ID = li["ID"].ToString(),
Name = li["Title"].ToString(),
CompanyName = li["_x4f1a__x793e__x540d_"].ToString(),
Post = li["_x5f79__x8077_"].ToString(),
IsVisited = (bool)li["_x8a2a__x554f__x6e08__x307f_"],
LastVisitedDate = (DateTime?)li["_x6700__x7d42__x8a2a__x554f__x65"]
});
}
this.dataGrid1.ItemsSource = partners;
});
}, (sender, args) => //エラー系処理
{
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(args.Message + "\r\n" + args.ErrorDetails + "\r\n" + args.StackTrace);
});
});
}
- リストアイテムのデータ追加
同じようにリストアイテムにデータを追加する方法も見ていきましょう。以下では Silverlight のテキストボックス内に入力された値をリストアイテムのプロパティに設定しています。
//リストアイテム追加
private void AddListData(string listTitle)
{
ClientContext context = new ClientContext(ApplicationContext.Current.Url);
context.Load(context.Web);
List list = context.Web.Lists.GetByTitle(listTitle);
ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();
ListItem listItem = list.AddItem(itemCreateInfo);
listItem["Title"] = this.Name.Text;
listItem["_x4f1a__x793e__x540d_"] = this.CompanyName.Text;
listItem["_x5f79__x8077_"] = this.Post.Text;
listItem["_x8a2a__x554f__x6e08__x307f_"] = this.IsVisited.IsChecked;
listItem["_x6700__x7d42__x8a2a__x554f__x65"] = this.LastVisitDate.SelectedDate;
listItem.Update();
context.ExecuteQueryAsync((sender, args) => // 正常系処理
{
GetListData(listTitle); //データ更新後の再表示のため
}, (sender, args) => // エラー系処理
{
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(args.Message + "\r\n" + args.ErrorDetails + "\r\n" + args.StackTrace);
});
}
);
}
- リストアイテムのデータ削除
最後にリストアイテムのデータ削除の方法を紹介します。DataGrid 内で選択されたアイテムの情報を取得し、ID 列の値をもとにアイテムを削除しています。
//リストアイテム削除
private void DeleteListData(string listTitle)
{
BusinessPartner biz = (BusinessPartner)this.dataGrid1.SelectedItem;
ClientContext context = new ClientContext(ApplicationContext.Current.Url);
context.Load(context.Web);
List list = context.Web.Lists.GetByTitle(listTitle);
ListItem item = list.GetItemById(biz.ID);
item.DeleteObject();
context.ExecuteQueryAsync((sender, args) => // 正常系処理
{
GetListData(listTitle); //データ更新後の再表示のため
}, (sender, args) => // エラー系処理
{
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(args.Message + "\r\n" + args.ErrorDetails + "\r\n" + args.StackTrace);
});
}
);
}
今回作成したサンプルプログラムを本記事に添付しておきます。ご自由にお使いください。
免責事項 : このサンプルプログラムはご自身の責任にてご使用ください。このサンプルプログラムに関していかなる責任も負うものではありません。
- プロジェクト作成、ビルド、デプロイ、デバッグについて
次は Siliverlight アプリケーションのプロジェクト作成、ビルド、デプロイ、およびデバッグについて紹介します。
Siliverlight アプリケーションを作成する SharePoint 専用のプロジェクト テンプレートは Visual Studio 2010 では用意されていません。そのため、Silverlight アプリケーションのビルド、デプロイ、デバッグはひと工夫が必要です。
- クロスドメイン制約について
Siliverlight アプリケーションを作成する際、通常は Visual Studio の “Silverlight アプリケーション” テンプレートを使用して作成します。“Silverlight アプリケーション” プロジェクト テンプレートはデバック用に aspx ページが用意され、ローカルホスト上に仮想サーバーを立ててデバッグを行います。デバッグ環境の仮想サーバーの URL は通常 https://localhost:<ポート番号> になります。一方 SharePoint の URL は既定では https://<サーバー名>:<ポート番号> になっています。 この URL の相違により Silverlight アプリケーションがホストされているドメインと、SharePoint のクライアント オブジェクト モデルがホストされているドメインが異なり、一般的にセキュリティ リスクがあると認識されるとクロスドメイン制約に触れるため、Silverlight アプリケーションから SharePoint のクライアント オブジェクト モデルを呼び出すことができません。
※ クロスドメインアクセスを可能にする方法に関しては本記事では触れませんが、以下の技術資料をご参考ください。
タイトル : Making a Service Available Across Domain Boundaries
アドレス : https://msdn.microsoft.com/en-us/library/cc197955(VS.95).aspx
タイトル : [新機能] Silverlight の統合とドメインを越えたデータ アクセス
アドレス : https://msdn.microsoft.com/ja-jp/library/ee539062.aspx
- ビルドとデプロイについて
上記のクロスドメイン制約を回避するためには、Silverlight アプリケーションを SharePoint サイト上で動作させる必要があります。Silverlight アプリケーションを SharePoint 上で動作させるには既定で用意されている Silverlight Web パーツを使用するのが一番簡単です。xap ファイルを指定するだけで Web パーツとして Silverlight アプリケーションを動作させることができます。
xap ファイルは通常 C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\ClientBin フォルダーに配置します。そのため、Visual Studio のビルド設定で出力先を ClientBin フォルダーに変更します。
xap ファイルを配置したら、次は Silverlight Web パーツの設定です。Web パーツの編集画面で CientBin フォルダー配下に配置した xap ファイルを指定します。
(※ 上記の設定値は /_layouts/ClientBin/SilverlightApplication2.xap)
このような手順を踏むことで、Silverlight アプリケーションを再ビルドしても手動での再配置の手間が省け、デバッグが楽になります。再ビルドした後は、新しい xap ファイルを読み込むため、IE のキャッシュをクリアする必要があります。
- デバッグについて
デバッグは 上述のクロスドメイン制約などにより Visual Studio の F5 ボタンでは出来ないため、IE から通常ページ アクセスを実施した後、Visual Studio の [デバッグ] – [プロセスにアタッチ] メニューより実行中の IE のプロセスに対してアタッチして行います。iexplore.exe のプロセスは複数ありますが、Type 列が “Silverlight” となっているものを選択します。
補足:Type 列が "Silverlight" の iexplore.exe にアタッチしても、うまくブレークポイントにヒットしない場合は、一旦 IE で開いているすべてのページを閉じ、デバックする Silverlight アプリケーションが存在する SharePoint のページのみを開いて再度アタッチします。
ビルド、デプロイ、デバッグについては以上です。一般的な Silverlight アプリケーションの作成について記載された以下の技術資料も合わせてご参考ください。
タイトル : SharePoint 2010 データにアクセスする Silverlight アプリケーションを作成する
アドレス : https://msdn.microsoft.com/ja-jp/library/ff728647.aspx
- 補足情報
その他、本記事を書くにあたって確認した情報を以下に案内いたします。
- SharePoint Online (Office 365) について
SharePoint Online では ClientBin フォルダにアクセス出来ないため、任意のドキュメント ライブラリに xap ファイルを配置し、Silverlight Web パーツで xap ファイルのパスを指定します。今回のサンプルは SharePoint Online で動作することを確認済みです。 (2012/06/06 時点)
- Silverlight Web パーツ内での日本語入力について
Silverlight Web パーツ内のテキストボックス等で日本語が入力できない場合があります。その際は、Silverlight Web パーツが存在するページを SharePoint Designer で開き、WebPartPages:SilverlightWebPart タグに WindowlessMode="False" を加えます。
WindowlessMode については以下の資料をご参考ください。
タイトル : SilverlightWebPart.WindowlessMode Property
タイトル : Windowless (Silverlight プラグイン オブジェクト)
アドレス : https://msdn.microsoft.com/ja-jp/library/cc838156(VS.95).aspx
- SharePoint 用の Silverlight アプリケーション プロジェクト テンプレート
既定では SharePoint 用の Silverlight アプリケーション プロジェクト テンプレートは用意されておりません。しかしながら、non-support ではありますが SharePoint 用の Silverlight アプリケーション プロジェクト テンプレートがありました。検証用等に便利かと思いますのでここに紹介しておきます。
タイトル : SharePoint 2010 Extensibility Projects
アドレス : https://archive.msdn.microsoft.com/vsixforsp