すぐに使えるWPF/SilverlightのUI開発パターン

ウィザード型アプリケーション

グレープシティ株式会社 八巻 雄哉
2012/04/06
Page1 Page2

 ウィザード型アプリケーションとは、インストーラに代表されるような、ステップごとに画面を切り替えながら順を追って進めていくタイプのユーザー・インターフェイス(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]タブの[アプリケーションのブラウザー外実行を有効にする]をチェックし、[ブラウザー外実行の設定]ボタンを押す。

[ブラウザー外実行の設定]ダイアログで、次のとおりにフィールドの値を設定する。

プロパティ
493
高さ 378

[ブラウザー外での実行時に昇格された信頼を要求する]をチェックし、[OK]ボタンを押す。


 INDEX
  すぐに使えるWPF/SilverlightのUI開発パターン
  ウィザード型アプリケーション
  1.ViewModelの作成/ブラウザ外実行の設定(Siliverlightの場合のみ)
    2.XAMLの特殊な構文/Silverlight固有のXAML機能

インデックス・ページヘ  「すぐに使えるWPF/SilverlightのUI開発パターン」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

注目のテーマ

業務アプリInsider 記事ランキング

本日 月間
ソリューションFLASH