一、說明
1.數據源DataSet是保存數據,以及發布數據更新通知的核心。
2.BindingSource,是控件(DataGridView / BindingNavigator)與數據源交互的橋梁。BindingSource的DataSource指向數據源DataSet之后,數據源或控件的數據更改,則會被BindingSource傳遞到另一頭。
3.DataGridView / BindingNavigator是數據控件,它們應該從BindingSource獲取數據。當數據控件的數據,被用戶更改后,則會通知BindingSource,接着BindingSource再通知數據源DataSet。
因此,這些組件綁定與更新的順序為:
1.創建DataSet
2.把BindingSource的BindingSource綁定到DataSet。
3.把DataGridView的DataSource,以及BindingNavigator的BindingSource,都綁定到BindingSource。
關鍵:如果DataSet的數據發生變化,或者DataGridView / DataSource的數據發生變化,則雙向的更改是自動進行與傳遞的。
但如果DataSet被替換,則需要把BindingSource的BindingSource重新綁定到DataSet。DataGridView 與 BindingNavigator則不需要更改,因為BindingSource會自動通知他們。
同理,如果DataGridView的DataSource,以及BindingNavigator的BindingSource被替換,也就是他們的BindingSource被替換,則DataSet也需要重新綁定到新的BindingSource中。注意,DataGridView的DataSource,以及BindingNavigator的BindingSource,應該被替換為同一個BindingSource,這樣他們才能具有可交互性。
二、例子
代碼:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication4 { public partial class Form1 : Form { DataSet ds = new DataSet(); string conn = "Data Source = (local)\\SQLEXPRESS; Initial Catalog = northwind; Persist Security Info = True;User ID=sa;Password=6617saSA"; string sql = "SELECT CustomerID 編號,CompanyName ,ContactName ,ContactTitle ,Address ,City FROM Customers ;"; SqlDataAdapter sda ; BindingSource bindingsource1 ; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { dataGridView1.AllowUserToAddRows = false; //去掉DataGridView的最后一個空行 bindingsource1 = new BindingSource(); sda = new SqlDataAdapter(sql, conn); sda.Fill(ds, "Customers"); bindingsource1.DataSource = ds; bindingsource1.DataMember = ds.Tables[0].TableName; bindingNavigator1.BindingSource = bindingsource1; dataGridView1.DataSource = bindingsource1; //textBox_ID.DataBindings.Add("text", bs, "編號",false, DataSourceUpdateMode.OnPropertyChanged); //textBox_name.DataBindings.Add("text", bs, "CompanyName", false, DataSourceUpdateMode.OnPropertyChanged); textBox_ID.DataBindings.Add("text", bindingsource1, "編號"); textBox_name.DataBindings.Add("text", bindingsource1, "CompanyName"); textBox_ContactName.DataBindings.Add("text", bindingsource1, "ContactName"); textBox_ContactTitle.DataBindings.Add("text", bindingsource1, "ContactTitle"); textBox_Address.DataBindings.Add("text", bindingsource1, "Address"); textBox_City.DataBindings.Add("text", bindingsource1, "City"); } private void toolStripButton1_Click(object sender, EventArgs e) { //該語句使修改cell內容后直接保存有效,否則需要移動到其它CELL再保存才有效 dataGridView1.CurrentCell = null; //將控制修改內容更新到DataSet基礎數據,如果沒有EndEdit() , ds.GetChanges() == null永遠成立 ((BindingSource)dataGridView1.DataSource).EndEdit(); if (ds.GetChanges() != null) { //沒有下一條會出現錯誤:當傳遞具有新行的 DataRow 集合時,Update 要求有效的 InsertCommand SqlCommandBuilder sb1 = new SqlCommandBuilder(sda); sda.Update(ds.Tables["Customers"]);//更新數據庫 ds.AcceptChanges(); } } private void label2_Click(object sender, EventArgs e) { } private void textBox_ContactName_TextChanged(object sender, EventArgs e) { } } }
三、DataTable的AcceptChange方法為什么不能在Update之前?
總結:DataTable.AcceptChanges相當於將DataTable表中的所有DataRow的RowState狀態 重置為Unchanged
DataTable.RejectChanges方法:回滾自該表加載以來或者上次調用AcceptChanges以來對該表進行的所有更改;並且DataTable表中的所有DataRow的RowState狀態 重置為Unchanged
例子:
DataTable dataTable = ds.Tables[0];
dataTable.Rows[0][0] = 96.6669;//此時的RowState為Modied
dataTable.AcceptChanges();//此時的RowState為Unchanged
dataTable.Rows[0][0] = 7777;此時的RowState為Modied此時的RowState為Modied
dataTable.RejectChanges();//此時的dataTable.Rows[0][0] 為96.6669,RowState為Unchanged
//最后 Update需要注意的是;防止並發性的操作。有在Update執行之前所包含的數據行有被修改,則會發生並發性操作錯誤。
da.Update(dataTable);
解決並發性辦法:
if (dataTable.GetChanges() != null)
{
da.Update(dataTable.GetChanges());
}
AcceptChanges方法會將所有改動保存到DataSet或DataTable中,使得所有行的狀態都是Unchanged(沒有被更改狀態)
而DataAdapter.Update方法在保存數據到數據庫表時做過一個檢查,即檢查表行是否被修改過,如果沒被修改過,那么更需將不會執行任何命令,直接跳過本行,開始檢查下一行,如此,一個表如果行都是Unchanged狀態,那么它就不會被更新到數據庫中。
所以,在更改了DataSet或DataTable后,若想調用DataAdapter.Update方法直接更新數據到數據庫,那么你只需要這個Update方法,無需在此前調用一次AcceptChanges方法了。
Added
該行已添加到 DataRowCollection 中,AcceptChanges尚未調用。
Deleted
該行已通過 DataRow 的 Delete 方法被刪除。
Detached
該行已被創建,但不屬於任何 DataRowCollection。DataRow 在以下情況下立即處於此狀態:創建之后添加到集合中之前;或從集合中移除之后。
Modified
該行已被修改,AcceptChanges 尚未調用。
Unchanged
該行自上次調用 AcceptChanges 以來尚未更改。
DataTable.AcceptChanges方法:提交自上次調用AcceptChanges以來對該表進行的所有更改。
調用AcceptChanges時,任何仍處於編輯模式的DataRow對象將成功結束其編輯。DataRowState也發生更改:所有Added和Modified行成為Unchanged;Deleted行被移除。
在您嘗試使用DbDataAdapter.Update方法更新DataSet之后,通常會對DataTable調用AcceptChanges方法。
DataTable.RejectChanges方法:回滾自該表加載以來或上次調用AcceptChanges以來對該表進行的所有更改。
調用RejectChanges時,任何仍處於編輯模式的DataRow對象將取消其編輯。新行被移除。DataRowState設置為Modified或Deleted的行返回到其初始狀態。
會出現對DataTable進行多次更改,但是通過調用RejectChanges方法拒絕這些更改的現象
DataRow.BeginEdit方法:對DataRow對象開始編輯操作。
使用BeginEdit方法將DataRow置於編輯模式。在此模式中,事件被臨時掛起,以便允許用戶在不觸發驗證規則的情況下對多行進行多處更改。例如,如果需要確保總數列的值等於某行中借貸列的值,則可以將每一行都置入編輯模式,以便在用戶嘗試提交值之前掛起對行值的驗證。
BeginEdit方法在用戶更改數據綁定控件的值時被隱式調用;EndEdit方法在您調用DataTable對象的 AcceptChanges方法時被隱式調用。