印刷用ページ       送信     
クリックして評価とフィードバックをお寄せください
 基本的な本能 : XML リテラルを使用した動的なデータ入力
Related Articles

Cobra は Python の子孫であり、特に、動的または静的に型指定されたプログラミング モデルを組み合わせて使用することや、組み込みの単体テスト機能、スクリプト機能、およびいくつかの契約による設計の定義が特徴です。そのすばらしい能力をご紹介します。

Ted Neward

MSDN Magazine June 2009

...

Read more!

.NET RIA Services には、認証、ロール、プロファイル管理などの一連のサーバー コンポーネントおよび ASP.NET の拡張機能が用意されています。ここではそのしくみについて説明します。

Jonathan Carter

MSDN Magazine May 2009

...

Read more!

Udi Dahan が、大規模な Software Plus Services 取引アプリケーションの開発中に、予測していなかった問題を彼らのチームがどのようにして識別し、乗り越えていったのかを説明します。

Udi Dahan

MSDN Magazine April 2009

...

Read more!

高パフォーマンスで高品質の市販アプリケーションを開発するには、依然として C++ とネイティブ コードの機能が頼りになります。Direct2D は、開発者の要求に応えるグラフィックス性能の実現に役立ちます。

Kenny Kerr

MSDN Magazine June 2009

...

Read more!

Jeremy Miller が引き続き、永続化パターンの解説の一環として、Unit of Work 設計パターンを検討し、永続性の無視にまつわる問題について調べます。

Jeremy Miller

MSDN Magazine June 2009

...

Read more!

Popular Articles

The MVP pattern helps you separate your logic and keep your UI layer free of clutter. This month learn how.

Jean-Paul Boodhoo

MSDN Magazine August 2006

...

Read more!

James Avery does it again with his popular list of developer tools. This time he covers the best Visual Studio add-ins available today that you can download for free.

James Avery

MSDN Magazine December 2005

...

Read more!

WPF は、.NET Framework 3.0 の最も重要な新しいテクノロジの 1 つです。今月は、John Papa がそのデータ バインド機能について紹介します。

John Papa

MSDN Magazine December 2007

...

Read more!

サイドバー ガジェットは小さいけれど強力なツールで、驚くほど簡単に作成できます。ここでは、Donavon West がその楽しさを教えてくれます。

Donavon West

MSDN Magazine August 2007

...

Read more!

この記事では、Windows Presentation Foundation でのプログラムおよび宣言によるデータ バインドと表示の手法を説明します。

Josh Smith

MSDN Magazine July 2008

...

Read more!

基本的な本能
XML リテラルを使用した動的なデータ入力
Beth Massi
データ駆動型アプリケーションのあらゆる管理画面を自動的に生成できればすばらしいと思いませんか。ここで言う "管理" の対象は、シンプルなルックアップ データ テーブルや連絡先テーブルです。それらの画面の作成をチームの新人開発者に任せる代わりに、ご自分のオブジェクト モデルやデータベース スキーマに基づいて、実行時に生成できるようにしてみてはいかがでしょうか。XAML を使用する Windows Presentation Foundation (WPF) では、これは比較的容易な作業です。また、Visual Basic の幅広い XML のサポートがあれば、ユーザー インターフェイスを動的に生成することなど実に簡単に行えます。

XAML の動的な読み込み
Visual Basic の XML リテラルおよび XML 名前空間インポートを使用すると、難なく WPF ユーザー インターフェイスを実行時に動的に生成できます。WPF ではそのユーザー インターフェイスの要素とレイアウトを XAML で表現できるうえに、Visual Basic が複雑な処理をすべて肩代わりし、適切な XAML を作成するために XML 名前空間を解決してくれるためです。
具体的に説明しましょう。System.Windows.Markup 名前空間の XamlReader クラスと XamlWriter クラスを使用することで、XAML の読み込みと保存を実行時に行うことができます。新しい WPF プロジェクトを作成し、Window1 分離コードで次のように記述してください。
Imports <xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
Imports <xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
Imports System.Windows.Markup

