DataFormの編集と新規データの追加

2010年8月24日(火)
PROJECT KySS

ロジックコードを記述する

ソリューションエクスプローラ内のMainPage.xamlを展開し、MainPage.xaml.vbをダブルクリックしてコード画面を開きます。

リスト2のようにロジックコードを記述します。

リスト2: ロジックコード(MainPage.xaml.vb)

1<!--//--><![CDATA[// ><!--
2 
3Option Strict On
4 
5//--><!

XML to LINQでXMLを処理するクラスの含まれるSystem.Xml.Linq名前空間をインポートします。ファイルの入出力に関するクラスの含まれるSystem.IO名前空間をインポートします。また、エンティティ クラスのメタデータを定義するために使用される属性クラスを提供する、System.ComponentModel.DataAnnotations名前空間をインポートしておきます。

1<!--//--><![CDATA[// ><!--
2 
3Imports System.Xml.Linq
4Imports System.IO
5Imports System.ComponentModel.DataAnnotations
6 
7//--><!

PersonalInfoクラス内に、「ID」、「氏名」、「年齢」、「性別」、「住所」、「勤務先」のプロパティを定義しておきます。

1<!--//--><![CDATA[// ><!--
2 
3Public Class PersonalInfo
4    Dim IDstr As String
5    Dim ageStr As String
6 
7//--><!

「ID」の値を保持するプロパティに対して、RequiredAttribute 属性(必須項目)を適用します。データが不正な場合はErrorMessageが表示されます。

検証する型に関する情報を提供する、ValidationContextの新しいインスタンスmyValidationContextオブジェクト(検証対象オブジェクトで初期化されている)を生成します。

ValidationContextのパラメータは下記の通りです。

ValidationContext(検証対象オブジェクト,サービスプロバイダー(ここではNothingを指定),検証に関連する値のコレクション(ここではNothingを指定))

検証するメンバのプログラム名をMemberNameプロパティで指定します。ここでは”ID”を指定しています。関連付けられているValidationAttribute 属性の値を検証するValidatorの、ValidatePropertyメソッドで、指定されたプロパティの値が有効であるかどうかを判別します。

01<!--//--><![CDATA[// ><!--
02 
03    <Required(ErrorMessage:="IDは必須です。!")>
04    Public Property ID() As String
05        Get
06            Return IDstr
07        End Get
08        Set(ByVal value As String)
09            If Equals(IDstr, value) Then Return
10            Dim myValidationContext As ValidationContext = New ValidationContext(Me, Nothing, Nothing)
11            myValidationContext.MemberName = "ID"
12            Validator.ValidateProperty(value, myValidationContext)
13            IDstr = value
14        End Set
15    End Property
16    Property 氏名 As String
17 
18//--><!

「年齢」の値を保持するプロパティに対してRangeAttribute 属性(データが指定された数値の範囲内にあるかどうかを検証)を適用しています。データが不正な場合はErrorMessageが表示されます。検証する型に関する情報を提供する、ValidationContextの新しいインスタンスmyValidationContextオブジェクト(検証対象オブジェクトで初期化されている)を生成します。検証するメンバのプログラム名をMemberNameプロパティで指定します。ここでは”年齢”を指定しています。

関連付けられているValidationAttribute 属性の値を検証するValidatorの、ValidatePropertyメソッドで、指定されたプロパティの値が有効であるかどうかを判別します。

01<!--//--><![CDATA[// ><!--
02 
03    <Range(0, 120, ErrorMessage:="年齢の値が不正です。")>
04    Public Property 年齢() As String
05        Get
06            Return ageStr
07        End Get
08        Set(ByVal value As String)
09            If Equals(ageStr, value) Then Return
10            Dim myValidationContext As ValidationContext = New ValidationContext(Me, Nothing, Nothing)
11            myValidationContext.MemberName = "年齢"
12            Validator.ValidateProperty(value, myValidationContext)
13            ageStr = value
14        End Set
15 
16    End Property
17    Property 性別 As String
18    Property 住所 As String
19    Property 勤務先 As String
20End Class
21 
22//--><!

