none
TableAdapterについて RRS feed

  • 質問

  • こんばんは。

    VB2010+SQLServerExpressを利用しています。

    TableAdapterを作成し、DataGridViewにバインドしてデータを表示させようとしています。

    Comboboxで選択した値をパラメータにしてTableAdapterで作成されるFillメソッドを
    使用してデータを取得しています。
    (ComboboxのSelectedValueChangedイベントにて、Fillを呼んでいます)

    同じ画面上で、1回目のデータ取得は問題なくできるのですが、一度データを取得した後で
    Comboboxの値を変更すると、Fillメソッドを読んではいるのですが、DataGridViewの
    データが更新されません。

    特にエラーも出ず、デバッグでFillメソッドを呼んでいることも確認しました。

    1回目にデータを取得した際に、DataGridViewのどこでもよいのでいったん選択をした後で
    Comboboxの値を変更すると、ちゃんと新しく選択した値をパラメータとして、データを取得し、
    DataGridViewの表示も更新されます。

    しかし、データ取得できないのではなく、データがちゃんとDataGridViewに表示がされていない
    ように思われます。
    (何も表示されないDataGridViewのセルをクリックするとクリックしたところのデータが表示されます。
    しかし、次々にセルをクリックするとエラーになります。現在、エラー内容を確認できないので、
    こちらに表示ができません。)

    いったい何が原因なのでしょうか。

    いろいろ調べたのですが、どうにもわかりません。

    どうか、アドバイスをお願いします。

    2014年9月4日 12:42