Class Window1
  Private Sub Window1_Loaded() Handles MyBase.Loaded
    Dim UI = <Label Name="Label1">This is COOL!</Label>
    Me.Content = XamlReader.Load(UI.CreateReader())
  End Sub
End Class
図 1 に示すように、ウィンドウのコンテンツがラベルに表示されます。
図 1 実行時に XAML を読み込む XAMLReader
コード ファイルの上部に WPF 名前空間インポートを含めた場合は、XAML を実行時に読み込むために必要な XAML が Visual Basic によって自動的に作成されます。この例ではコントロールの既定の名前空間を使用していますが、名前空間プレフィックスを指定し、XAML を変更してそれを使用することもできます。その結果、コードで操作する他の XML 用に既定の名前空間を残しておくことができます。
Imports <xmlns:controls=  "http://schemas.microsoft.com/winfx/2006/xaml/presentation">
Imports <xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
Imports System.Windows.Markup

Class Window1
  Private Sub Window1_Loaded() Handles MyBase.Loaded
    Dim UI = <controls:Label Name="Label1">      This is COOL!      </controls:Label>
    Me.Content = XamlReader.Load(UI.CreateReader())
  End Sub
End Class
ここで注意しなければならないのは、動的に読み込まれるコントロールのイベント ハンドラを XAML では直接指定できないという点です。実行時に読み込む XAML ではコンパイルを要求できないためです。ただし、実行時に分離コードに追加することはできます。たとえば、次の処理を動的に行うことはできません。"イベントを指定する XAML ファイルをコンパイルする必要があります。" という XAML 解析例外が発生するためです。
Dim UI = <Label
    MouseDown="Label1_MouseDown"
    Name="Label1">
    This is COOL!
  </Label>
Me.Content = XamlReader.Load(UI.CreateReader())
代わりに、実行時にラベルへの参照を取得し、コードにイベント ハンドラを明示的に追加できます。
Private Sub Window1_Loaded() Handles Window1.Loaded
  Dim UI = <Label Name="Label1">This is COOL!</Label>
  Dim myLabel As Label = XamlReader.Load(UI.CreateReader())
  Me.Content = myLabel
  AddHandler myLabel.MouseDown, AddressOf Label1_MouseDown
End Sub

Private Sub Label1_MouseDown()
  MsgBox("Mouse Down!")
End Sub
このように、さまざまな手段があります。もちろん、UI の動的な生成は WPF に限ったものではありません。Windows フォームでも同様に行うことができます。ただし、手動によるレイアウトのコーディングの練習のような作業になります。WPF と Visual Basic を使用すると、この処理が非常に簡単になります。ごくわずかな XAML を定義し、単一の LINQ クエリでそれを構築できるためです。

メタデータの取得
UI の XAML を構築するには、いくつかのメタデータを取得しなければなりません。テーブルのスキーマに基づいて UI の外観を構築する処理を記述するための簡単な方法が必要です。そこで、リフレクションを使用してオブジェクト モデルを参照することができます。ただし、そのモデルが非常にシンプルで、データベース テーブルに 1 対 1 でマッピングされるのであれば、データベースへの問い合わせを行って、そのテーブルに関するメタデータを取得することができます。
シンプルな UI を生成するために、少なくともテーブルの各列のプロパティ (ColumnName、DataType、MaxLength、および IsPrimaryKey) を取得しなければならない場合があります。これを行うための 1 つの方法は、情報スキーマ ビューを使用して特定のテーブルの基本的なスキーマを返す、GetTableSchema というストアド プロシージャ (この例では、Northwind データベースを使用しています) を作成することです (図 2 を参照)。
CREATE PROCEDURE dbo.GetTableSchema
  (
  @table varchar(50)
  )    
