none
DataTable をバインドした DataGrid に行を追加 RRS feed

  • 質問

  • お世話になります

    基本的すぎる質問ですが、
    DataGrid の ItemsSource プロパティに、DataTable をバインドして利用しています

    DataTable table = new DataTable();
    table.Columns.Add( "data1", typeof( string ) );
    table.Columns.Add( "data2", typeof( string ) );
    table.Columns.Add( "data3", typeof( string ) );
    
    grid.ItemsSource = table.Select();
    
    
    <DataGrid Name="grid" ItemsSource="{Binding}" AutoGenerateColumns="False">
     <DataGrid.Columns>
     <DataGridTextColumn Header="A" Binding="{Binding Path=data1}"/>
     <DataGridTextColumn Header="B" Binding="{Binding Path=data2}"/>
     <DataGridTextColumn Header="C" Binding="{Binding Path=data3}"/>
     </DataGrid.Columns>
    </DataGrid> 

     

    このとき、DataTable 側で行が追加された場合に DataGrid 側でも更新するにはどのようにすればよいでしょうか?
    WindowsForms の DataGridView からの書き換えなのですが、単純に書き換えても更新されません

    DataRow row = grid.NewRow();
    row[0] = "A";
    row[1] = "B";
    row[2] = "C";
    grid.Rows.Add( row ); // データを追加したら、DataGridを更新したい
    
    

    よろしくお願いします

    • 編集済み Nymphaea 2010年5月25日 9:13
    2010年5月25日 7:25

回答

  • grid.NewRow とかは table.NewRow の書き間違いだとして。

    ItemsSource に設定するのが Select() になってますね? なぜわざわざ Select() してるんでしょうか。これだと、バインディング対象は Select() の結果の DataRow[] になるわけで、元の DataTable に追加しても配列が変わるはずありません。

    • 回答としてマーク Nymphaea 2010年5月26日 10:19
    2010年5月25日 8:13
  • これまで確認した中では、
     grid.ItemsSource = table.DefaultView;
     FrameworkElement.DataContext = table;
    いずれの方法でもうまく更新されませんでした

     コード側と XAML 側のどちらが問題なのでしょうか?

    よろしくお願いします

    全体のソースが判らないので何とも言えませんが、以下のソースでは問題ありませんでした。
    C# ではなく VB で試しましたが、やることは変わらないと思います。何か勘違いされていませんか?

    Imports System.Data
    
    Class MainWindow
    
     Private m_table As New DataTable()
    
     Private Sub MainWindow_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
    
      m_table.Columns.Add("data1", GetType(String))
      m_table.Columns.Add("data2", GetType(String))
      m_table.Columns.Add("data3", GetType(String))
    
      For i As Integer = 0 To 100
       Dim row As DataRow = m_table.NewRow()
       row(0) = i.ToString() : row(1) = "てすと" : row(2) = "Test"
       m_table.Rows.Add(row)
      Next
    
      grid.ItemsSource = m_table.DefaultView
    
     End Sub
    
     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
    
      ' この更新は反映される
      For i As Integer = 0 To 100
       Dim row As DataRow = m_table.NewRow()
       row(0) = i.ToString() : row(1) = "てすと" : row(2) = "Test"
       m_table.Rows.Add(row)
      Next
    
     End Sub
    
    End Class

     


    ひらぽん http://d.hatena.ne.jp/hilapon/

    • 回答としてマーク Nymphaea 2010年5月26日 10:19
    2010年5月25日 10:09
    モデレータ
  • コード側と XAML 側のどちらが問題なのでしょうか?

    コードによるバインドとXAMLによるバインドがごっちゃになっているようです。ご提示されたXAMLであれば、
    this.DataContext = table.DefaultView;
    を追加するだけですし、コードによるバインドでしたら、
    grid.ItemsSource = table.DefaultView;
    でよく、XAMLにあるItemsSource="{Binding}"は必要ありません。

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    • 回答としてマーク Nymphaea 2010年5月26日 10:19
    2010年5月25日 14:37
    モデレータ
  • 何らかの要素の Dispatcher プロパティから Invoker/BeginInvoke して下さい。
    • 回答としてマーク Nymphaea 2010年5月26日 10:19
    2010年5月26日 4:16

