Windows アプリケーションで画面遷移をしてみよう(その2)
掲示板などでよく方法が聞かれる、インストールウィザードの様な画面遷移を行います。
一言でいうと、紙芝居のような感じです。
紙芝居は、おじさんが持っている枠は物語の始まりから終わりまで変わりませんが、
途中途中で中の紙が変わりますよね。
枠 = Form、紙 = UserControl として、考えるとわかり易いと思います。
ここでは、詳細は説明は省きますが、コードを読んでいただくなり、
サンプルをダウンロードしていただくなりすれば、どのような事をやっているかお解かりになるかと思います。
※解説に用いた Visual Studio 2005 の Edition は、Team System Edition です。
※Windows Form デザイナで生成されたコードについては省略しています。
サンプルのダウンロード
■実行イメージ
プログラムが開始されると、最初に以下の画面が出現します。

[お名前]を入力して、タブキーで Focus 移動すると、[次へ]ボタンが有効になります。
次に、居住地域の選択画面が現れます。
ComboBox より値を選択して、タブキーで Focus 移動すると、[次へ]ボタンが有効になります。

次に、メールアドレスの入力画面が現れます。
[メールアドレス]を入力して、タブキーで Focus 移動すると、[次へ]ボタンが有効になります。
※メールアドレスっぽい文字列でないと、エラーメッセージが出力されるようになっています。

最後に、完了画面が現れます。
おめでとうございます!えっちなサイトにユーザー登録されてしまいました!
※嘘です。画面に今まで入力してきた値を表示しているだけですので、ご安心下さい。