SexListクラス内でString型の新しいリストであるmySexListを宣言しておきます(A)。

String型のリストである、読み取り専用のsexInfoプロパティを定義します。その中で、mySexListオブジェクトにAddメソッドで「男性」、「女性」の項目を追加しておきます。この追加されたmySexListオブジェクトを戻り値とします。この値が、DataFormの「性別」セルにレイアウトされたComboBoxコントロールに表示されます。

■(A)

01<!--//--><![CDATA[// ><!--
02 
03Public Class sexList
04    Dim mySexList As New List(Of String)
05    Public ReadOnly Property sexInfo As List(Of String)
06        Get
07            mySexList.Clear()
08            mySexList.Add("男性")
09            mySexList.Add("女性")
10            Return mySexList
11        End Get
12    End Property
13End Class
14 
15~コード略~
16 
17//--><!

PesonalInfoクラスの新しいリストとして作成するmyPersonalInfoをメンバ変数として宣言します。

1<!--//--><![CDATA[// ><!--
2 
3    Dim myPersonalInfo As New List(Of PersonalInfo)
4    Dim xmldoc As XElement
5    Dim filePath As String
6    Dim selectSex As String
7    Dim no As Boolean = False
8 
9//--><!

■ページが読み込まれた時の処理

DataFormにデータをバインドするDataShowプロシジャを実行します。

1<!--//--><![CDATA[// ><!--
2 
3    Private Sub MainPage_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
4        DataShow()
5    End Sub
6 
7//--><!

■DataFormにデータをバインドする処理

IsRunningOutOfBrowserプロパティでアプリケーションがブラウザ外で実行されているかどうかをチェックし、HasElevatedPermissionsプロパティで、アプリケーションが高い信頼性のあるブラウザ外で動作しているかをチェックします。

ブラウザ外で実行されず、高い信頼性に基づいていない場合は、メッセージボックスを表示します。

ブラウザ外で実行され、高い信頼性に基づいている場合は、以下の処理を実行します。

Path.CombineメソッドでMyDocumentsフォルダのパスと、personalInfo_ID_sex_2.xmlのファイル名を連結し、変数filePathに格納します。MyDocumentsフォルダ内にpersonalInfo_ID_sex_2.xmlというファイルがない場合は、メッセージボックスを表示します。

新しいStreamReaderのインスタンスである、readerオブジェクトを生成します。ReadToEndメソッドでファイルの内容を読み取ります。読み取った内容を変数xmlDataに格納します。StreamReaderを閉じます。StreamReaderクラスは、標準テキストファイルから情報の行を読み込みます。つまり、読み込み可能なファイルは、テキスト形式のファイルに限られます。

XElement.Parseメソッドで、xmlDataに格納されたXML情報を読み取ります。ParseメソッドはXMLを格納した文字列からXElementを読み取ります。読み取ったXML情報から、要素のコレクションを取得するクエリ(query)を定義します。myPersonalInfoオブジェクト内をClearメソッドで一度クリアしておきます。このコードを記述しておかないとデータの追加や編集保存を繰り返しているとエラーが発生する場合があります。

queryを実行します。クエリコレクション内を変数resultで反復処理しながら、以下の処理を実行します。

PersonalInfoクラスのリストとして作成した、myPersonalInfoオブジェクトのAddメソッドで、PersonalInfoクラスの各プロパティ(「ID」、「氏名」、「年齢」、「性別」、「住所」、「勤務先」)に、XML要素(、、、、、)の値を追加します。DataFormコントロールのItemsSourceプロパティにmyPersonalInfoオブジェクトを指定します。DataFormコントロールにXMLデータがバインドされます。