すべての返信

  • DataGridViewにバインドしているのはデータテーブルでしょうか? であれば、データテーブルのデータが変わってもその変更はDataGridViewに伝わらなかったと思います。よって、DataSourceに一度nothingをセットし、再度DataSourceを指定すれば、そのタイミングでデータテーブルを読むはずなので変更後のデータが表示されると思います。

    #以下によると、データテーブルのClearメソッドを読んでおくとよさそうですね。今、確かめられなくてごめんなさい。

    How to Refresh/ Update a TableAdapter or BindingSource
    http://social.msdn.microsoft.com/Forums/vstudio/en-US/7b5892d1-6caf-4252-a9e5-5f63ad637f39/how-to-refresh-update-a-tableadapter-or-bindingsource?forum=vbgeneral


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2014年9月4日 13:27
    モデレータ
  • 以前から何度か指摘させていただいてますが情報不足です。

    あなたがどのようにそのアプリを作っているか、アプリの構造がどうなっているか、具体的な情報があなたの質問にはほとんどないということは認識されてますか?

    今提供されている情報だけで答えるには、回答者はあなたのアプリの構造を想像して、想像をベースに答えるということになります。

    で、その想像が間違っていると、当然ですが、答えが的外れになって、回答者が費やした時間・努力が無駄になるだけではなく、あなたもタイムリーに的を得た回答が得られない(場合によっては混乱するだけ、スレッドにノイズを増やすだけ)という結果になることは理解いただけますか?

    なので、回答者にとっては掲示板に書いてあること以外あなたの状況は知り得ないということを認識していただき、どういう情報を提供すれば回答者があなたの状況を的確に把握でき、ピンポイントで的を得た回答ができるかをよ~く考えて、質問を書いていただくようお願いします。

    2014年9月4日 14:19
  • ご回答有難うございます。

    人事管理システムを作成しており、現在の処理はDataBaseからTableAdapterにより作られたDataTableを
    DataGridViewのDataSourceにバインドして日付、職員名にて抽出したデータを表示しています。

    TableAdapterのFillメソッドを実行することで、DataGridViewの表示も更新をされると思っていたのですが、
    更新がされず、困っています。

    はじめは、
    TableAdapter.Fill(DataSet.テーブル名,社員名,日付)

    DataGridView.Refresh

    としてみたのですが、DataGridViewのデータが更新されませんでした。

    trapemiya様がおっしゃるように、

    DataGridView.DataSource=Nothing
    DataGridView.DataSource=DataSet.テーブル名

    TableAdapter.Fill(DataSet.テーブル名,社員名,日付)

    としてみましたが、(この書き方でよいかどうかは、はなはだ不安ですが)
    初回のデータ取得、表示はできるのですが、2回目に

    DataGridView.DataSource=Nothing

    の部分で、「値が範囲外です」とのエラーが出ます。

    現在、自分なりに調べているところですが、現状報告まで。

    2014年9月5日 1:14
  • あなたの作っているアプリがどういう構造になっているか書けないのですか?

    先にも書きましたが、今提供されている情報だけで答えるには、回答者はあなたのアプリの構造を想像して、想像をベースに答えるということになります。

    2014年9月5日 1:32
  • Hoshinaです
    こんにちは

    TI-cb400さん
    >DataGridView.DataSource=Nothing
    >の部分で、「値が範囲外です」とのエラーが出ます。

    私の予想ですが、この部分が直接エラーの原因ではない気がします。
    この部分は表示に使用するデータが無い状態にしているだけです。
    おそらく、表示用データが存在することを仮定して動作する部分(たとえば、直前に選択された行のインデックスを記録していて、その値に基づいて何か処理しているような)が存在しませんか?
    おそらくそのような部分が直接のエラーの原因になっている気がします。

    さて、私の文書を読むと、「気がする」「気がする」が目立ちますよね。
    SuferOnWwwさんも書かれていますが、どのような状況にあるかが不明なためにこのような文章になっています。
    問題解決のためにも、どのような状況なのかをより詳しく、正確に伝えてもらえるとお互いにたすかります。

    そのための1手段として、できるだけ「短く」かつ現象を再現できるサンプルを作成して、投稿してみるという方法があります。

    それでは

    2014年9月5日 1:53
  • DataGridView.DataSource=Nothing
    DataGridView.DataSource=DataSet.テーブル名

    TableAdapter.Fill(DataSet.テーブル名,社員名,日付)

    としてみましたが、(この書き方でよいかどうかは、はなはだ不安ですが)

    私の説明が足らなかったようです。一応、私がイメージしていたのは以下の順序です。

    TableAdapter.Fill(DataSet.テーブル名,社員名,日付)
    DataGridView.DataSource=Nothing DataGridView.DataSource=DataSet.テーブル名
    要するにFillした後に、一度Nothingにして再度データテーブルをセットすれば、セットされた時点で再表示するはずだということを期待しています。

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2014年9月5日 2:57
    モデレータ
  • TI-cb400 さま よろしく。

    皆さんがお書きの様に、推測で進めますね。
      次回からは SuferOnWww さまがお書きの様に、事象を再現出来る 雛形 を切り出し、
      利用環境等を明記した上で、ご投稿なさるのをお勧めします。
      最も、この作業をすれば あるいは ご自分で解決できる事も多いとは思いますけれど。

    さて、ご自分の以下の文章にある様に、ひとつの解決方法は、

    1回目にデータを取得した際に、DataGridViewのどこでもよいのでいったん選択をした後で
    Comboboxの値を変更すると、ちゃんと新しく選択した値をパラメータとして、データを取得し、
    DataGridViewの表示も更新されます。

    Fill する前に、DataGridView のポインタ(今指定されているデータの位置)を明示してから、
    目的の作業に移っては如何ですか。
    恐らく、どれも指定されていない無選択の状態で、DataGridView に紐着けられたイベントハンドラが
    悪さをしている(原因 ?)のではないでしょうか。
    デバッグで、ステップ実行すれば、どのルーチンが悪さをしているか、分る筈ですけれど。
    そこを直すか、冒頭書いた様な位置指定をするか、で対処が出来ると思います。

    2014年9月5日 3:55
  • 質問者さんが現れないので、質問者さん個人のやり方ベースでは話が進みませんが・・・

    とりあえず質問者さんのやり方とは離れて(たぶん間違った方向に進んでいると思われるので)、普通に Visual Studio のウィザードで以下のページの図にある構成のアプリを作って、それに ComboBox を追加し、ComboBox の選択の変更と連動して DataGridView の表示を変えるアプリを作ってみたので紹介しておきます。

    Windows フォーム アプリケーションでのデータへの接続
    http://msdn.microsoft.com/ja-jp/library/wxt2cwcc(v=vs.110).aspx

    Northwind サンプルデータベースの Categories テーブルと Products テーブルを使い、ComboBox には Categories テーブルのデータ、DataGridView には ComboBox で選択された Category に属する Products データ一覧を表示することにします。

    まず、以下のページのステップ 1 から 12 まで実行します。ステップ 1 ~ 12 以外の説明は無視してください。そこに書いてあるアプリを作るわけではありません。

    DataGridView に ComboBox を表示
    http://surferonwww.info/BlogEngine/post/2014/01/23/how-to-show-combobox-column-in-datagridview.aspx

    ここまでで DataGridView への Products データの表示、レコードの INSERT, DELETE, UPDATE ができるアプリが完成しているはずです。

    これに ComboBox を追加して、ComboBox の選択の変更と連動して DataGridView の表示を変えるアプリを作るには、ステップ 13 以降を以下のようにすればいいです。

    (13) Visual Studio のデザイン画面で、ツールボックスから ComboBox を BindingNavigator の余白の部分(Save ボタンの右側)にドラッグ&ドロップする。

    (14) 同じくデザイン画面で、ComboBox の▲印(実際には右向き)をクリックすると、[Combobox タスク]ダイアログが開くので、そこでデーターソース、表示メンバー、値メンバーを設定する。(ステップ 11 で作った Categories の方を設定してください)

    (15) Visual Studio のソリューションエクスプローラから NorthwindDataSet.xsd を開き、その中の ProductTableAdapter にツールボックスから Query をドラッグ&ドロップし、WHERE ([CategoryID] = @CategoryID) で DataSet/DataTable を生成するメソッドを作成する(とりあえず  FillByCategoryID, GetDataByCategoryID と言う名前にします)。

    (16) Visual Studio のウィザードで ComboBox の SelectedValueChanged ハンドラを追加する。

    (17) 上に紹介したページのステップ 6 で自動生成されたコード+ステップ (16) で追加したイベントハンドラを以下のように変更する。(ここだけウィザードベースでなく自力でコードを書く必要があります)

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    
    namespace DataGridViewComboBox
    {
        public partial class Form2 : Form
        {
            public Form2()
            {
                InitializeComponent();
                
            }
    
            private void productsBindingNavigatorSaveItem_Click(object sender, EventArgs e)
            {
                this.Validate();
                this.productsBindingSource.EndEdit();
                this.tableAdapterManager.UpdateAll(this.northwindDataSet);
            }
    
            private void Form2_Load(object sender, EventArgs e)
            {
                // TODO: このコード行はデータを 'northwindDataSet.Categories' テーブルに読み込みます。必要に応じて移動、または削除をしてください。
                this.categoriesTableAdapter.Fill(this.northwindDataSet.Categories);
                // TODO: このコード行はデータを 'northwindDataSet.Products' テーブルに読み込みます。必要に応じて移動、または削除をしてください。
                //this.productsTableAdapter.Fill(this.northwindDataSet.Products);
            }
    
            private void comboBox1_SelectedValueChanged(object sender, EventArgs e)
            {
                int index = this.comboBox1.SelectedIndex;
                
                if (index != -1)
                {
                    this.productsTableAdapter.FillByCategoryID(this.northwindDataSet.Products, (int)this.comboBox1.SelectedValue);
                }
            }
        }
    }

    DataTable を Clear するとか、DataSource に Nothing を代入するとか、DataSource に DataTable を再設定するなどの操作は不要です。上記の設定だけで、ComboBox の選択を変えると自動的に DataGridView もそれに応じて書き換わるはずです。

    • 編集済み SurferOnWww 2014年9月5日 9:02 一部訂正
    2014年9月5日 8:36
  • ご回答が遅くなり、申し訳ありません。

    ご提示いただいた内容をすべてテストできていないので、申し訳ありません。
    どのような説明をすればよいか、悩んでおりました。

    一度、状況を整理したいと思います。

    フォームの状況として
    Textbox            2個    日付範囲を指定
    Combobox        1個    社員を選択する
    DataGridView    1個    上記2つの条件によりDBに登録されている勤務時間データから
                        抽出された勤務時間を表示

    やりたいこと
    日付範囲、もしくは社員を変更すると、その条件に合ったデータをDataGridViewに
    表示したい

    作製済みのもの
    勤務時間テーブルからTableAdapterを作成し、同時に作成されるDataTableを
    DataGridViewにバインド
    TableAdapterにクエリを追加(日付範囲、社員を抽出条件とするもの)
    上記をクエリ1とします。

    Combobox、Textboxの変更時にクエリ1をFillして、抽出されたデータを
    DataGridViewに表示。
    この際、1回目のデータ表示は正常にできる。

    わからないこと
    職員を変更すると、FillはされているがDataGridViewにデータが表示されない。
    ただし、1回目のデータが表示された際に、どこでもよいのでDataGridViewの
    セルをクリックするなどして選択をした後、Comboboxの値を変更すると
    ちゃんとデータが表示される。

    という状況です。

    職員数が300名前後なので、日付の範囲によってはデータが多くなるので、
    その都度、DBからデータを取ってきたほうが良いかと思い、上記のような形で
    作製をしてみました。

    このような説明で大丈夫でしょうか。

    2014年9月6日 23:31
  • > ご提示いただいた内容をすべてテストできていないので、申し訳ありません。

    全てテストしてください。Visual Studio のウィザードベースで作るので、慣れてなくでも 2 ~ 3 時間で完成できるはずです。ウィザードになれていれば 1 時間もかからないはず。

    分からないとことがあったら聞いてください。


    > このような説明で大丈夫でしょうか。

    大丈夫ではないです。

    追加情報として検索条件が提供されただけで、肝心のアプリの構造に関する情報は依然として不明です。

    先に書いたように「あなたがどのようにそのアプリを作っているか、アプリの構造がどうなっているか、具体的な情報があなたの質問にはほとんどない」「今提供されている情報だけで答えるには、回答者はあなたのアプリの構造を想像して、想像をベースに答えるということになります」という状況に変わりはないです。

    先のレスで紹介したように、Visual Studio のウィザードを使って定石の構成で作っていれば、質問者さんが詳しく書かなくても、回答者はアプリの構成は分かります。

    でも、質問者さんのアプリは独自実装で、ウィザードベースの定石の構成ではないですよね。

    そうなると、回答者のほうでも問題を再現できる必要最小限のコードを提示してもらうなどをしてもらわないと、質問者さんが独自に実装したアプリの構成など分かるわけはありません。

    独自実装にこだわる必要はないと思いますが、そうであれば先のレスで紹介したような手順で、ウィザードベースで作ってください。

    Northwind を持っていなければ AdventureWorks でもいいです。回答者のほうでも入手可能な DB をベースにしてウィザードベースで作ってください。

    それをベースに話をすれば、回答者の方も構成は分かるので、話が早いと思います。

    2014年9月7日 0:27
  • 現状の報告です。

    SurferOnWww様がご提示していただいたうちの追加のコードの前でつまずいています。

    自分でDataTableのコードを記述し、Comboboxにセットする段階でエラーになります。
    DataGridViewComboboxCellの値が有効ではありません。となります。

    コードを比較しているのですが、間違っている部分が現状わかりません。

    ちょっと時間をおいて、見直しをしてみたいと思います。

    また、よろしければ、「アプリの構造」とはどのようなことをさすかを教えていただけないでしょうか。
    正直なところ、何を説明すればよいかわからない状態です。

    以下、現時点のコードです。

    Imports System.Data.SqlClient

    Public Class Form1

        Private Sub ProductsBindingNavigatorSaveItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ProductsBindingNavigatorSaveItem.Click
            Me.Validate()
            Me.ProductsBindingSource.EndEdit()
            Me.TableAdapterManager.UpdateAll(Me.NorthwindDataset)

        End Sub

        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            'TODO: このコード行はデータを 'NorthwindDataset.Categories' テーブルに読み込みます。必要に応じて移動、または削除をしてください。
            Me.CategoriesTableAdapter.Fill(Me.NorthwindDataset.Categories)
            'TODO: このコード行はデータを 'NorthwindDataset.Products' テーブルに読み込みます。必要に応じて移動、または削除をしてください。
            Me.ProductsTableAdapter.Fill(Me.NorthwindDataset.Products)

            Me.DataGridViewTextBoxColumn4.DataSource = CreateSupliersDataTable()
            Me.DataGridViewTextBoxColumn4.DisplayMember = "CompanyName"
            Me.DataGridViewTextBoxColumn4.ValueMember = "Suppeliers"
        End Sub

        Private Function CreateSupliersDataTable() As DataTable
            Dim table As New DataTable("Supplier")

            table.Columns.Add(New DataColumn("Suppliers", Type.GetType("System.Int32")))
            table.Columns.Add(New DataColumn("CompanyName", Type.GetType("System.string")))

            Dim connString As String = My.MySettings.Default.NorthwindConnectionString

            Dim Query As String = _
             "SELECT SupplierID, CompanyName FROM Suppliers " + _
             "ORDER BY SupplierID"

            Using Conn As SqlConnection = New SqlConnection(connString)

                Conn.Open()

                Using cmd As SqlCommand = New SqlCommand(Query, Conn)

                    Using reader As SqlDataReader = cmd.ExecuteReader

                        If reader IsNot DBNull.Value Then

                            While (reader.Read())


                                Dim row As DataRow = table.NewRow()


                                row("Suppliers") = reader.GetInt32(0)   ←ここでエラー
                                row("CompanyName") = reader.GetString(1)

                                table.Rows.Add(row)
                            End While
                        End If
                    End Using
                End Using
            End Using

            Return table
        End 関数

    End クラス

    2014年9月7日 12:09
  • > コードを比較しているのですが、間違っている部分が現状わかりません。

    先の説明で「ステップ 1 ~ 12 以外の説明は無視してください。そこに書いてあるアプリを作るわけではありません。」と書きましたが、読んでないのでしょうか? 読んでいれば、ステップ 1 ~ 12 以外で作る関係ないコードをコピペするなんてことはしないはずですけど。

    それとも、読んでも理解できなかったので、とりあえずそこに書いてあったコードをコピペしてやってみたのでしょうか? どっちにしてもダメです。前にも書きましたが、分からないとことがあったら聞いてください。理解できないままテキトーにやるのは最悪です。


    >「アプリの構造」とはどのようなことをさすかを教えていただけないでしょうか。

    ソースコードもしくはそれと同等な情報です。

    Hoshina さんが述べておられるように「そのための1手段として、できるだけ『短く』かつ現象を再現できるサンプルを作成して、投稿してみるという方法があります。」がベストな方法でしょう。

    回答をもらったら、よく読んでください。

    2014年9月8日 1:15
  • DataGridViewにバインドしているのはデータテーブルでしょうか? であれば、データテーブルのデータが変わってもその変更はDataGridViewに伝わらなかったと思います。よって、DataSourceに一度nothingをセットし、再度DataSourceを指定すれば、そのタイミングでデータテーブルを読むはずなので変更後のデータが表示されると思います。

    すみません。ウソ言ってました。以下のコードで問題なくDataGridViewの表示が切り替わります。逆にTI-cb400さんの現象が再現するコードに絞って行かれると、原因がつかめると思います。

    Imports test2010vb.TESTDataSetTableAdapters
    Imports test2010vb.TESTDataSet
    
    Public Class DataGridView検索
    
        Dim TA職員 As T00職員TableAdapter = New T00職員TableAdapter()
        Dim TA勤怠時間 As T00勤怠時間TableAdapter = New T00勤怠時間TableAdapter()
    
        Dim DT職員 As T00職員DataTable = New T00職員DataTable()
        Dim DT勤怠時間 As T00勤怠時間DataTable = New T00勤怠時間DataTable()
    
        Public Sub New()
    
            ' この呼び出しはデザイナーで必要です。
            InitializeComponent()
    
            ' InitializeComponent() 呼び出しの後で初期化を追加します。
    
            TA職員.Fill(DT職員)
    
            comb_職員.DataSource = DT職員
            comb_職員.ValueMember = "職員ID"
            comb_職員.DisplayMember = "職員名"
    
            dtgv_勤怠.DataSource = DT勤怠時間
    
            'comb_職員の初回設定後にイベントハンドラを設定する。
            'そうしなければ、comb_職員の初回設定後にすぐにイベントハンドラが実行されてしまうため。
            AddHandler comb_職員.SelectedIndexChanged, AddressOf comb_職員_SelectedIndexChanged
    
        End Sub
    
    
        Private Sub bttn_検索_Click(sender As System.Object, e As System.EventArgs) Handles bttn_検索.Click
            検索()
        End Sub
    
        Private Sub comb_職員_SelectedIndexChanged(sender As System.Object, e As System.EventArgs)
            検索()
        End Sub
    
        Private Sub 検索()
            TA勤怠時間.Fill(DT勤怠時間, comb_職員.SelectedValue, txtb_勤務日開始.Text, txtb_勤務日終了.Text)
        End Sub
    
    End Class


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


    • 編集済み trapemiyaModerator 2014年9月8日 1:42 文章が途中で切れていたのを追加
    2014年9月8日 1:15
    モデレータ
  • 問題の個所が見つかりました。

    SurferOnWww様がご呈示いただいた内容を実施して、Comboboxの値をキーにして抽出を行うことが
    できました。

    私が作成した内容も、trapemiya様がご提示いただいたものとほぼ同じ構造です。

    で、原因となっていたコードが下記の部分です。

    Private Sub DataGridView_CellEnter(ByVal sender As Object, _
                ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles Me.CellEnter
            Dim dgv As CDataGridView = CType(sender, CDataGridView)

            dgv.BeginEdit(True)
        End Sub

    この部分をコメントアウトしたら、SurferOnWww様がご呈示していただいたコードと同じ動作をするようになりました。

    自分でも、なぜこのコードを記述したか全く定かではなかったので、全く気が付いていませんでした。

    いろいろとアドバイスを頂いた皆様、本当にありがとうございました。

    今後は、質問の仕方も勉強をしたいと思います。

    2014年9月9日 9:41
  • > 自分でも、なぜこのコードを記述したか全く定かではなかったので、全く気が付いていませんでした。

    そんなコードがあるなんてことは回答者の誰も知り得ないということは理解できますよね。

    先のレスで、

    > Hoshina さんが述べておられるように「そのための1手段として、できるだけ『短く』かつ現象を
    > 再現できるサンプルを作成して、投稿してみるという方法があります。」がベストな方法でしょう。

    と述べた意味が分かっていただけたでしょうか?

    以前にも言ったと思いますが、Visual Studio のウィザードを使って作れるものはウィザードを使って作ることをお勧めします。


    > 今後は、質問の仕方も勉強をしたいと思います。

    是非そうしてください。最低限のマナーとして。

    2014年9月9日 9:58