はじめに
NetAdvantage for Silverlight / NetAdvantage for WPF にはスケジュールを管理するために活用できる、xamSchedule コントロールが含まれています。この xamSchedule コントロールを活用することで Microsoft Outlook ライクなスケジュール表示を行うことが可能です。今回から複数回に分けて、この xamSchedule コントロールの活用方法について解説し、リッチなスケジュール画面を構築します。
対象読者
Visual Basic 2010, Visual C# 2010, Expression Blend 4, XAML を使ってプログラミングをしたことのある人。XAML プラットフォームにおいてのデータ バインディングについて基本的な理解を持っている人
必要環境
Visual Basic 2010 あるいは Visual C# 2010, Expression Blend 4 でプログラムが作れる環境。サンプルは Visual Studio 2010 Ultimate, Silverlight 4, WPF4 にて作成しています。また、Windows 7 Ultimate 64bit 版において動作を検証しています。
プログラム実行時の注意環境
Silverlight 4 の場合
あらかじめ Silverlight 4 Tools for Visual Studio 2010 がインストールされていることを確認してください。インストーラーはこちらより入手可能です。サンプル ソリューションを Visual Studio 2010 または Expression Blend 4 で開き実行します。
WPF4 の場合
あらかじめ、.NET Framework 4 ランタイムがインストールされていることを確認してください(Visual Studio 2010 セットアップ時に自動的にインストールされます)。単体のランタイムはこちらより入手可能です。
コンポーネントのインストールならびに製品ラインナップ
はじめて NetAdvantage for Silverlight LOB, NetAdvantage for WPF LOB を使用する場合は、事前にソフトウェアをインストールする必要があります。インフラジスティックス社の Web ページからインストーラーをダウンロードしてください。両プラットフォームを含め、すべてのプラットフォーム対応版をインストールする場合、NetAdvantage Ultimateを選択し、こちらからダウンロードを行います(サイトへの登録が必要になります)。NetAdvantage Ultimate 2010 Volume 3 Complete はサンプル、ヘルプ全てを含んでおり、NetAdvantage Ultimate 2010 Volume 3 Full はコントロールのみ含まれています。この製品は有償ですが、20日間全ての機能を使用できるトライアル版として利用が可能です。
NetAdvantage for Silverlight LOB, NetAdvantage for WPF LOB について
Silverlight 版 は 2009 年に日本初の Silverlight 対応コンポーネントとして、WPF 版は 2007 年に世界初の WPF 対応コンポーネントとして発売が開始され、原稿執筆時点での日本語版最新バージョンは 2010 Volume 3 となります。収録されているコントロールの特徴として、高機能、高パフォーマンスを実現したデータグリッド、ツリー、チャート、そして今回紹介するスケジュール コントロールを備え、リッチクライアント、RIA 開発の効率を高めます。また、2010 Volume 2 より両プラットフォームでのソースコード統合が図られ、プラットフォーム間でのポータビリティが向上しました。製品サンプルについてはこちらからオンラインで確認することも可能です。
xamSchedule コントロール - ビュー
xamSchedule には 日単位での表示を行う XamDayView コントロール、月単位での表示を行う XamMonthView コントロール、Outlook 2010 で導入された横時間軸表示を行う XamScheduleView コントロールが用意されています。
各ビューでは予定のドラッグを行ったり、コンポーネントに付属しているダイアログを使用し、情報を更新したりすることが可能です。
xamSchedule コントロール - データ マネージャー
xamSchedule では予定を XamScheduleDataManager コントロールで一元的に管理するため、各ビュー間でのデータの同期を容易に行うことが可能になります。予定以外にも稼働日(例 月~金)や稼働時間(例 09:00 - 18:00)などを設定可能です。このコントロールはコントロールとしては何も画面に表示しません。
xamSchedule コントロール - データ コネクター
xamSchedule では独自のデータ構造を使用し予定情報を保存しています。この予定情報を DB に保存する、あるいは要件に合わせて予定情報のスキーマを変更するために DB と データ マネージャーの間を取り持つデータ コネクター クラスが用意されています。現在のバージョンでは次の 3 つの選択肢が存在します。
コネクター名 | 概要 |
WCFScheduleConnector | WCF サービスを使用するコネクター |
ListScheduleConnector | 汎用的に使用できるコネクター データ項目に IEnumerable インターフェースを実装したリスト項目をバインド可能 |
ScheduleConnectorDataBase | カスタム コネクターを実装するためのベース クラス そのままでは使用できない |
xamSchedule コントロール - データ構造
xamSchedule コントロールを使用して予定を表示させる場合、それぞれのエンティティで最低限次のデータ構造を設定する必要があります。ただし、Appointment、Journal、Task に関しては使用しない場合、定義する必要はありません。下記のエンティティは C# の型として記載しています。
Resource - スケジュールのリソース(人、会議室など)を表します。
エンティティ名 | 型 | 概要 |
Id | String | 一意の値(非 Null 許容) |
ResourceCalendar - リソース毎のカレンダーを表します。リソースは複数のカレンダーを持つことも可能です。
エンティティ名 | 型 | 概要 |
Id | String | 一意の値 (非 Null 許容) |
OwningResourceId | String | リソース ID(非 Null 許容) |
Appointment / Journal / Task - 予定、履歴やメモ、タスクを表します。
エンティティ名 | 型 | 概要 |
Id | String | 一意の値 (非 Null 許容) |
Start | DateTime | 開始時刻(非 Null 許容) |
End | DateTime | 終了時刻(非 Null 許容) Task, Appointment は必須、Journal はオプション |
OwningResourceId | String | 項目を保持しているリソース (非 Null 許容) |
OwningCalendarId | String | 項目が属しているカレンダー。 リソース カレンダーを使用する場合に必要 (非 Null 許容) |
IsTimeZoneNeutral | bool | タイム ゾーンに関係なく発生する項目をサポートする場合に必要 |
ReminderInterval | TimeSpan | アラームの間隔 |
ReminderEnabled | bool | アラームの使用 |
Recurrence | String | 繰り返しをサポートする場合に必要 |
RootActivityId | String | 繰り返し項目のルート項目 ID |
OriginalOccurrenceStart | DateTime | 繰り返し項目のオリジナルの開始時刻 |
OriginalOccurrenceEnd | DateTime | 繰り返し項目のオリジナルの終了時刻 |
IsOccurrenceDeleted | bool | 繰り返しが削除されているかを示すフラグ |
VariantProperties | long | ルート アクティビティと同期されないプロパティ |
RecurrenceVersion | int | 繰り返しのバージョン |
上記の項目以外にも予定の件名や現在のリソースの主カレンダー設定などのエンティティが定義されています。詳細については次の各クラスを参照してください。
ソリューションを作成し、データ クラスを定義
今回は、.NET Framework 4 並びに Silverlight 4 のアセンブリ共有の仕組みを利用し、データ モデルを一元化するサンプルを作成します。また、各データについてはデータ バインディングの仕組みを用いてコントロールにスケジュール情報を表示させます。まず、Visual Studio 2010 を起動し、空のソリューション "xamSchedule" を作成します。
作成したソリューションに Silverlight クラス ライブラリを "ScheduleDataViewModel" と名付け、追加します。その際、ターゲットとするバージョンは Silverlight 4 を指定します。
あらかじめ作成されている Class1.cs ファイルを削除し、新たに ViewModelBase クラスを作成します。
このクラスでは INotifyPropertyChanged インターフェースの実装を行い、さらに、OnPropertyChanged メソッドの実装を行います。このインターフェースを実装することで、今後ロジック側において、プロパティの値が変更された場合にビュー(UI)側に変更通知が行われます。
using System.ComponentModel; namespace ScheduleDataViewModel { public class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { if ( PropertyChanged != null ) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } }
続けて、Resource , ResourceCalendar, Appointment エンティティを実装した、ResourceInfo,ResourceCalendarInfo, AppointmentInfo クラスを実装します。ResourcInfo クラスには PrimaryResourceCalendarId という現在のリソースの主カレンダーを保持するエンティティを追加します。
namespace ScheduleDataViewModel { public class ResourceInfo : ViewModelBase { #region プロパティ private string _id; public string Id { get { return _id; } set { _id = value; OnPropertyChanged("Id"); } } private string _primaryResourceCalendarId; public string PrimaryResourceCalendarId { get { return _primaryResourceCalendarId; } set { _primaryResourceCalendarId = value; OnPropertyChanged("PrimaryResourceCalendarId"); } } #endregion } }
namespace ScheduleDataViewModel { public class ResourceCalendarInfo : ViewModelBase { #region プロパティ private string _id; public string Id { get { return _id; } set { _id = value; OnPropertyChanged("Id"); } } private string _owningResourceId; public string OwningResourceId { get { return _owningResourceId; } set { _owningResourceId = value; OnPropertyChanged("OwningResourceId"); } } #endregion } }
using System; namespace ScheduleDataViewModel { public class AppointmentInfo : ViewModelBase { #region プロパティ private string _id; public string Id { get { return _id; } set { _id = value; OnPropertyChanged("Id"); } } private DateTime _start; public DateTime Start { get { return _start; } set { _start = value; OnPropertyChanged("Start"); } } private DateTime _end; public DateTime End { get { return _end; } set { _end = value; OnPropertyChanged("End"); } } private string _owningResourceId; public string OwningResourceId { get { return _owningResourceId; } set { _owningResourceId = value; OnPropertyChanged("OwningResourceId"); } } private string _owningCalendarId; public string OwningCalendarId { get { return _owningCalendarId; } set { _owningCalendarId = value; OnPropertyChanged("OwningCalendarId"); } } private TimeSpan _reminderInterval; public TimeSpan ReminderInterval { get { return _reminderInterval; } set { _reminderInterval = value; OnPropertyChanged("ReminderInterval"); } } private bool _reminderEnabled; public bool ReminderEnabled { get { return _reminderEnabled; } set { _reminderEnabled = value; OnPropertyChanged("ReminderEnabled"); } } private string _recurrence; public string Recurrence { get { return _recurrence; } set { _recurrence = value; OnPropertyChanged("Recurrence"); } } private int _recurrenceVersion; public int RecurrenceVersion { get { return _recurrenceVersion; } set { _recurrenceVersion = value; } } private string _rootActivityId; public string RootActivityId { get { return _rootActivityId; } set { _rootActivityId = value; OnPropertyChanged("RootActivityId"); } } private DateTime _originalOccurrenceStart; public DateTime OriginalOccurrenceStart { get { return _originalOccurrenceStart; } set { _originalOccurrenceStart = value; OnPropertyChanged("OriginalOccurrenceStart"); } } private DateTime _originalOccurrenceEnd; public DateTime OriginalOccurrenceEnd { get { return _originalOccurrenceEnd; } set { _originalOccurrenceEnd = value; OnPropertyChanged("OriginalOccurrenceEnd"); } } private bool _isOccurrenceDeleted; public bool IsOccurrenceDeleted { get { return _isOccurrenceDeleted; } set { _isOccurrenceDeleted = value; OnPropertyChanged("IsOccurrenceDeleted"); } } private long _variantProperties; public long VariantProperties { get { return _variantProperties; } set { _variantProperties = value; OnPropertyChanged("VariantProperties"); } } private bool _isTimeZoneNeutral; public bool IsTimeZoneNeutral { get { return _isTimeZoneNeutral; } set { _isTimeZoneNeutral = value; OnPropertyChanged("IsTimeZoneNeutral"); } } #endregion } }
Silverlight アプリケーション プロジェクトを追加し、XamDayView コントロールを画面に追加
xamSchedule ソリューションに Silverlight アプリケーション プロジェクトを "xamSchedule_SL" と名付け、追加します。その際、Silverlight 4 を選択し、今後のために WCF RIA サービスを有効にします。
MainPage.xaml を開き、ツール ボックスから XamDayView コントロールを選択し、ドラッグします。コントロールの Width, Height が設定されている場合はクリアしてください。
XamDayView コントロールを追加すると、プロジェクトには次のアセンブリが追加されます。
- InfragisticsSL4.v10.3.dll
- InfragisticsSL4.Controls.Schedules.v10.3.dll
次に XamScheduleDataManager, ListScheduleDataConnector コントロールを追加します。
<UserControl x:Class="xamSchedule_SL.MainPage" xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml xmlns:d=http://schemas.microsoft.com/expression/blend/2008 xmlns:mc=http://schemas.openxmlformats.org/markup-compatibility/2006 mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" xmlns:ig="http://schemas.infragistics.com/xaml"> <Grid x:Name="LayoutRoot" Background="White"> <ig:XamDayView Name="xamDayView1" /> <ig:XamScheduleDataManager Name="xamScheduleDataManager1" /> <ig:ListScheduleDataConnector Name="listScheduleDataConnector1" /> </Grid> </UserControl>
追加した XamDayView コントロールの DataManager プロパティに先ほど追加した XamScheduleDataManager コントロールをバインドします。
<ig:XamDayView Name="xamDayView1" DataManager="{Binding ElementName=xamScheduleDataManager1}" />
XamScheduleDataManager ではデータ コネクターを指定します。
<ig:XamScheduleDataManager Name="xamScheduleDataManager1" DataConnector="{Binding ElementName=listScheduleDataConnector1}"/>
これで、ビュー - データ マネージャー - データ コネクター の接続が完了しました。
画面に表示させるデータをコードで生成する
今回はコード ビハインドを用いて画面に表示するデータを生成します。まず、先ほど作成した ScheduleDataViewModel プロジェクトを xamSchedule_SL プロジェクトに追加します。追加する場合は、プロジェクトの [参照設定] のコンテキスト メニューから [参照の追加] を選択します。その後表示される追加ダイアログで [プロジェクト] タブを選択し、ScheduleDataViewModel を追加します。
次に xamSchedule_SL プロジェクトに新しいフォルダーを "ViewModels" と名付け作成し、
そのフォルダー内に新しいクラスを "MainPageViewModel.cs" と名付け、作成します。
MainPageViewModel.cs クラスでは、先ほど参照を追加した ScheduleDataViewModel を用いて、Resource, ResourceCalendar, Appointment を格納するプロパティを作成します。また、現在のユーザーを示す、CurrentUserId プロパティを実装します。
using System; using ScheduleDataViewModel; using System.Collections.ObjectModel; namespace xamSchedule_SL.ViewModels { public class MainPageViewModel : ViewModelBase { #region プロパティ private ObservableCollection<ResourceInfo> _resources; public ObservableCollection<ResourceInfo> Resources { get { return _resources; } set { _resources = value; OnPropertyChanged("Resources"); } } private ObservableCollection<ResourceCalendarInfo> _resourceCalendars; public ObservableCollection<ResourceCalendarInfo> ResourceCalendars { get { return _resourceCalendars; } set { _resourceCalendars = value; OnPropertyChanged("ResourceCalendars"); } } private ObservableCollection<AppointmentInfo> _appointments; public ObservableCollection<AppointmentInfo> Appointments { get { return _appointments; } set { _appointments = value; OnPropertyChanged("Appointments"); } } private string _currentUserId; public string CurrentUserId { get { return _currentUserId; } set { _currentUserId = value; OnPropertyChanged("CurrentUserId"); } } #endregion // その他の実装 } }
次にコンストラクタを実装します。今回の記事ではコードでデータを作成します。ここでの注意点としては、予定の開始時刻、終了時刻は世界標準時であるということです。
using System; using ScheduleDataViewModel; using System.Collections.ObjectModel; namespace xamSchedule_SL.ViewModels { public class MainPageViewModel : ViewModelBase { #region プロパティ // 省略 #endregion #region コンストラクタ public MainPageViewModel() { // リソースを作成 Resources = new ObservableCollection<ResourceInfo>(); Resources.Add(new ResourceInfo() { Id = "R0001", PrimaryResourceCalendarId = "C0001" }); // リソースカレンダーを作成 ResourceCalendars = new ObservableCollection<ResourceCalendarInfo>(); ResourceCalendars.Add(new ResourceCalendarInfo() { Id = "C0001", OwningResourceId = "R0001" }); // 予定を追加 Appointments = new ObservableCollection<AppointmentInfo>(); Appointments.Add(new AppointmentInfo() { Id = "A0001", OwningCalendarId = "C0001", OwningResourceId = "R0001", Start = DateTime.Now.ToUniversalTime(), End = DateTime.Now.AddHours(2).ToUniversalTime(), }); // 現在のユーザー ID を指定 this.CurrentUserId = this.Resources[0].Id; } #endregion } }
この MainPageViewModel は実行時に MainPage の DataContext に格納します。そのため、MainPage.xaml.cs を開き、コンストラクタで MainPageViewModel のインスタンスを作成します。
using System.Windows.Controls; using xamSchedule_SL.ViewModels; namespace xamSchedule_SL { public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); MainPageViewModel vm = new MainPageViewModel(); this.DataContext = vm; } } }
これでスケジュールに表示させるためのデータの作成が完了しました。
データのマッピングを行い、予定を画面に表示させる
XamDataManager コントロールでは現在のリソース、もしくはリソース ID を CurrentUser, CurrentUserId プロパティに割り当てる必要があります。MainPage.xaml を開き、MainPageViewModel.CurrentUserId を割り当てます。
<ig:XamScheduleDataManager Name="xamScheduleDataManager1" DataConnector="{Binding ElementName=listScheduleDataConnector1}" CurrentUserId="{Binding CurrentUserId}" />
さらに、データ コネクター コントロールでは各種情報のコレクションを設定し、それぞれのプロパティをマッピングする必要があります。割り当てるデータのエンティティ名を標準のものにしている場合は、UseDefaultMappings プロパティを True と設定することであらかじめ定義されたマッピングが設定されます。
<ig:ListScheduleDataConnector Name="listScheduleDataConnector1" AppointmentItemsSource="{Binding Appointments}" ResourceCalendarItemsSource="{Binding ResourceCalendars}" ResourceItemsSource="{Binding Resources}" > <ig:ListScheduleDataConnector.AppointmentPropertyMappings> <ig:AppointmentPropertyMappingCollection UseDefaultMappings="True" /> </ig:ListScheduleDataConnector.AppointmentPropertyMappings> <ig:ListScheduleDataConnector.ResourceCalendarPropertyMappings> <ig:ResourceCalendarPropertyMappingCollection UseDefaultMappings="True" /> </ig:ListScheduleDataConnector.ResourceCalendarPropertyMappings> <ig:ListScheduleDataConnector.ResourcePropertyMappings> <ig:ResourcePropertyMappingCollection UseDefaultMappings="True" /> </ig:ListScheduleDataConnector.ResourcePropertyMappings> </ig:ListScheduleDataConnector>
全ての XAML は下記のとおりです。
<UserControl x:Class="xamSchedule_SL.MainPage" xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml xmlns:d=http://schemas.microsoft.com/expression/blend/2008 xmlns:mc=http://schemas.openxmlformats.org/markup-compatibility/2006 mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" xmlns:ig="http://schemas.infragistics.com/xaml"> <Grid x:Name="LayoutRoot" Background="White"> <ig:XamDayView Name="xamDayView1" DataManager="{Binding ElementName=xamScheduleDataManager1}" /> <ig:XamScheduleDataManager Name="xamScheduleDataManager1" DataConnector="{Binding ElementName=listScheduleDataConnector1}" CurrentUserId="{Binding CurrentUserId}" /> <ig:ListScheduleDataConnector Name="listScheduleDataConnector1" AppointmentItemsSource="{Binding Appointments}" ResourceCalendarItemsSource="{Binding ResourceCalendars}" ResourceItemsSource="{Binding Resources}" > <ig:ListScheduleDataConnector.AppointmentPropertyMappings> <ig:AppointmentPropertyMappingCollection UseDefaultMappings="True" /> </ig:ListScheduleDataConnector.AppointmentPropertyMappings> <ig:ListScheduleDataConnector.ResourceCalendarPropertyMappings> <ig:ResourceCalendarPropertyMappingCollection UseDefaultMappings="True" /> </ig:ListScheduleDataConnector.ResourceCalendarPropertyMappings> <ig:ListScheduleDataConnector.ResourcePropertyMappings> <ig:ResourcePropertyMappingCollection UseDefaultMappings="True" /> </ig:ListScheduleDataConnector.ResourcePropertyMappings> </ig:ListScheduleDataConnector> </Grid> </UserControl>
ソリューションを実行すると、最初にタイムゾーンを指定するダイアログが表示されます。
タイムゾーンを選択すると、コードで設定した予定が表示されます。
まとめ
今回は Silverlight クラス ライブラリに NetAdvantage for Silverlight LOB のスケジュール データ 構造を実装し、画面にデータを表示させました。ビューについては、XamDayView 以外にも XamMonthView, XamScheduleView を用いて全く同じデータを別の形式で表現することが可能になります。Silverlight においてスケジュール管理を行うアプリケーション開発を検討されている場合はぜひ一度試されてはいかがでしょうか。次回は同じデータ構造を WPF で表現する方法についてお伝えします。