AS
SELECT 
c.table_name As TableName, 
c.column_name As ColumnName, 
c.data_type As DataType, 
c.character_maximum_length As MaxLength,
  COALESCE (
  ( SELECT 
    CASE cu.column_name
      WHEN null THEN 0
      ELSE 1
    END
  FROM information_schema.constraint_column_usage cu
  INNER join information_schema.table_constraints ct
  ON ct.constraint_name = cu.constraint_name
  WHERE 
  ct.constraint_type = 'PRIMARY KEY' 
  AND ct.table_name = c.table_name
  AND cu.column_name = c.column_name 
  ),0) AS IsPrimaryKey
FROM information_schema.columns c
INNER JOIN information_schema.tables t
ON c.table_name = t.table_name
WHERE @table = t.table_name and 
  (t.table_type = 'BASE TABLE' and not 
  (t.table_name = 'dtproperties') and not 
  (t.table_name = 'sysdiagrams'))
ORDER BY c.table_name, c.ordinal_position
このストアド プロシージャには、決められた簡単な方法でアクセスできます。具体的には、新しい LINQ to SQL クラスのアイテムをプロジェクトに追加し、サーバー エクスプローラでデータベースの [ストアド プロシージャ] ノードを展開し、上記のプロシージャをメソッド ペインにドラッグします。次に、ストアド プロシージャから返されるフィールドと同じプロパティを格納する TableSchema というオブジェクトをデザイン サーフェイスで作成する必要があります。この例で編集することにしている Northwind 顧客テーブルもモデルに追加できます。設定を終えたら、GetTableSchema メソッドのプロパティで、結果の型を TableSchema クラスに設定します。

メタデータに基づいた UI の生成
ここまでの手順で、メタデータを取得できるようになりました。実行時に XAML を生成するためのクエリを記述する準備はほぼ整っています。ContentControl を使用すると、ウィンドウのコンテンツ全体を生成することも、その一部だけを生成することもできます。そのため、ウィンドウの静的要素を簡単にレイアウトしたり、動的コンテンツの配置先を指定したりできます。
この例では、フォームをレイアウトするためのグリッド パネルを使用して、シンプルなテキスト ボックス、[Find] (検索) ボタン、および [Save] (保存) ボタンをウィンドウ上部の固定領域に作成します。ウィンドウのその下の領域には ContentControl を配置し、DynamicContent などの名前を付けます (図 3 を参照)。この場合の XAML は図 3 のようになります。
<Window x:Class="Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Window1"  Name="Window1" SizeToContent="WidthAndHeight" >
  <Grid Name="MainGrid"  >
    <Grid.RowDefinitions>
      <RowDefinition Height="10*" />
      <RowDefinition Height="60*" />
    </Grid.RowDefinitions>
    <StackPanel Name="StackPanel1" Orientation="Horizontal" 
      Margin="3" VerticalAlignment="Top">
      <Label Height="28" Name="Label1" Width="84" 
        HorizontalContentAlignment="Right" FontWeight="Bold">ID</Label>
      <TextBox Height="25" Name="txtSearch" Width="120"></TextBox>
      <Button Height="25" Name="btnFind" Width="75">Find</Button>
      <Button Height="25" Name="btnSave" Width="75">Save</Button>
    </StackPanel>
    <ContentControl Grid.Row="1" Name="DynamicContent" Margin="3" />
  </Grid>
</Window>
分離コード ファイルの先頭に、適切な XML 名前空間インポートを追加してください。その後、Window Loaded イベント ハンドラに LINQ クエリを記述することで、UI を生成できます。実際に顧客データの読み込みと保存を行えるように、ボタン クリック ハンドラを追加することもできます。
図 4 に、Window クラスのコード全体を示します。このコードにはいくつかの LINQ クエリ式が埋め込まれていることに注意してください (図 4 を参照)。このクエリ式により、コントロールの XAML を XElement として返す GetUIElement というヘルパ関数を使用して、フィールド名、コントロール、およびデータ バインドのラベルを作成できます。
Imports <xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
Imports <xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
Imports System.Windows.Markup

