2016-08-01 36 views
1

我一直在試圖解決一個令人沮喪的問題,就Winforms中的數據綁定問題。在同一個數據源中每行數據綁定一個控件

我有一個數據源,這是一個DataTableDataSet。這DataTable有三排。我的表單上有三個CheckBox控件,還有一個Button。我想將每個CheckBox綁定到此數據源中的一行,並且只要更新CheckBox,數據源就會反映Checked屬性中的值。我還希望通過致電HasChanges()和致電GetChanges()正確接收這些更改。

當點擊ButtonEndCurrentEdit()將被調用並且該數據源,其被結合到,並DataSet檢查用於使用HasChanges()方法的變化。

但是,在我嘗試這樣做時,在撥打EndCurrentEdit()後遇到兩種情況之一。

在第一種情況下,只有第一個CheckBox檢測到其更改。在第二種情況下,將所有其他CheckBoxes更新爲CheckBox的值,該值是在致電EndCurrentEdit()時最後一次檢查的值。

在調用EndCurrentEdit()之後,在方案1中查看RowState值後,只有第一行的狀態爲Modified。在情景2中,只有第三行的狀態爲Modified。對於情景2,用戶是否更新了第三個CheckBox並不重要。

爲了演示我的問題,我創建了一個演示它的簡單示例。

這是一個包含三個CheckBox控件和一個Button控件的股票Windows窗體,全部都使用它們的默認名稱。

Option Strict On 
Option Explicit On 

Public Class Form1 

    Public ds As DataSet 

    Public Sub New() 

     ' This call is required by the designer. 
     InitializeComponent() 

     ' Add any initialization after the InitializeComponent() call. 

     ds = New DataSet() 
     Dim dt As New DataTable() 

     dt.Columns.Add("ID", GetType(Integer)) 
     dt.Columns.Add("Selected", GetType(Boolean)) 

     dt.Rows.Add(1, False) 
     dt.Rows.Add(2, False) 
     dt.Rows.Add(3, False) 

     dt.TableName = "Table1" 

     ds.Tables.Add(dt) 

     ds.AcceptChanges() 

     For i As Integer = 1 To 3 

      Dim bs As New BindingSource() 

      'After the call to Me.BindingContext(ds.Tables("Table1")).EndCurrentEdit() there are two scenarios: 
      'Scenario 1 - only changes to the first CheckBox are detected. 
      'Scenario 2 - when any CheckBox is checked, they all become checked. 

      'Uncomment the first and comment out the second to see Scenario 1. 
      'Uncomment the second and comment out the first to see Scenario 2. 
      'bs.DataSource = New DataView(ds.Tables("Table1")) 'Scenario 1 
      bs.DataSource = ds.Tables("Table1") 'Scenario 2 
      bs.Filter = "ID=" & i 

      Dim db As New Binding("Checked", bs, "Selected") 
      db.DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged 

      If i = 1 
       CheckBox1.DataBindings.Add(db) 
      ElseIf i = 2 
       CheckBox2.DataBindings.Add(db) 
      ElseIf i = 3 
       CheckBox3.DataBindings.Add(db) 
      End If 
     Next 
    End Sub 


    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click 
     Me.BindingContext(ds.Tables("Table1")).EndCurrentEdit() 

     If ds.HasChanges() 
      MessageBox.Show("Number of rows changed: " & ds.GetChanges().Tables("Table1").Rows.Count) 
      ds.AcceptChanges() 
     End If 
    End Sub 
End Class 

我已經做了大量的搜索,但一直沒有能夠解決發生了什麼事情,因此完全喪失了工作。感覺就像我想要做的事很簡單,但我懷疑我一定是誤解了某個地方或者錯過了一些關於綁定過程的重要事情。

編輯