すべての返信

  • grid.NewRow とかは table.NewRow の書き間違いだとして。

    ItemsSource に設定するのが Select() になってますね? なぜわざわざ Select() してるんでしょうか。これだと、バインディング対象は Select() の結果の DataRow[] になるわけで、元の DataTable に追加しても配列が変わるはずありません。

    • 回答としてマーク Nymphaea 2010年5月26日 10:19
    2010年5月25日 8:13
  • Hongliang様、返信ありがとうございます

    > grid.NewRow とかは table.NewRow の書き間違いだとして。

    すいません、書き間違いでした・・・

     

    > ItemsSource に設定するのが Select() になってますね? なぜわざわざ Select() してるんでしょうか。
    > これだと、バインディング対象は Select() の結果の DataRow[] になるわけで、元の DataTable に追加しても配列が変わるはずありません。

    確かにそうでした・・・

     

    これまで確認した中では、
     grid.ItemsSource = table.DefaultView;
     FrameworkElement.DataContext = table;
    いずれの方法でもうまく更新されませんでした

     

    コード側と XAML 側のどちらが問題なのでしょうか?

    よろしくお願いします

    2010年5月25日 9:08
  • これまで確認した中では、
     grid.ItemsSource = table.DefaultView;
     FrameworkElement.DataContext = table;
    いずれの方法でもうまく更新されませんでした

     コード側と XAML 側のどちらが問題なのでしょうか?

    よろしくお願いします

    全体のソースが判らないので何とも言えませんが、以下のソースでは問題ありませんでした。
    C# ではなく VB で試しましたが、やることは変わらないと思います。何か勘違いされていませんか?

    Imports System.Data
    
    Class MainWindow
    
     Private m_table As New DataTable()
    
     Private Sub MainWindow_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
    
      m_table.Columns.Add("data1", GetType(String))
      m_table.Columns.Add("data2", GetType(String))
      m_table.Columns.Add("data3", GetType(String))
    
      For i As Integer = 0 To 100
       Dim row As DataRow = m_table.NewRow()
       row(0) = i.ToString() : row(1) = "てすと" : row(2) = "Test"
       m_table.Rows.Add(row)
      Next
    
      grid.ItemsSource = m_table.DefaultView
    
     End Sub
    
     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
    
      ' この更新は反映される
      For i As Integer = 0 To 100
       Dim row As DataRow = m_table.NewRow()
       row(0) = i.ToString() : row(1) = "てすと" : row(2) = "Test"
       m_table.Rows.Add(row)
      Next
    
     End Sub
    
    End Class

     


    ひらぽん http://d.hatena.ne.jp/hilapon/

    • 回答としてマーク Nymphaea 2010年5月26日 10:19
    2010年5月25日 10:09
    モデレータ
  • コード側と XAML 側のどちらが問題なのでしょうか?

    コードによるバインドとXAMLによるバインドがごっちゃになっているようです。ご提示されたXAMLであれば、
    this.DataContext = table.DefaultView;
    を追加するだけですし、コードによるバインドでしたら、
    grid.ItemsSource = table.DefaultView;
    でよく、XAMLにあるItemsSource="{Binding}"は必要ありません。

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    • 回答としてマーク Nymphaea 2010年5月26日 10:19
    2010年5月25日 14:37
    モデレータ
  • ひらぽん様、trapemiya様、返信ありがとうございます

    コードによるバインドとXAMLによるバインドがごっちゃになっているようです。ご提示されたXAMLであれば、
    this.DataContext = table.DefaultView;
    を追加するだけですし、コードによるバインドでしたら、
    grid.ItemsSource = table.DefaultView;
    でよく、XAMLにあるItemsSource="{Binding}"は必要ありません。

    いろいろと試しているうちにコードがごっちゃになってしまっていました・・・
    XAML 側に ItemsSource="{Binding}" があっても実質的な影響がないため、書き換えずに放置していました
    実際のコードでは、 ItemsSource のほうを利用しています

     

    ひらぽん様が検証してくださったように、最初から検証用のコードを書きなおして試してみましたら、
    確かに問題はなく正常に行が追加されていることを確認しました

    そこでデバッガで違いを追ってみたところ、データを DataTable に追加する際のメソッドが別スレッドとなっており、
    スレッド境界をまたいでイベントを通知していました

    元のプログラムでは別スレッドで処理された結果を、DataGridView を張り付けたフォームに BeginInvoke で通知していたのですが、
    WPF では BeginInvoke に相当するメソッドが無いため、とりあえず通常のイベントで通知していたことを、すっかり忘れていました・・・

     

    この場合、メインスレッドに対してイベントを通知するにはどのような方法があるでしょうか?
    別スレッドは System.Threading.Timer.Timer の TimerCallback メソッドからイベントを通知しています

    よろしくお願いします

    2010年5月26日 2:46
  • 何らかの要素の Dispatcher プロパティから Invoker/BeginInvoke して下さい。
    • 回答としてマーク Nymphaea 2010年5月26日 10:19
    2010年5月26日 4:16
  •  Hongliang様、返信ありがとうございます

    何らかの要素の Dispatcher プロパティから Invoker/BeginInvoke して下さい。

    TimerCallback メソッドから DataGrid.Dispatcher を利用して BeginInvoke を呼んでいたのですが、
    カウント値が一致しないエラーが出て困っていました

     

    そこで、Dispatcher について調べていたところ、DispatcherTimer クラスを利用することでできそうなことが MSDN に書いてあったので試してみました
    とりあえずは DispatcherTimer クラスを利用することで目的は達成できました

    DispatcherTimer timer = new DispatcherTimer( DispatcherPriority.Normal, DataGrid.Dispatcher );
    timer.Interval = TimeSpan.FromMilliseconds( 100 );
    timer.Tick += new EventHandler( TimerFunction );
    timer.Start();
    
    private void TimerFunction( object sender, EventArgs e )
    {
      // ここで処理を実行
      DataRow row = DataTable.NewRow();
      row[0] = "A";
      row[1] = "B";
      row[2] = "C";
      DataTable.Rows.Add( row );
    }

     

    実際のコードでは Timer クラスは使用しないので、Dispatcher プロパティについては更に原因を調査してみます

    お知恵をお貸しくださった皆様、ありがとうございました

    2010年5月26日 10:18