01<!--//--><![CDATA[// ><!--
02 
03    Sub DataShow()
04        If App.Current.IsRunningOutOfBrowser = False And App.Current.HasElevatedPermissions = False Then
05            MessageBox.Show("TrustedモードのOut-Of-Browserで実行してください。")
06            Exit Sub
07        Else
08            filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "personalInfo_ID_sex_2.xml")
09            Dim reader As StreamReader = New StreamReader(filePath, System.Text.Encoding.GetEncoding("utf-8"))
10            Dim xmlData As String = reader.ReadToEnd
11            reader.Close()
12            xmldoc = XElement.Parse(xmlData)
13            Dim query = From c In xmldoc.Descendants("情報") Select c
14            myPersonalInfo.Clear()
15            For Each result In query
16                With myPersonalInfo
17                    .Add(New PersonalInfo With {.ID = result.Element("ID").Value,
18                        .氏名 = result.Element("氏名").Value,
19                        .年齢 = result.Element("年齢").Value,
20                        .性別 = result.Element("性別").Value,
21                        .住所 = result.Element("住所").Value,
22                        .勤務先 = result.Element("勤務先").Value})
23                End With
24            Next
25            DataForm1.ItemsSource = myPersonalInfo
26        End If
27    End Sub
28 
29//--><!

■データの新規追加アイコン[+]をクリックしたときの処理

データの修正か、追加保存かを判別するメンバ変数noにTrueを指定します。noがTrueの場合はデータの追加保存を意味します。DataFormのカレントアイテムのインデックを取得する、CurrentItemプロパティに1を指定します。一時的に2ページ目(インデックスは0から開始のため)を選択させます。この処理を記述しておかなければ、新規にデータを追加する[+]アイコンをクリックした際、ツールバーの使用が不可になってしまいます。これを防ぐため、DataFormの2ページ目を一時的に選択させます。

01<!--//--><![CDATA[// ><!--
02 
03    Private Sub DataForm1_AddingNewItem(ByVal sender As Object, ByVal e As System.Windows.Controls.DataFormAddingNewItemEventArgs) Handles DataForm1.AddingNewItem
04        no = True
05        DataForm1.CurrentIndex = 1
06        AddSave.IsEnabled = True
07        EditSave.IsEnabled = False
08    End Sub
09 
10//--><!

■ComboBoxから項目が選択された時の処理

FindNameInContentメソッドで、指定された名前(mySex)を持つComboBoxオブジェクトから、選択された項目をメンバ変数selectSexに格納しておきます。

1<!--//--><![CDATA[// ><!--
2 
3    Private Sub ComboBox_SelectionChanged(ByVal sender As Object, ByVal e As SelectionChangedEventArgs)
4        selectSex = DirectCast(DataForm1.FindNameInContent("mySex"), ComboBox).SelectedItem.ToString
5    End Sub
6 
7//--><!

■[編集保存]ボタンがクリックされた時の処理

メンバ変数noの値がTrue(追加保存の場合)のときに、[編集保存]ボタンがクリックされた場合は、メッセージを表示します。

DataFormのカレントアイテムのインデックスを変数myIndexに格納します。

FindNameInContentメソッドで、指定された名前をもつDataForm内のTextBoxを参照します。「年齢」に入力された値が0より小さく120より大きい場合は警告メッセージを表示します。

変数myIndexに該当する要素の各子要素を参照します。Valueプロパティで、参照した各要素の値に、DataForm内のTextBoxの値を指定します。

「ID」の欄が空白でない場合は、ファイル名と文字コードで初期化された新しいStreamWriterのインスタンスである、writerオブジェクトを生成します。Writeメソッドで、編集修正されたXMLデータ(xmldoc.ToString)をストリームに書き込みます。StreamWriterを閉じます。