它已經在第二段,但只是爲了說清楚,這裏是我想要做的基本輪廓:

  1. 我有一個包含DataSet具有值的DataTable。對於這個例子,有兩列。 ID可能是任何Integer,'Selected'可能是任何Boolean的值。這些都不會是NothingDBNull
  2. 我想將每一行綁定到一個Checkbox控件。一個CheckBoxID值。 Checked屬性應該綁定到DataTable中的Selected列。
  3. 進行更改時,用戶點擊Button,我希望能夠告訴什麼樣的變化做出的用戶,使用DataSet(即2行更新的HasChanges()GetChanges()方法,如果用戶已經改變了Checked財產Checkbox控件中的兩個控件)。

EDIT 2

感謝@RezaAghaei我想出了一個解決方案。我從我的問題示例中提煉出了代碼,並根據數據生成了所有控件(並相應地定位它們),以使此示例可以簡單地複製和粘貼。此外,這在DataView上使用RowFilter,而不是BindingSourcePosition財產。

Option Strict On 
Option Explicit On 

Public Class Form1 

    Public ds As DataSet 

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load 
     ds = New DataSet() 
     Dim dt As New DataTable() 

     dt.Columns.Add("ID", GetType(Integer)) 
     dt.Columns.Add("Selected", GetType(Boolean)) 

     dt.Rows.Add(1, False) 
     dt.Rows.Add(2, False) 
     dt.Rows.Add(3, False) 

     dt.TableName = "Table1" 
     ds.Tables.Add(dt) 
     ds.AcceptChanges() 

     AddHandler dt.ColumnChanged, Sub(sender2 As Object, e2 As DataColumnChangeEventArgs) 
              e2.Row.EndEdit() 
             End Sub 

     For i As Integer = 0 To dt.Rows.Count-1 
      Dim cb As New CheckBox() With {.Text = "CheckBox" & i+1, .Location = New Point(10, 25 * (i))} 

      Dim dv As New DataView(dt) 
      dv.RowFilter = "ID=" & DirectCast(dt.Rows(i)(0), Integer) 

      Dim bs As New BindingSource(dv, Nothing) 
      Dim db As New Binding("Checked", bs, "Selected") 

      db.DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged 
      cb.DataBindings.Add(db) 

      Me.Controls.Add(cb) 
     Next 

     Dim btn As New Button() 
     btn.Location = New Point(10, 30 * dt.Rows.Count) 
     btn.Text = "Submit" 
     AddHandler btn.Click, AddressOf Button1_Click 
     Me.Controls.Add(btn) 
    End Sub 


    Private Sub Button1_Click(sender As Object, e As EventArgs) 
     'Me.BindingContext(ds.Tables("Table1")).EndCurrentEdit() 'Doesn't cut the mustard! 

     If ds.HasChanges() 
      MessageBox.Show("Number of rows changed: " & ds.GetChanges().Tables("Table1").Rows.Count) 
      ds.AcceptChanges() 
     Else 
      MessageBox.Show("Number of rows changed: 0") 
     End If 
    End Sub 

End Class 

的關鍵是在ColumnChanged事件調用EndEdit()DataTable(以EndCurrentEdit()一般號召只是似乎沒有削減它),但是我遇到了一個額外的問題是,該代碼將無法正常工作如果它的格式爲New()方法,即使它是在撥打InitializeComponent()之後。我猜這是因爲Winforms在調用New()之後做了一些初始化操作,這是數據綁定正常工作所必需的。

我希望這個例子能夠節省其他人花時間研究這個問題的時間。

+0

@RezaAghaei我曾希望這是很清楚的了,但我已經添加了一個額外的對更多clari的第二段判決TY。編輯:你不斷更新你的評論!什麼網格?這裏沒有網格控件。 – Interminable

+0

@RezaAghaei這是一個我正在經歷的行爲的簡單功能示例。該按鈕調用'EndCurrentEdit()'。在一個更大的項目中,這將隨後處理用戶輸入的數據。我在這裏沒有對數據做任何事情,因爲它是無意義的測試數據。我沒有在網格中顯示控件,因爲...爲什麼我需要在網格中顯示它們? – Interminable

+0

