在同一数据源中每行数据绑定一个控件

时间:2016-08-01 15:10:28

标签: vb.net winforms data-binding bindingsource

我一直在尝试处理关于Winforms中数据绑定的令人沮丧的问题。

我有一个数据源,DataTable中的DataSet。这个DataTable有三行。我的表单上有三个CheckBox控件,还有一个Button。我想将每个CheckBox绑定到此数据源中的一行,并且只要Checked更新,数据源就会反映CheckBox属性中的值。我还希望通过拨打HasChanges()和拨打GetChanges()来正确接听这些更改。

点击Button后,系统会调用EndCurrentEdit()并传递绑定的数据源,并使用DataSet方法检查HasChanges()是否有更改

但是,在我尝试执行此操作时,在调用EndCurrentEdit()后遇到两种情况之一。

在第一个方案中,只有第一个CheckBox检测到其更改。在第二种方案中,所有其他CheckBoxes都会更新为CheckBox调用时最后检查的EndCurrentEdit()的值。

在调用RowState后查看EndCurrentEdit()值时,在场景1中,只有第一行的状态为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,“已选择”可以是任何Boolean值。这些都不会是NothingDBNull
  2. 我想将每一行绑定到一个Checkbox控件。每CheckBox个值IDChecked属性应绑定到Selected中的DataTable列。
  3. 当进行更改并且用户点击Button时,我希望能够使用{HasChanges()GetChanges()方法告知用户所做的更改1}}(即如果用户更改了两个DataSet控件的Checked属性,则会更新2行。
  4. 编辑2

    感谢@RezaAghaei,我想出了一个解决方案。我已经从我的问题示例中改进了代码,并根据数据生成了所有控件(并相应地定位自己),以使这个示例易于复制和粘贴。此外,这会使用Checkbox上的RowFilter,而不是DataView的{​​{1}}属性。

    Position

    关键是BindingSource 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 事件中对EndEdit()的调用{对ColumnChanged的一般调用似乎没有削减它),尽管一个我遇到的另一个问题是,如果它采用表单的DataTable方法,即使在调用EndCurrentEdit()之后,此代码也无效。我猜这是因为在调用New()之后,Winforms会进行一些初始化,这是数据绑定正常工作所必需的。

    我希望这个例子可以节省其他人花在调查时间上的时间。

1 个答案:

答案 0 :(得分:1)

考虑这些更正,问题将得到解决:

  • 要将控件绑定到列表的特定索引,请将控件绑定到包含该列表的绑定源,然后将绑定源的位置设置为specifixindex。
  • CheckBox控件添加数据绑定时,请将更新模式设置为OnPropertyChanged
  • 处理CheckedChangedCheckBox控件事件并调用Invalidate网格方法以显示网格中的更改。
  • CheckedChanged中,还可以致电EndEdit所绑定的BindingSource 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