Class Window1
  Dim db As New NorthwindDataContext
  Dim CustomerData As Customer

  Private Sub Window1_Loaded() Handles MyBase.Loaded
    Dim customerSchema = db.GetTableSchema("Customers").ToList()

    Dim UI = _
      <Grid xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns=          "http://schemas.microsoft.com/winfx/2006/xaml/presentation">
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="100*"/>
          <ColumnDefinition Width="200*"/>
        </Grid.ColumnDefinitions>
        <StackPanel Name="StackLabels" Margin="3">
          <%= From column In customerSchema _
            Where column.IsPrimaryKey = 0 _
            Select <Label
              Height="28"
              Name=<%= column.ColumnName & "Label" %>
              HorizontalContentAlignment="Right">
            <%= column.ColumnName %>:</Label> %>
          </StackPanel>
          <StackPanel Grid.Column="1" Name="StackFields" Margin="3">
            <%= From column In customerSchema _
              Where column.IsPrimaryKey = 0 _
              Select GetUIElement(column) %>
          </StackPanel>
      </Grid>

    Me.DynamicContent.Content = XamlReader.Load(UI.CreateReader)

  End Sub

  Private Function GetUIElement(ByVal column As TableSchema) As XElement
    Select Case column.DataType
      Case "datetime", "int", "smallint", "money"
        Return <TextBox
          Height="28"
          Name=<%= "txt" & column.ColumnName %>
          Text=<%= "{Binding Path=" & column.ColumnName & "}" %>/>
      Case "bit"
        Return <CheckBox
          HorizontalContentAlignment="Left"
          Name=<%= "chk" & column.ColumnName %>
          IsChecked=<%= "{Binding Path=" & column.ColumnName & "}" %>>
          <%= column.ColumnName %>
          </CheckBox>
      Case "image"
        Return <Image
           Height="150"
           Width="150"
           Stretch="Fill"
           HorizontalAlignment="Left"
           Name=<%= "img" & columnInfo.ColumnName %>
           Source=<%= "{Binding Path=" & columnInfo.ColumnName & "}" %>/>
      Case Else
        Return <TextBox
          Height="28"
          Name=<%= "txt" & column.ColumnName %>
          MaxLength=<%= column.MaxLength %>
          Text=<%= "{Binding Path=" & column.ColumnName & "}" %>/>
    End Select
  End Function

  Private Sub btnFind_Click() Handles btnFind.Click
    If Me.txtSearch.Text <> "" Then
      Me.CustomerData = (From cust In db.Customers _
        Where cust.CustomerID = Me.txtSearch.Text).FirstOrDefault()

      Me.DataContext = Me.CustomerData
    Else
      Me.DataContext = Nothing
    End If
  End Sub

  Private Sub btnSave_Click() Handles btnSave.Click
    If Me.DataContext IsNot Nothing Then
      Try
        db.SubmitChanges()
        MsgBox("Saved")

      Catch ex As Exception
        MsgBox(ex.ToString)
      End Try
    End If
  End Sub
End Class
この時点でアプリケーションを実行すると、UI にはすべての顧客フィールドが表示されます (図 5 を参照)。GetTableSchema ストアド プロシージャは、データベースに格納されているあらゆるテーブルに対して使用できるので、さらに抽象化することも可能だということを覚えておいてください。たとえば、スキーマを問わず、データベースに含まれている任意の管理テーブルの編集が可能な単一のウィンドウが必要な場合は、どうすればよいでしょうか。
図 5 UI の顧客フィールド