01<!--//--><![CDATA[// ><!--
02 
03    Private Sub EditSave_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles EditSave.Click
04        Try
05            If no = True Then
06                MessageBox.Show("追加保存ボタンをクリックしてください。")
07                Exit Sub
08            End If
09 
10            Dim myIndex As Integer = DataForm1.CurrentIndex
11            Dim IDdata As TextBox = DirectCast(DataForm1.FindNameInContent("myID"), TextBox)
12            Dim nameData As TextBox = DirectCast(DataForm1.FindNameInContent("myName"), TextBox)
13            Dim ageData As TextBox = DirectCast(DataForm1.FindNameInContent("myAge"), TextBox)
14            Dim addressData As TextBox = DirectCast(DataForm1.FindNameInContent("myAddress"), TextBox)
15            Dim companyData As TextBox = DirectCast(DataForm1.FindNameInContent("myCompany"), TextBox)
16 
17            If CInt(ageData.Text) < 0 OrElse CInt(ageData.Text) > 120 Then
18                MessageBox.Show("年齢が不正です。")
19                Exit Sub
20            End If
21 
22            Dim IDQuery = xmldoc.Descendants("情報")(myIndex).<ID>
23            IDQuery.Value = IDdata.Text
24            Dim nameQuery = xmldoc.Descendants("情報")(myIndex).<氏名>
25            nameQuery.Value = nameData.Text
26            Dim ageQuery = xmldoc.Descendants("情報")(myIndex).<年齢>
27            ageQuery.Value = ageData.Text
28            Dim sexQuery = xmldoc.Descendants("情報")(myIndex).<性別>
29            sexQuery.Value = selectSex
30            Dim addressQuery = xmldoc.Descendants("情報")(myIndex).<住所>
31            addressQuery.Value = addressData.Text
32            Dim companyQuery = xmldoc.Descendants("情報")(myIndex).<勤務先>
33            companyQuery.Value = companyData.Text
34            If IDdata.Text <> String.Empty Then
35                Dim writer As StreamWriter = New StreamWriter(filePath, False, System.Text.Encoding.GetEncoding("utf-8"))
36                writer.Flush()
37                writer.Write(xmldoc.ToString)
38                writer.Close()
39                MessageBox.Show("修正保存しました。")
40            Else
41                MessageBox.Show("IDは必須です。")
42                Exit Sub
43            End If
44            Catch ex As Exception
45            MessageBox.Show(ex.Message)
46            Exit Sub
47        End Try
48    End Sub
49 
50//--><!

■[追加保存]ボタンがクリックされた時の処理

メンバ変数noの値がFalse(編集保存の場合)のときに、[追加保存]ボタンがクリックされた場合は、メッセージを表示します。

DataFormのカレントアイテムのインデックスを変数myIndexに格納します。

FindNameInContentメソッドで、指定された名前をもつDataForm内のTextBoxを参照します。「ID」が未入力であったり、「年齢」に入力された値が0より小さく120より大きい場合は警告メッセージを表示します。

ファイル名と文字コードで初期化された新しいStreamWriterのインスタンスである、writerオブジェクトを生成します。New XElementコンストラクタで要素を生成し、その子要素として、、、、、要素を生成します。これら子要素の値は参照しておいた、DataForm内の各TextBoxの値です。「性別」の値だけは、ComboBoxから選択された値を保持している変数、selectSexの値になります。Addメソッドで、生成したXMLデータをxmldocオブジェクトに追加します。Writeメソッドで、新規データの追加されたXMLデータ(xmldoc.ToString)をストリームに書き込みます。StreamWriterを閉じます。DataFormに追加したデータをバインドするために、DataShowプロシージャを実行します。