@RezaAghaei實際上,控件是動態生成的,但與示例問題的示例無關。上面的代碼旨在成爲[最小,完整和可驗證示例](http://stackoverflow.com/help/mcve)。而已。 – Interminable

回答

1

考慮這些修正和問題就會得到解決:

  • 到控制綁定到的列表的特定索引,綁定控制到包含列表綁定源,然後設置結合源的位置到specixindex。
  • 將數據綁定添加到CheckBox控件時,將更新模式設置爲OnPropertyChanged
  • 處理CheckedChanged事件CheckBox控件和調用Invalidate網格的方法來顯示網格的變化。
  • CheckedChanged也呼叫BindingSourceEndEdit其中CheckBox被綁定到。
  • 注意:致電EndEdit usnig BeginInvoke讓所有檢查複選框(包括更新數據源)的過程完成。

enter image description here

C#示例

DataTable dt = new DataTable(); 
private void Form3_Load(object sender, EventArgs e) 
{ 
    dt.Columns.Add("Id"); 
    dt.Columns.Add("Selected", typeof(bool)); 
    dt.Rows.Add("1", true); 
    dt.Rows.Add("2", false); 
    dt.Rows.Add("3", true); 
    this.dataGridView1.DataSource = dt; 
    var chekBoxes = new CheckBox[] { checkBox1, checkBox2, checkBox3 }; 
    for (int i = 0; i < dt.Rows.Count; i++) 
    { 
     var bs = new BindingSource(dt, null); 
     chekBoxes[i].DataBindings.Add("Checked", bs, "Selected", 
      true, DataSourceUpdateMode.OnPropertyChanged); 
     chekBoxes[i].CheckedChanged += (obj, arg) => 
     { 
      this.dataGridView1.Invalidate(); 
      var c = (CheckBox)obj; 
      var b = (BindingSource)(c.DataBindings[0].DataSource); 
      this.BeginInvoke(()=>{b.EndEdit();}); 
     }; 
     bs.Position = i; 
    } 
} 

VB實例

Dim dt As DataTable = New DataTable() 
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _ 
Handles MyBase.Load 
    dt.Columns.Add("Id") 
    dt.Columns.Add("Selected", GetType(Boolean)) 
    dt.Rows.Add("1", True) 
    dt.Rows.Add("2", False) 
    dt.Rows.Add("3", True) 
    dt.AcceptChanges() 
    Me.DataGridView1.DataSource = dt 
    Dim chekBoxes = New CheckBox() {CheckBox1, CheckBox2, CheckBox3} 
    For i = 0 To dt.Rows.Count - 1 
     Dim bs = New BindingSource(dt, Nothing) 
     chekBoxes(i).DataBindings.Add("Checked", bs, "Selected", _ 
      True, DataSourceUpdateMode.OnPropertyChanged) 
     AddHandler chekBoxes(i).CheckedChanged, _ 
      Sub(obj, arg) 
       Me.DataGridView1.Invalidate() 
       Dim c = DirectCast(obj, CheckBox) 
       Dim b = DirectCast(c.DataBindings(0).DataSource, BindingSource) 
       Me.BeginInvoke(Sub() b.EndEdit()) 
      End Sub 
     bs.Position = i 
    Next i 
End Sub 
+0

謝謝你的答案,但它仍然遇到我在我的問題中的同樣的問題。我再次更新了我的問題,試圖讓我更加清楚自己正在努力做什麼。 – Interminable

+0

這篇文章有一個截圖。每個複選框綁定到一行,並在選中或取消選中後立即更新「DataRow」。不是你在找什麼?你能說出答案有什麼問題嗎?因爲根據我對你問題的理解,它完全回答你的問題。我錯過了什麼嗎? –

+0

你好,我不只是使用你的截圖,我還創建了一個新的Visual Studio項目並測試了你的代碼,但我不得不做一些小的改動以適應我的需求。我將添加我用於我的問題的代碼。另外,請閱讀第一個**編輯**下我的問題的更新,它應該有助於弄清楚事情。 – Interminable

相關問題