動的なデータ入力
前の例のフォームは、データの読み込みと編集のために、依然として Customer のオブジェクト モデルに関連付けられています。UI は動的に生成できますが、データ入力はそうはいきません。皆さんが求めているのは、どの管理テーブルのデータベース スキーマを変更した場合にも、オブジェクト モデルを更新したり、すべてのコードを再コンパイルしたりする必要のない、より抽象化されたフォームではないでしょうか。
これを実現する方法の 1 つに、型指定のないシンプルな DataTable の読み込みと編集を実行時に行う方法があります。この場合も、XML リテラルを使用するとうまくいきます。ここでは、SqlDataAdapter の SELECT ステートメントと UPDATE ステートメントを生成します。ここで注意すべきなのは、この手法では検証ルールを適用するためにデータベースを利用するという点です。そのため、私は非常にシンプルなテーブル (管理テーブルやルックアップ テーブルなど) を編集する場合にのみ、このような動的フォームを使用するようにしています。とはいえ、WPF Binding.ValidationRules プロパティをいろいろと操作して、検証ルールをコントロールに宣言で追加してみることをお勧めします。
TableSchema メタデータに対応する型指定された DataTable も同様に作成できるため、プロジェクトに LINQ to SQL クラスが必要なくなります。これを作成するには、プロジェクトを右クリックし、新しいアイテムを追加し、[データセット] を選択します。そのアイテムに TableSchemaDataSet という名前を付け、GetTableSchema ストアド プロシージャをサーバー エクスプローラからデザイン サーフェイスにドラッグします。この操作により、型指定された DataTable を手間をかけずに自動的に作成できます。DataTable の名前を TableSchema に変更し、保存してください。
さて、データを編集するには、動的に生成された UI に型指定のない DataTable を読み込む必要があります。何らかの主キーがある場合を除き、編集するテーブルのスキーマについて関知する必要はありません。WPF では DataTable と DataSet をうまく処理できますが、これらは実行時に読み込まれるので、手動で設定しなければならないものが何点かあります。
その 1 つが、TableName というパブリック プロパティです。編集対象のテーブルの名前を表示するために、このプロパティをフォームに設定する必要があります。このプロパティは、コードの呼び出しによって簡単に設定できます。さらに、必要な ADO.NET オブジェクトを参照するために、いくつかのクラス レベルのプライベート変数を作成してください。この例のウィンドウの XAML マークアップは、以前のものとまったく変わりません。図 6 に、必要なクラス レベルの変数と、Shippers の既定値に設定されている TableName プロパティを示します。
Imports <xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
Imports <xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
Imports System.Windows.Markup
Imports System.Data.SqlClient
Imports System.Data

Partial Public Class Window2
  'This is the metadata table we created in the DataSet Designer
  Private TableSchema As New TableSchemaDataSet.TableSchemaDataTable
  'ADO.NET objects used to load and save the table we're editing
  Private TableDataAdapter As New SqlDataAdapter
  Private TableConnection As New _
          SqlConnection(My.Settings.NorthwindConnectionString)
  Private Table As DataTable
  'This is the key field used in searching for a row in this example
  Private PKField As TableSchemaDataSet.TableSchemaRow

  'This property can be set before the Form.Show() to edit any table
  Private m_tableName As String = "Shippers"
  Public Property TableName() As String
    Get
      Return m_tableName
    End Get
    Set(ByVal value As String)
      m_tableName = value
    End Set
  End Property
  End Class
これで、Loaded イベント ハンドラを通じてメタデータを読み込み、XAML の作成と読み込みを行って前の例のような UI を表示し、TableDataAdapter の UpdateCommand を設定できるようになりました。図 7 に、Loaded イベント ハンドラと、XAML を生成するコードを示します。
Private Sub Window1_Loaded() Handles MyBase.Loaded
  Try
    'Get the schema of the database table we want to edit
    Dim taSchema As New _      TableSchemaDataSetTableAdapters. TableSchemaTableAdapter
    taSchema.Fill(Me.TableSchema, Me.TableName)

    'Create the DataTable that will hold the record we're editing
    Me.Table = New DataTable(Me.TableName)
    Me.Title = Me.TableName
    Me.LoadUI() 
    Me.SetPrimaryKey()
    Me.SetUpdateCommand()

  Catch ex As Exception
    MsgBox(ex.ToString)
    Me.Close()
  End Try
End Sub

Private Sub LoadUI() _
  Dim UI = _
    <Grid xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      Name="Grid1">
      <Grid.ColumnDefinitions>
        <ColumnDefinition Width="100*"/>
        <ColumnDefinition Width="200*"/>
      </Grid.ColumnDefinitions>
      <StackPanel Name="StackLabels" Margin="3">
        <%= From column In Me.TableSchema _
          Where column.IsPrimaryKey = 0 AndAlso _              column.DataType <> "timestamp" _          Select <Label 
            Height="28"
            Name=<%= column.ColumnName & "Label" %>
            HorizontalContentAlignment="Right">
          <%= column.ColumnName %>:</Label> %>
        </StackPanel>
        <StackPanel Grid.Column="1" Name="StackFields" Margin="3">
          <%= From column In Me.TableSchema _
            Where column.IsPrimaryKey = 0 AndAlso _               column.DataType <> "timestamp" _
            Select GetUIElement(column) %>
        </StackPanel>
      </Grid>

  Me.DynamicContent.Content = XamlReader.Load(UI.CreateReader())
End Sub

Private Function GetUIElement(ByVal columnInfo As _  TableSchemaDataSet.TableSchemaRow) As XElement
  Select Case columnInfo.DataType.ToLower
    Case "datetime", "int", "smallint", "money"
      Return <TextBox
        Height="28"
        Name=<%= "txt" & columnInfo.ColumnName %>
        Text=<%= "{Binding Path=" & columnInfo.ColumnName & "}" %>/>
    Case "bit"
      Return <CheckBox
        HorizontalContentAlignment="Left"
        Name=<%= "chk" & columnInfo.ColumnName %>
        IsChecked=<%= "{Binding Path=" & columnInfo.ColumnName & "}" %>>
        <%= columnInfo.ColumnName %>
        </CheckBox>
    Case "image"
      Return <Image
         Height="150"
         Width="150"
         Stretch="Fill"
         HorizontalAlignment="Left"
         Name=<%= "img" & columnInfo.ColumnName %>
         Source=<%= "{Binding Path=" & columnInfo.ColumnName & "}" %>/>
    Case Else
      Return <TextBox
        Height="28"
        Name=<%= "txt" & columnInfo.ColumnName %>
        MaxLength=<%= columnInfo.MaxLength %>
        Text=<%= "{Binding Path=" & columnInfo.ColumnName & "}" %>/>
  End Select
End Function
これで、XAML UI は生成できました。次に、主キーのフィールド (これは TableSchemaDataRow オブジェクトです) を設定し、ユーザーがフォームの [Find] (検索) ボタンをクリックした際の SELECT クエリや UPDATE ステートメントで主キーを使用できるようにします。一般に、主キーは代理キー (自動的に増分する整数など) であり、ユーザーにとって特に意味はないので、検索フィールド名をキャプチャする別のパブリック プロパティを作成してもかまいません。
すべてのテーブルに 1 つの主キー フィールドしか定義されていない場合は、クエリでインデクサ 0 を使用できます。この場合、シーケンスの最初の要素が返されます。
  Private Sub SetPrimaryKey()
    'Grab the Primary Key column of the table we want to 
    ' edit so we can use it in the search
    Me.PKField = (From column In Me.TableSchema _
                  Where column.IsPrimaryKey = 1)(0)
  End Sub
SELECT ステートメントを作成するために、もう一度 XML リテラルを使用します。ただし、ここでは XML 自体は作成しません。代わりに、XElement の .Value プロパティを呼び出すことで文字列を作成できます。その後で、SqlCommand を作成し、型指定のない DataTable に結果を挿入できます。ウィンドウの DataContext を設定すると、XAML の生成時に指定されたフィールドへのデータ バインドが設定されます。図 8 に、SELECT ステートメントを生成し、DataContext を結果に設定するコードを示します。
Private Sub btnFind_Click() Handles btnFind.Click
  If Me.txtSearch.Text <> "" Then
    Try
      'Create the SELECT command
      Dim cmdText = <s>
        SELECT * FROM <%= Me.TableName %> 
        WHERE <%= Me.PKField.ColumnName %> = 
          <%= If(Me.PKField.DataType.Contains("char"), _
            "'" & Me.txtSearch.Text & "'", _
            Me.txtSearch.Text) %>
        </s>.Value

      Dim cmd As New SqlCommand(cmdText, Me.TableConnection)
      Me.Table.Clear()
      Me.TableDataAdapter.SelectCommand = cmd
      Me.TableDataAdapter.Fill(Me.Table)

      Me.DataContext = Me.Table
      Dim view = CollectionViewSource.GetDefaultView(Me.Table)
      view.MoveCurrentToFirst()

    Catch ex As Exception
      MsgBox(ex.ToString)
      Me.DataContext = Nothing
    End Try
  Else
    Me.DataContext = Nothing
  End If