01<!--//--><![CDATA[// ><!--
02 
03    Private Sub AddSave_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles AddSave.Click
04        Try
05            If no = False Then
06                MessageBox.Show("編集保存ボタンをクリックしてください。")
07                Exit Sub
08            End If
09 
10            Dim myIndex As Integer = DataForm1.CurrentIndex
11            Dim IDdata As TextBox = DirectCast(DataForm1.FindNameInContent("myID"), TextBox)
12            Dim nameData As TextBox = DirectCast(DataForm1.FindNameInContent("myName"), TextBox)
13            Dim ageData As TextBox = DirectCast(DataForm1.FindNameInContent("myAge"), TextBox)
14            Dim addressData As TextBox = DirectCast(DataForm1.FindNameInContent("myAddress"), TextBox)
15            Dim companyData As TextBox = DirectCast(DataForm1.FindNameInContent("myCompany"), TextBox)
16 
17            If IDdata.Text = String.Empty Then
18                MessageBox.Show("IDは必須です。")
19                Exit Sub
20            End If
21 
22            If CInt(ageData.Text) < 0 OrElse CInt(ageData.Text) > 120 Then
23                MessageBox.Show("年齢が不正です。")
24                Exit Sub
25            End If
26 
27            Dim writer As StreamWriter = New StreamWriter(filePath, False, System.Text.Encoding.GetEncoding("utf-8"))
28            Dim addXml As XElement = New XElement("情報",
29                New XElement("ID", IDdata.Text),
30                New XElement("氏名", nameData.Text),
31                New XElement("年齢", ageData.Text),
32                New XElement("性別", selectSex),
33                New XElement("住所", addressData.Text),
34                New XElement("勤務先", companyData.Text))
35            xmldoc.Add(addXml)
36            writer.Flush()
37            writer.Write(xmldoc.ToString)
38            writer.Close()
39            DataShow()
40            no = False
41            MessageBox.Show("データが追加保存されました。")
42            Catch ex As Exception
43            MessageBox.Show(ex.Message)
44        End Try
45    End Sub
46 
47//--><!

■編集が開始された時に発生するイベント

メンバ変数noの値がTrueのとき、すなわち、追加保存の場合は、[編集保存]ボタンの使用を不可とし、編集時に[編集保存]ボタンの使用を可能にします。

01<!--//--><![CDATA[// ><!--
02 
03    Private Sub DataForm1_BeginningEdit(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles DataForm1.BeginningEdit
04        If no = True Then
05            EditSave.IsEnabled = False
06        Else
07            EditSave.IsEnabled = True
08        End If    End Sub
09 
10//--><!

■削除アイコン[-]が選択された時の処理

DataFormのカレントアイテムのインデックスを変数myIndexに格納します。FindNameInContentメソッドで、指定された名前(myID)をもつDataForm内のTextBoxを参照します。MessageBox.ShowメソッドでIDに該当するデータを削除するかどうかの確認メッセージ([OK]と[キャンセル]付き)を表示します。

[OK]ボタンをクリックした場合は、ファイル名と文字コードで初期化された新しいStreamWriterのインスタンスwriterオブジェクトを生成します。インデックスに該当する要素を取得します。Removeメソッドで要素とその子要素を削除します。IDに該当する要素の削除されたXMLデータ(xmldoc.ToString)をWriteメソッドでストリームに書き出します。StreamWriterオブジェクトを閉じます。

[キャンセル]の場合は処理を中止します。

01<!--//--><![CDATA[// ><!--
02 
03    Private Sub DataForm1_DeletingItem(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles DataForm1.DeletingItem
04        Dim myIndex As Integer = DataForm1.CurrentIndex
05        Dim IDdata As TextBox = DirectCast(DataForm1.FindNameInContent("myID"), TextBox)
06        Dim kakunin = MessageBox.Show(IDdata.Text & "のデータを削除しますか?", "削除確認", MessageBoxButton.OKCancel)
07        Select Case kakunin
08            Case MessageBoxResult.OK
09                Dim writer As StreamWriter = New StreamWriter(filePath, False, System.Text.Encoding.GetEncoding("utf-8"))
10                Dim delQuery = xmldoc.Descendants("情報")(myIndex)
11                delQuery.Remove()
12                writer.Flush()
13                writer.Write(xmldoc.ToString)
14                writer.Close()
15            Case MessageBoxResult.Cancel
16                e.Cancel = True
17            Exit Sub
18        End Select
19    End Sub
20~コード略~
21 
22//--><!

四国のSOHO。薬師寺国安(VBプログラマ)と、薬師寺聖(デザイナ、エンジニア)によるコラボレーション・ユニット。1997年6月、Dynamic HTMLとDirectAnimationの普及を目的として結成。共同開発やユニット名義での執筆活動を行う。XMLおよび.NETに関する著書や連載多数。最新刊は「Silverlight実践プログラミング」両名とも、Microsoft MVP for Development Platforms - Client App Dev (Oct 2003-Sep 2012)。http://www.PROJECTKySS.NET/

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています