none
DataGridView綁定BindingSource後,DataGridView排序後新增問題 RRS feed

  • 問題

  • 各位前輩好

    我在 WinForm 中將 DataGridView 綁定 BindingSource,可以正常將新增、刪除及修改的資料傳回資料庫

    但是只要將 DataGridView 排序後,新增時 BindingSource 的 Current 不會指向 BindingSource.AddNew() 的那筆資料(取 Current 之前我有先 EndEdit ),而是 BindingSource 的最後一筆,導致回無法新增到資料庫。

    我在網路上有找到微軟的說明,它的因應措施說在BindingSource.ListChanged 事件加入以下片段

    if ((e.ListChangedType == ListChangedType.ItemAdded) && ((BindingSourceData.Count - e.NewIndex) > 1))
              BindingSourceData.Position = e.NewIndex;

    我設定之後,排序後確定可以新增, BindingSource 會取得正確的Current,但只能新增一次。當我要新增第二筆時會傳回資料行被限制為唯一,值已經存在。

    (重新說明↓)

    我新增成功的第一筆完成後,新增的資料第一筆資料有寫回資料庫,然後有回傳新增資料錄的pk並回寫到DataGridView上,可是回寫BindingSource的時候,卻還是更新到新增前的最後一筆(將原資料最後的pk覆寫為空白)。所以在新增第二次時,呼叫BindingSource.EndEdit()時就出現pk不是唯一值。

    我想請問各位前輩該如何處理?


    • 已編輯 tsshinyo 2014年3月7日 上午 02:13 述敘修改
    2014年3月5日 上午 10:14

解答

  • 不好意思,麻煩大家

    剛剛在測試的專案中加入

    private void bsData_ListChanged(object sender, ListChangedEventArgs e)
            {
                if ((e.ListChangedType == ListChangedType.ItemAdded) && ((bsData.Count - e.NewIndex) > 1))
                {
                    bsData.Position = e.NewIndex;
                }
            }

    新增可以正常,我會回去看原本專案哪裡有問題

    非常抱歉

    謝謝大家熱心回覆

    • 已標示為解答 tsshinyo 2014年3月18日 上午 03:57
    2014年3月18日 上午 03:56

所有回覆

  • 您可以利用BindingSource.AddNew方法的傳回值取得新加入的記錄, 再拿來設定Current屬性
    2014年3月5日 下午 12:33
  • tihs 你好,謝謝你的回覆

    但我問題沒有寫清楚

    (重新說明↓)

    在依照微軟的因應措施修改後,新增的資料第一筆資料有寫回資料庫,然後有回傳新增資料錄的pk並回寫到DataGridView上,可是回寫BindingSource的時候,卻還是更新到新增前的最後一筆(將原資料最後的pk覆寫為空白)。所以在新增第二次時,呼叫BindingSource.EndEdit()時就出現pk不是唯一值。

    我看有人討論說在 DataGridView 上排序的結果,只是呈現給USER看,BindingSource 並沒有被排序,所以對不起來。

    我有在DataGridView  的 Sorted 事件重新 BindingSource 排序,但還是不行。

    有人能給我其他建議嗎?謝謝


    • 已編輯 tsshinyo 2014年3月7日 上午 02:11 述敘修正
    2014年3月6日 上午 08:40
  • 用 DbDataAdapter.Update 來更新資料庫.

    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2014年3月6日 上午 09:14
    版主
  • 呼叫BindingSource.AddNew新增的記錄本來就不會寫入資料庫, 要寫入資料庫要另外呼叫TableAdapter的Update方法
    2014年3月6日 上午 10:34
  • Bill 您好,謝謝您的回覆

    新增第一筆時,確實是有更新到資料庫的。在用了微軟的方法前,原本取到 BindingSource.Current 是新增前的最後一筆,所以新增失敗,因為 DataRowState 就不是Added。但用了微軟的方法後,取到的 BindingSource.Current 就是新增的那筆資料列。在儲存時,資料庫也可以正常新增並回傳pk,DataGridView 新增的那列有回寫pk值,可是 BindingSource新憎前的最後一筆內容卻被修改為 BidningSource.AddNew 的資料內容(資料庫沒有被修改,只有顯示在 DataGridView 上)。所以在新增第二筆時,就因為呼叫 BindingSource.EndEdit 時出錯。

    我想問的是,為什麼pk值正確回傳到 DataGridView 新增的那列之後, BindingSource 也更新了新增前的最後一筆(這筆就是用微軟的方式前取得的 BindingSource.Current),等於畫面上我新增的一筆,同時被更新了兩筆,一筆是新增的(資料庫也有新增),一筆是原始最後一筆(資料庫沒有被異動)。

    這有什麼方式可以解決嗎?

    tihs 您好,謝謝您的回覆

    在 EndEdit 之後,有取得 BidningSource‧Current,去判斷 DataRowState,然後另外寫新增或修改到資料庫。我的問題是用了微軟的方式之後,雖然可以新增,但它同時更新了 DataGridView上的兩筆資料,其中一筆是不應該被更新的,但它被更新了,而且造成我無法新增第二次。我該如何讓 DataGridView 和 BindingSource取得相同的資料列?


    • 已編輯 tsshinyo 2014年3月7日 上午 02:54 錯字修正
    2014年3月7日 上午 02:31
  • 在另外執行新增或修改資料庫的動作之後必須將BindingSource中成功更新到資料庫的記錄的DataRowState變更成Unchanged, 否則會影響下一次更新資料庫的動作

    2014年3月7日 下午 01:35
  • tihs 您好

    我確認過 BindingSource 的 DataRowState 是Unchanged。

    且如同我之前說的,在未使用 DataGridView 的標題列排序之前,新增、刪除及修改都是正常的。

    只有在使用排序功能之後,才會造成新增的錯誤

    有其他造成錯誤的可能嗎?

    (增加說明:)

    在使用微軟的因措施之後,因為重新指定 BindingSource.Position

    所以將原來 Position 指向的內容變成跟 重新指定的 BindingSource.Position 內容一樣(只有在 DataGridView 顯示上,且)

    所以才會變成有兩個pk導致無法新增第二次

    • 已編輯 tsshinyo 2014年3月10日 上午 10:38 增加說明
    2014年3月10日 上午 01:52
  • 我試了一下, 並沒有不能新增, 修改, 刪除的問題

     
     public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
               
            }
    
            private DataSet dataset = new DataSet();
            SqlDataAdapter adapter;
            private void Form1_Load(object sender, EventArgs e)
            {
                GetData("SELECT [ID],[Name]  FROM [Personal]", "Personal");
    
                BindingSource source = new BindingSource();
                source.DataSource = dataset.Tables["Personal"];
                dataGridView1.DataSource = source;
            }       
    
            private void GetData(string commandstring, string tablename)
            {
                string connectionstring = @"Persist Security Info=False;Integrated Security=True;Initial Catalog=StudentDB;Server=.\SQLEXPRESS";
                SqlConnection connection = new SqlConnection(connectionstring);
                SqlCommand command = new SqlCommand(commandstring, connection);
                DataTable datatable = new DataTable();
                adapter = new SqlDataAdapter();
                adapter.SelectCommand = command;
    
                adapter.InsertCommand = new SqlCommand("INSERT INTO [Personal]  ([ID]  ,[Name]) VALUES  (@ID  ,@Name) ", connection);
                adapter.InsertCommand.Parameters.Add("@ID", SqlDbType.NVarChar, 10, "ID");
                adapter.InsertCommand.Parameters.Add("@Name", SqlDbType.NVarChar, 50, "Name");
    
                adapter.UpdateCommand = new SqlCommand("UPDATE [Personal] SET [ID] = @ID ,[Name] = @Name WHERE [ID]=@OID AND [Name]=@OName", connection);
                adapter.UpdateCommand.Parameters.Add("@ID", SqlDbType.NVarChar, 10, "ID");
                adapter.UpdateCommand.Parameters.Add("@Name", SqlDbType.NVarChar, 50, "Name");
                adapter.UpdateCommand.Parameters.Add("@OID", SqlDbType.NVarChar, 10, "ID").SourceVersion =  DataRowVersion.Original ;
                adapter.UpdateCommand.Parameters.Add("@OName", SqlDbType.NVarChar, 50, "Name").SourceVersion = DataRowVersion.Original;
    
    
                adapter.DeleteCommand = new SqlCommand("DELETE FROM [Personal] WHERE [ID]=@ID", connection);
                adapter.DeleteCommand.Parameters.Add("@ID", SqlDbType.NVarChar, 10, "ID").SourceVersion = DataRowVersion.Original; 
    
                adapter.Fill(dataset, tablename);
    
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
               
                adapter.Update(dataset.Tables["Personal"]);
                MessageBox.Show("Update");
            }
        }


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2014年3月11日 上午 03:08
    版主
  • Bill 您好

    在 DataGridView 綁定 BindingSource之後

    我的程式可以正常新增、刪除、修改

    但是對 DataGridView 排序之後,再做新增就會有問題

    所以我才想知道,使用 DataGridView 排序後,要如何才可以正常新增

    2014年3月17日 上午 05:45
  • 我排序後還是正常新增修改刪除, 基本上排序並不會影響 DataTable , 他影響的是 DataView

    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2014年3月17日 上午 07:00
    版主
  • Bill 您好,感謝您一直以來的幫忙

    因為 DataGridView 綁定 BindingSource 後,我是針對 BindingSource操作

    在 BindingSource 新增後,要同步更新 DataSet

    我有重寫一個範例,可以麻煩您看一下哪裡有問題嗎?

    一樣是在未排序狀態下可以正常一直新增,但一排序後,取得的 Position 就會錯,會導致內容有誤

    code 如下:

     
    public partial class Form1 : Form
        {
            private DataSet dsData = new DataSet();
            public Form1()
            {
                InitializeComponent();
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
                GetData();
            }
    
            private void btnNew_Click(object sender, EventArgs e)
            {
                bsData.AddNew();
            }
    
            private void btnSave_Click(object sender, EventArgs e)
            {
                bsData.EndEdit();
    
                DataRow dr = ((DataRowView)bsData.Current).Row;
    
                if (dsData.HasChanges())
                {
                    SaveData(dr);
                }
    
                dsData.AcceptChanges();
            }
    
            private void GetData()
            {
                Database db = DatabaseFactory.CreateDatabase();
                DbCommand MyCommand = db.GetSqlStringCommand("SELECT ID, Name, Sort FROM Personal");
    
                    dsData = db.ExecuteDataSet(MyCommand);
                    if (dsData != null && dsData.Tables.Count > 0)
                    {
                        dsData.Tables[0].TableName = "Personal";
                        bsData.DataSource = new BindingSource(dsData, "Personal");
                        dgvData.DataSource = bsData;
    
                        txtID.DataBindings.Add("Text", bsData, "ID");
                        txtName.DataBindings.Add("Text", bsData, "Name");
                        txtSort.DataBindings.Add("Text", bsData, "Sort");
                    }
            }
    
            private void SaveData(DataRow dr)
            {
                Database db = DatabaseFactory.CreateDatabase();
                DbCommand MyCommand = db.GetStoredProcCommand("Presonal_Insert");
    
                db.AddInParameter(MyCommand, "Name", DbType.String, dr["Name"].ToString());
                db.AddInParameter(MyCommand, "Sort", DbType.Int32, Convert.ToInt32(dr["Sort"].ToString()));
                db.AddOutParameter(MyCommand, "ID", DbType.String, 5);
    
                db.ExecuteNonQuery(MyCommand);
                dr["ID"] = db.GetParameterValue(MyCommand, "@ID").ToString();
            }
        }

    //SQL Presonal_Insert 內容

    create PROCEDURE [dbo].[Presonal_Insert]
    (
    @ID VARCHAR(5) OUTPUT
    ,@Name VARCHAR(10)
    ,@Sort INT
    )
    AS
    BEGIN
    DECLARE @NUMBER INT
    SELECT @NUMBER=ISNULL(MAX(CONVERT(INT,RIGHT(ID,4),0)),0)+1 FROM Personal
    SET @ID = 'A'+RIGHT('0000'+CAST(@NUMBER AS VARCHAR(4)),4)
    
    INSERT INTO Personal(ID,Name,Sort)
    VALUES(@ID,@Name,@Sort)
    END


    測試後和之前結果一樣,是因為排序後取到錯誤的 Position

    是我哪裡有做錯嗎?


    • 已編輯 tsshinyo 2014年3月18日 上午 03:46 格式化內容
    2014年3月18日 上午 03:44
  • 不好意思,麻煩大家

    剛剛在測試的專案中加入

    private void bsData_ListChanged(object sender, ListChangedEventArgs e)
            {
                if ((e.ListChangedType == ListChangedType.ItemAdded) && ((bsData.Count - e.NewIndex) > 1))
                {
                    bsData.Position = e.NewIndex;
                }
            }

    新增可以正常,我會回去看原本專案哪裡有問題

    非常抱歉

    謝謝大家熱心回覆

    • 已標示為解答 tsshinyo 2014年3月18日 上午 03:57
    2014年3月18日 上午 03:56
  • 你把結構弄得太複雜了吧. 在 DataGridView 和 BindingSource 中間那個 bsData 又是甚麼東西 ?

    就算要對 BindingSource 操作, 你也可以操作 BindingSource  的 DataSource 吧 ?

    所以可以改成 adapter.Update((DataTable)source.DataSource);


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。


    2014年3月18日 上午 04:00
    版主
  • Bill 您好

    因為這個架構是由 Base From 繼承來的,讓其他 From 可以省去設定的麻煩

    在 Base From 先定義好要用的元件及事件,可以減少其他 From 的設定,加快設計步驟

    所以新增、刪除、修改都寫好在 Base From,然後在其他 From 建好 DataGridView 後,再綁定 Base From 的 BindingSource

    可以省去重寫新增、刪除、修改。

    另外,上面的測試專案我原本沒有用DataSet,可是這樣新增時,Textbox 無法綁回 DataGridView(也許是我少做了什麼)

    只有將新增完成的 pk(ID)值回寫到 DataGridView

    所以才把 DataSet 綁在 BindingSource上,這樣新增好,使用 AcceptChanges 才會更新 DataGridView

    2014年3月18日 上午 07:25
  • bsData.DataSource = new BindingSource(dsData, "Personal");

    我對這行一直很有疑問, 如果 bsData 是個Bindingsource 型別, 那架構就變成

    DataGridView -> BindingSource ->BindingSource -> DataTable
    用兩個 BindingSource ? 這不是很怪嗎 ?


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2014年3月18日 上午 07:43
    版主
  • Bill 您好

    我把 bsData.DataSource = new BindingSource(dsData"Personal")

    換成 bsData.DataSource = dsData.Tables["Personal"]; 也是可以的

    我會試試專案裡是不是因為多綁一層才會取錯值

    2014年3月19日 上午 01:47