End Sub
このレコードを編集および保存する機能も必要なので、UPDATE コマンドを生成する必要があります。この例のフォームではレコードの編集だけを行うため、コードでは SqlDataAdapter の UpdateCommand だけを設定していますが、Delete コマンドや Insert コマンドも同じ方法で簡単に作成できます。図 9 に、UPDATE ステートメントを作成し、UpdateCommand を設定するコードを示します。
Private Sub SetUpdateCommand()
  'Set the UpdateCommand so that we can save edited records in the table
  Dim cmdText = <s>
    UPDATE <%= Me.TableName %> 
    SET <%= From column In Me.TableSchema _
      Where column.IsPrimaryKey = 0 AndAlso _        column.DataType <> "timestamp" _
      Select <c>
        <%= column.ColumnName %> = @<%= column.ColumnName %>
        <%= If(Me.TableSchema.Rows.IndexOf(column) < _
          Me.TableSchema.Rows.Count - 1, ", ", "") %>
        </c>.Value %>
    WHERE <%= Me.PKField.ColumnName %> = @<%= Me.PKField.ColumnName %>
      <%= From column In Me.TableSchema _
        Where column.IsPrimaryKey = 0 AndAlso _          column.DataType = "timestamp" _
        Select <c>
          AND <%= column.ColumnName %> = @<%= column.ColumnName %>
          </c>.Value %>
    </s>.Value

  Dim cmd As New SqlCommand(cmdText, Me.TableConnection)
  Dim p As SqlParameter

  For Each column In Me.TableSchema
    If column.IsPrimaryKey = 0 AndAlso column.DataType = "timestamp" Then
    'Note: It's recommended to use a TimeStamp column in your tables for     'concurrency checking
      p = New SqlParameter("@" & column.ColumnName, SqlDbType.Timestamp)
      p.SourceVersion = DataRowVersion.Original
      p.SourceColumn = column.ColumnName
      cmd.Parameters.Add(p)
    Else
      p = New SqlParameter("@" & column.ColumnName, _
        CType([Enum].Parse(GetType(SqlDbType), _          column.DataType, True), SqlDbType))
      p.SourceColumn = column.ColumnName
      p.SourceVersion = DataRowVersion.Current
      cmd.Parameters.Add(p)
    End If
  Next

  Me.TableDataAdapter.UpdateCommand = cmd
End Sub
ここで、次の点に注意してください。UpdateCommand を作成するコードでは、TimeStamp フィールド (私はこれを自分の Northwind に追加しました) を使用して同時実行チェックを行うことを想定していますが、元の値を現在の値と比較してチェックする長いバージョンを作成することもできます。
ここで、このフォームの TableName プロパティを設定できます。また、UI を動的に生成し、[Find] (検索) ボタンのクリック時にデータを読み込み、変更内容をデータベースに保存できるようになりました。データベース テーブルのスキーマを変更し、アプリケーションを再コンパイルせずに再度実行してみてください。うまくいくはずです。
この記事で説明した両方の例のプロジェクトをコード ギャラリーにアップロードしました。code.msdn.microsoft.com/dynamicWPF にアクセスしてみてください。

ご意見やご質問は instinct@microsoft.com まで英語でお送りください。

Beth Massi は、マイクロソフトの Visual Studio コミュニティ チームのオンライン コンテンツおよびコミュニティ プログラム マネージャです。彼女は、ビジネス アプリケーション開発者向けのコンテンツを発信し、MSDN デベロッパー センターにコミュニティの機能を導入する業務に携わっています。彼女のブログを blogs.msdn.com/bethmassi でご覧いただけます。

Page view tracker