|
すぐに使えるWPF/SilverlightのUI開発パターン
ウィザード型アプリケーション
グレープシティ株式会社 八巻 雄哉
2012/04/06 |
|
ウィザード型アプリケーションとは、インストーラに代表されるような、ステップごとに画面を切り替えながら順を追って進めていくタイプのユーザー・インターフェイス(UI)を持ったアプリケーションのことである。ASP.NET Webフォームではずばり「Wizard」という名前のコントロールが存在するが、WPF/Silverlightではウィザードのための直接的な機能は提供されていない。
WPF/Silverlightでウィザード型アプリケーションを作成しようと思った場合、その実現方法は何パターンか存在する。真っ先に思いつくのは、「Frame」と「Page」といったナビゲーション・フレームワークの利用だが、この方法はデータ・バインディングを活用しづらくなるという欠点がある。また、ブラウザ外実行ではないSilverlightアプリケーションでは、ディープ・リンクによってエンド・ユーザーが途中のページを直接表示できてしまうという問題もある。
そこで本稿では、ナビゲーション・フレームワークを使わない、データ・バインディングを積極的に活用した形でのウィザード型アプリケーションの作成方法を紹介する。なお、データ・バインディングそのものについては本稿の趣旨から外れるため、詳細については「データの表示と入力に必要な知識」を参照してほしい。
※本稿は画面の作成方法に主眼を置いたものであり、ビジネス・ロジックは含まれていません。
※WPFではFontSizeプロパティを「11pt」にするといったようにポイント値を設定できますが、Silverlightと共通で説明するためにピクセル換算の値で説明しています。
※WPFではコントロールにアクセス・キーを設定することができますが、この機能はSilverlightで使用できないため、説明ではアクセス・キーを設定していません。
※Visual Studioはバージョン「2010 SP1」に「Silverlight 5 Tools for Visual Studio 2010 SP1」をインストールしたものを用いています。
|
最終的な完成画面は次の図(WPF版とSilverlight版)のようになる。
|
ウィザード型「WPF」アプリケーションの完成画面 |
|
ウィザード型「Silverlight」アプリケーションの完成画面 |
さっそく、開発を始めよう。以下では基本的に箇条書きスタイルで、開発手順を示していく。なお、「Microsoft patterns & practices: Prism」に含まれるライブラリを使用するため、あらかじめ以下のサイトからPrismのインストーラをダウンロードしてインストールしておいてほしい。
■プロジェクトの新規作成
Visual Studio 2010(Visual BasicまたはC#)で、「WizardSample」という名前の新しいWPFアプリケーション、またはSilverlightアプリケーションのプロジェクトを作成する。なお、WPFはバージョン4、Silverlightはバージョン5で開発を行う。
●参照の追加
(メニューバーの)[プロジェクト]メニューの[参照の追加]をクリックする。
[参照の追加]ダイアログで、[参照]をクリックし、WPFとSilverlightでそれぞれ以下のPrismのアセンブリを選択する。次に、[OK]ボタンを押す。
- WPF:"\Bin\Desktop\Microsoft.Practices.Prism.dll"
- Silverlight:"\Bin\Silverlight\Microsoft.Practices.Prism.dll"
■ViewModelの作成
[ソリューション・エクスプローラー]でWizardSampleプロジェクトを選択し、[プロジェクト]メニューの[新しいフォルダー]をクリックする。
表示された新しいフォルダに「ViewModel」という名前を付ける。
●PageViewModelBaseクラスの作成
[ソリューション・エクスプローラー]で、ViewModelフォルダを選択し、[プロジェクト]メニューの[クラスの追加]をクリックする。
[新しい項目の追加]ダイアログで、[名前]ボックスに「PageViewModelBase」と入力し、[追加]ボタンをクリックする。
PageViewModelBase.vb/PageViewModelBase e.csファイルに次のコードを記述する。
Imports Microsoft.Practices.Prism.ViewModel
Namespace ViewModel
Public MustInherit Class PageViewModelBase
Inherits NotificationObject
MustOverride ReadOnly Property DisplayName As String
End Class
End Namespace |
using Microsoft.Practices.Prism.ViewModel;
namespace WizardSample.ViewModel
{
public abstract class PageViewModelBase : NotificationObject
{
public abstract string DisplayName { get; }
}
} |
|
PageViewModelBaseクラスのコード内容(上:VB、下:C#) |
●Page1ViewModelクラスの作成
[ソリューション・エクスプローラー]で、ViewModelフォルダを選択し、[プロジェクト]メニューの[クラスの追加]をクリックする。
[新しい項目の追加]ダイアログで、[名前]ボックスに「Page1ViewModel」と入力し、[追加]ボタンをクリックする。
Page1ViewModel.vb/Page1ViewModel.csファイルに、次のコードを記述する。
Namespace ViewModel
Public Class Page1ViewModel
Inherits PageViewModelBase
Public Overrides ReadOnly Property DisplayName As String
Get
Return "セットアップウィザードへようこそ"
End Get
End Property
End Class
End Namespace |
namespace WizardSample.ViewModel
{
public class Page1ViewModel : PageViewModelBase
{
public override string DisplayName
{
get { return "セットアップウィザードへようこそ"; }
}
}
} |
|
Page1ViewModelクラスのコード内容(上:VB、下:C#) |
●Page2ViewModelクラスの作成
[ソリューション・エクスプローラー]で、ViewModelフォルダを選択し、[プロジェクト]メニューの[クラスの追加]をクリックする。
[新しい項目の追加]ダイアログで、[名前]ボックスに「Page2ViewModel」と入力し、[追加]ボタンをクリックする。
Page2ViewModel.vb/Page2ViewModel.csファイルに、次のコードを記述する。
Namespace ViewModel
Public Class Page2ViewModel
Inherits PageViewModelBase
Public Overrides ReadOnly Property DisplayName As String
Get
Return "インストールの確認"
End Get
End Property
End Class
End Namespace |
namespace WizardSample.ViewModel
{
public class Page2ViewModel : PageViewModelBase
{
public override string DisplayName
{
get { return "インストールの確認"; }
}
}
} |
|
Page2ViewModelクラスのコード内容(上:VB、下:C#) |
●Page3ViewModelクラスの作成
[ソリューション・エクスプローラー]で、ViewModelフォルダを選択し、[プロジェクト]メニューの[クラスの追加]をクリックする。
[新しい項目の追加]ダイアログで、[名前]ボックスに「Page3ViewModel」と入力し、[追加]ボタンをクリックする。
Page3ViewModel.vb/Page3ViewModel.csファイルに、次のコードを記述する。
Namespace ViewModel
Public Class Page3ViewModel
Inherits PageViewModelBase
Public Overrides ReadOnly Property DisplayName As String
Get
Return "インストールが完了しました"
End Get
End Property
End Class
End Namespace |
namespace WizardSample.ViewModel
{
public class Page3ViewModel : PageViewModelBase
{
public override string DisplayName
{
get { return "インストールが完了しました"; }
}
}
} |
|
Page3ViewModelクラスのコード内容(上:VB、下:C#) |
●MainViewModelクラスの作成
[ソリューション・エクスプローラー]で、ViewModelフォルダを選択し、[プロジェクト]メニューの[クラスの追加]をクリックする。
[新しい項目の追加]ダイアログで、[名前]ボックスに「MainViewModel」と入力し、[追加]ボタンをクリックする。
MainViewModel.vb/MainViewModel.csファイルに、次のコードを記述する。
Imports Microsoft.Practices.Prism.Commands
Imports Microsoft.Practices.Prism.ViewModel
Imports System.Collections.ObjectModel
Namespace ViewModel
Public Class MainViewModel
Inherits NotificationObject
Public Event RequestClose As EventHandler
Public Sub New()
CreatePages()
CurrentPage = Pages(0)
End Sub
Public Property Pages As ReadOnlyCollection(Of PageViewModelBase)
Private _currentPage As PageViewModelBase
Public Property CurrentPage() As PageViewModelBase
Get
Return _currentPage
End Get
Set(ByVal value As PageViewModelBase)
_currentPage = value
RaisePropertyChanged(Function() CurrentPage)
RaisePropertyChanged(Function() NextButtonContent)
MovePreviousCommand.RaiseCanExecuteChanged()
End Set
End Property
Private ReadOnly Property CurrentPageIndex() As Integer
Get
Return Pages.IndexOf(CurrentPage)
End Get
End Property
Public ReadOnly Property NextButtonContent() As String
Get
If CurrentPageIndex = Pages.Count - 1 Then
Return "閉じる"
Else
Return "次へ >"
End If
End Get
End Property
Private _movePreviousCommand As DelegateCommand
Public ReadOnly Property MovePreviousCommand() As DelegateCommand
Get
If _movePreviousCommand Is Nothing Then
_movePreviousCommand = New DelegateCommand(
Sub() MoveToPreviousPage(),
Function() CanMoveToPreviousPage)
End If
Return _movePreviousCommand
End Get
End Property
Private ReadOnly Property CanMoveToPreviousPage() As Boolean
Get
Return 0 < CurrentPageIndex
End Get
End Property
Private Sub MoveToPreviousPage()
CurrentPage = Pages(CurrentPageIndex - 1)
End Sub
Private _moveNextCommand As DelegateCommand
Public ReadOnly Property MoveNextCommand() As DelegateCommand
Get
If _moveNextCommand Is Nothing Then
_moveNextCommand = New DelegateCommand(
Sub() MoveToNextPage())
End If
Return _moveNextCommand
End Get
End Property
Private Sub MoveToNextPage()
If CurrentPageIndex < Pages.Count - 1 Then
CurrentPage = Pages(CurrentPageIndex + 1)
Else
OnRequestClose()
End If
End Sub
Private _cancelCommand As DelegateCommand
Public ReadOnly Property CancelCommand() As DelegateCommand
Get
If _cancelCommand Is Nothing Then
_cancelCommand = New DelegateCommand(
Sub() Cancel())
End If
Return _cancelCommand
End Get
End Property
Private Sub Cancel()
OnRequestClose()
End Sub
Private Sub CreatePages()
Dim pages = New List(Of PageViewModelBase)()
Dim page1Vm = New Page1ViewModel()
Dim page2Vm = New Page2ViewModel()
Dim page3Vm = New Page3ViewModel()
pages.Add(page1Vm)
pages.Add(page2Vm)
pages.Add(page3Vm)
Me.Pages = New ReadOnlyCollection(Of PageViewModelBase)(pages)
End Sub
Private Sub OnRequestClose()
RaiseEvent RequestClose(Me, New EventArgs)
End Sub
End Class
End Namespace |
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.Practices.Prism.Commands;
using Microsoft.Practices.Prism.ViewModel;
namespace WizardSample.ViewModel
{
public class MainViewModel : NotificationObject
{
public event EventHandler RequestClose;
public MainViewModel()
{
CreatePages();
CurrentPage = Pages[0];
}
public ReadOnlyCollection<PageViewModelBase> Pages { get; set; }
PageViewModelBase _currentPage;
public PageViewModelBase CurrentPage
{
get { return _currentPage; }
private set
{
_currentPage = value;
RaisePropertyChanged(() => CurrentPage);
RaisePropertyChanged(() => NextButtonContent);
MovePreviousCommand.RaiseCanExecuteChanged();
}
}
int CurrentPageIndex
{
get
{
return Pages.IndexOf(CurrentPage);
}
}
public string NextButtonContent
{
get
{
if (CurrentPageIndex == Pages.Count - 1)
return "閉じる";
else
return "次へ >";
}
}
DelegateCommand _movePreviousCommand;
public DelegateCommand MovePreviousCommand
{
get
{
if (_movePreviousCommand == null)
_movePreviousCommand = new DelegateCommand(
() => MoveToPreviousPage(),
() => CanMoveToPreviousPage);
return _movePreviousCommand;
}
}
bool CanMoveToPreviousPage
{
get { return 0 < CurrentPageIndex; }
}
void MoveToPreviousPage()
{
CurrentPage = Pages[CurrentPageIndex - 1];
}
DelegateCommand _moveNextCommand;
public DelegateCommand MoveNextCommand
{
get
{
if (_moveNextCommand == null)
_moveNextCommand = new DelegateCommand(
() => MoveToNextPage());
return _moveNextCommand;
}
}
void MoveToNextPage()
{
if (CurrentPageIndex < Pages.Count - 1)
CurrentPage = Pages[CurrentPageIndex + 1];
else
OnRequestClose();
}
DelegateCommand _cancelCommand;
public DelegateCommand CancelCommand
{
get
{
if (_cancelCommand == null)
_cancelCommand = new DelegateCommand(() => Cancel());
return _cancelCommand;
}
}
void Cancel()
{
OnRequestClose();
}
private void CreatePages()
{
var pages = new List<PageViewModelBase>();
var page1Vm = new Page1ViewModel();
var page2Vm = new Page2ViewModel();
var page3Vm = new Page3ViewModel();
pages.Add(page1Vm);
pages.Add(page2Vm);
pages.Add(page3Vm);
this.Pages = new ReadOnlyCollection<PageViewModelBase>(pages);
}
void OnRequestClose()
{
if (RequestClose != null)
RequestClose(this, EventArgs.Empty);
}
}
} |
|
Page3ViewModelクラスのコード内容(上:VB、下:C#) |
■ブラウザ外実行の設定(Silverlightのみ)
今回のサンプルは[キャンセル]ボタン、および[閉じる]ボタンでアプリケーションを終了する仕様であるため、Silverlightアプリケーションはブラウザ外実行の昇格されたアプリケーションに設定する。
[ソリューション・エクスプローラー]で「WizardSample」プロジェクトを選択し、[プロジェクト]メニューの[WizardSample のプロパティ]をクリックする。
[Silverlight]タブの[アプリケーションのブラウザー外実行を有効にする]をチェックし、[ブラウザー外実行の設定]ボタンを押す。
[ブラウザー外実行の設定]ダイアログで、次のとおりにフィールドの値を設定する。
[ブラウザー外での実行時に昇格された信頼を要求する]をチェックし、[OK]ボタンを押す。