■コード
紙芝居のおじさんが持っている枠である Wizard です。
いくつも画面が登場していますが、実際は Form オブジェクトはコレだけです。
Public Class Wizard ' この画面で管理している Panel Private m_panels As List(Of PanelUserControlBase) = New List(Of PanelUserControlBase) ' 現在表示している Panel の Index Private m_currentPos As Integer = -1 ' 現在表示している Panel Private m_currentPanel As PanelUserControlBase ' 最後のパネルのインデックス Private Const LAST_PANEL_INDEX As Integer = 3 ' 画面遷移(次へ)を許すか否か Private m_allowNext As Boolean = False ' 画面遷移(戻る)を許すか否か Private m_allowBack As Boolean = False ' 画面遷移(次へ)を許すか否か Public Property AllowNext() As Boolean Get Return Me.m_allowNext End Get Set(ByVal value As Boolean) If value AndAlso LAST_PANEL_INDEX = Me.m_currentPos Then Return End If Me.m_allowNext = value Me.btnNext.Enabled = value End Set End Property ' 画面遷移(戻る)を許すか否か Public Property AllowBack() As Boolean Get Return Me.m_allowBack End Get Set(ByVal value As Boolean) If value AndAlso 0 = Me.m_currentPos Then Return End If Me.m_allowBack = value Me.btnBack.Enabled = value End Set End Property ' 自画面がロードされた時のイベント Private Sub Wizard_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim pnlLocation As System.Drawing.Point = New System.Drawing.Point(12, 12) ' 1 枚目 Dim pnl1 As Panel1 = New Panel1 pnl1.UserInfo = New UserInformation pnl1.Location = pnlLocation Me.m_panels.Add(pnl1) ' 2 枚目 Dim pnl2 As Panel2 = New Panel2 pnl2.Location = pnlLocation Me.m_panels.Add(pnl2) ' 3 枚目 Dim pnl3 As Panel3 = New Panel3 pnl3.Location = pnlLocation Me.m_panels.Add(pnl3) ' 4 枚目 Dim pnl4 As Panel4 = New Panel4 pnl4.Location = pnlLocation Me.m_panels.Add(pnl4) ' 1 枚目を表示する Me.m_currentPos = 0 Me.m_currentPanel = pnl1 Me.Controls.Add(Me.m_currentPanel) Me.AllowNext = False Me.AllowBack = False End Sub ' 戻るボタンのイベント Private Sub btnBack_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnBack.Click If Me.m_currentPos <= 0 Then Return Me.m_currentPos -= 1 Dim nextPanel As PanelUserControlBase = Me.m_panels(Me.m_currentPos) If Not Me.changePanel(nextPanel) Then Return Select Case Me.m_currentPos Case 0 Me.AllowNext = True Me.AllowBack = False Case LAST_PANEL_INDEX Me.AllowNext = False Me.AllowBack = False Case Else Me.AllowNext = True Me.AllowBack = True End Select End Sub ' 次へボタンのイベント Private Sub btnNext_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnNext.Click If Not Me.m_allowNext Then Return If Me.m_currentPos = Me.m_panels.Count - 1 Then Return Me.m_currentPos += 1 Dim nextPanel As PanelUserControlBase = Me.m_panels(Me.m_currentPos) If Not Me.changePanel(nextPanel) Then Return If nextPanel.HasValue Then Me.AllowNext = True Else Me.AllowNext = False End If Select Case Me.m_currentPos Case 0, LAST_PANEL_INDEX Me.AllowBack = False Case Else Me.AllowBack = True End Select End Sub ' パネルをとっかえるメソッド Private Function changePanel(ByVal nextPanel As PanelUserControlBase) As Boolean If Me.m_currentPanel Is nextPanel Then Return False Dim userInfo As UserInformation = Me.m_currentPanel.UserInfo ' 現在の UserInformation オブジェクトの参照を取って... Me.Controls.Remove(Me.m_currentPanel) nextPanel.UserInfo = userInfo ' 次の画面に渡す Me.m_currentPanel = nextPanel Me.Controls.Add(nextPanel) Return True End Function ' 終了ボタンのイベント Private Sub btnClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClose.Click If Me.m_currentPos <> LAST_PANEL_INDEX Then Dim dresult As DialogResult = _ MessageBox.Show("登録の途中です。本当に終了しますか", _ Me.Text, _ MessageBoxButtons.OKCancel, _ MessageBoxIcon.Question) If dresult = Windows.Forms.DialogResult.Cancel Then Return End If End If Me.Close() End Sub ' 画面を閉じる時のイベント Private Sub Wizard_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing For i As Integer = 0 To Me.m_panels.Count - 1 Me.m_panels(i).Dispose() Next Application.Exit() End Sub End Class
このシステムで、ユーザーに入力させる情報を保持するクラスです。
画面から画面へのこのオブジェクトの引渡しは、Wizard クラスの changePanel メソッドの中で行っています。
Public Class UserInformation '名前 Private m_name As String '居住地域 Private m_address As String 'メールアドレス Private m_mailAddress As String Public Sub New() End Sub Public Property Name() As String Get Return Me.m_name End Get Set(ByVal value As String) Me.m_name = value End Set End Property Public Property Address() As String Get Return Me.m_address End Get Set(ByVal value As String) Me.m_address = value End Set End Property Public Property MailAddress() As String Get Return Me.m_mailAddress End Get Set(ByVal value As String) Me.m_mailAddress = value End Set End Property End Class
紙芝居の紙自体の基底の抽象クラスです。
Public MustInherit Class PanelUserControlBase ' ユーザーの情報オブジェクト Protected m_userInfo As UserInformation ' 自身の親コントロール(Wizard クラス) Private ReadOnly Property myParent() As Wizard Get Return DirectCast(Me.Parent, Wizard) End Get End Property ' 親に画面遷移(次へ)を許すか否か Protected Property Parent_AllowNext() As Boolean Get Return Me.myParent.AllowNext End Get Set(ByVal value As Boolean) Me.myParent.AllowNext = value End Set End Property ' 親に画面遷移(戻る)を許すか否か Protected Property Parent_AllowBack() As Boolean Get Return Me.myParent.AllowBack End Get Set(ByVal value As Boolean) Me.myParent.AllowBack = value End Set End Property ' ユーザーの情報オブジェクト アクセサ・ミューテータ Public Property UserInfo() As UserInformation Get Return Me.m_userInfo End Get Set(ByVal value As UserInformation) Me.m_userInfo = value End Set End Property ' 自身が面倒を見るべき情報を保持しているか否か Public MustOverride ReadOnly Property HasValue() As Boolean End Class
1 枚目の紙芝居です。
Public Class Panel1 ' このパネルがロードされた時のイベント Private Sub Panel1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Me.TextBox1.Focus() End Sub ' TextBox から Focus が離れた時のイベント Private Sub TextBox1_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.Leave If Me.TextBox1.Text <> "" Then MyBase.m_userInfo.Name = Me.TextBox1.Text MyBase.Parent_AllowNext = True Else MyBase.Parent_AllowNext = False End If End Sub ' 自身が面倒を見るべき情報(名前)を保持しているか否か Public Overrides ReadOnly Property HasValue() As Boolean Get Return Me.TextBox1.Text <> "" End Get End Property End Class
2 枚目の紙芝居です。
Public Class Panel2 ' このパネルがロードされた時のイベント Private Sub Panel2_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load With Me.ComboBox1.Items .Clear() .Add("北海道") .Add("東北") .Add("首都圏") .Add("中部") .Add("関西") .Add("中国・四国") .Add("九州・沖縄") End With Me.ComboBox1.SelectedIndex = -1 Me.ComboBox1.Focus() End Sub ' TextBox から Focus が離れた時のイベント Private Sub ComboBox1_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles ComboBox1.Leave If Me.ComboBox1.Text <> "" Then MyBase.m_userInfo.Address = Me.ComboBox1.Text MyBase.Parent_AllowNext = True Else MyBase.Parent_AllowNext = False End If End Sub ' 自身が面倒を見るべき情報(居住地域)を保持しているか否か Public Overrides ReadOnly Property HasValue() As Boolean Get Return Me.ComboBox1.Text <> "" End Get End Property End Class
3 枚目の紙芝居です。
Public Class Panel3 ' このパネルがロードされた時のイベント Private Sub Panel3_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Me.TextBox1.Focus() End Sub ' TextBox から Focus が離れた時のイベント Private Sub TextBox1_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.Leave If Me.TextBox1.Text <> "" Then Dim inputText As String = Me.TextBox1.Text ' 正規表現で、メールアドレスっぽい文字列かどうかのチェックを行う Dim result As Boolean = System.Text.RegularExpressions.Regex.IsMatch( _ inputText, _ "\b[-\w.]+@[-\w.]+\.[-\w]+\b") If Not result Then ' NG だったら、エラーメッセージを出して処理終了 MessageBox.Show("入力された文字はメールアドレスじゃないみたいです", _ "error", _ MessageBoxButtons.OK, _ MessageBoxIcon.Exclamation) Me.TextBox1.Focus() MyBase.Parent_AllowNext = False Return End If MyBase.m_userInfo.MailAddress = Me.TextBox1.Text MyBase.Parent_AllowNext = True Else MyBase.Parent_AllowNext = False End If End Sub ' 自身が面倒を見るべき情報(メールアドレス)を保持しているか否か Public Overrides ReadOnly Property HasValue() As Boolean Get Return Me.TextBox1.Text <> "" End Get End Property End Class
4 枚目の紙芝居です。
Public Class Panel4 ' このパネルがロードされた時のイベント Private Sub Panel4_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load If Me.m_userInfo Is Nothing Then Return Dim sr As System.Text.StringBuilder = New System.Text.StringBuilder sr.AppendLine("お名前:" + Me.m_userInfo.Name) sr.AppendLine("居住地域:" + Me.m_userInfo.Address) sr.AppendLine("メールアドレス:" + Me.m_userInfo.MailAddress) sr.AppendLine() sr.AppendLine("えっちなサイトにユーザー登録しました!!") Me.Label1.Text = sr.ToString() End Sub ' 自身が面倒を見るべき情報を保持しているか否か→ここでは面倒をみるものがないので常に False Public Overrides ReadOnly Property HasValue() As Boolean Get Return False End Get End Property End Class
■ひとこと
PanelUserControlBase が抽象クラスになっているので、デザイナで開けないと思います。
デザイナで開くためには、一時的に 以下の作業を行ってください。
PanelUserControlBase の MustOverride の Property 及び、派生クラス(Panel1...Panel4)Overrides の Property をコメントアウトして下さい。
Wizard で、該当 Property を使っている部分もコメントアウトして下さい。
PanelUserControlBase の MustInherit 属性もはずして下さい。
その後、リビルドすると、デザイナで表示できると思います。