我一直在尝试处理关于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
我已经做了很多搜索但是却无法弄清楚发生了什么,因此我完全失去了。感觉就像我想要做的那样非常简单,但我怀疑我必须在某处误解某些内容或错过了关于绑定过程的重要内容。
修改
它已经在第二段了,但为了说清楚,这里是我正在尝试做的基本概要:
DataSet
包含DataTable
值。对于此示例,有两列。 ID
可以是任意Integer
,“已选择”可以是任何Boolean
值。这些都不会是Nothing
或DBNull
。Checkbox
控件。每CheckBox
个值ID
。 Checked
属性应绑定到Selected
中的DataTable
列。Button
时,我希望能够使用{HasChanges()
和GetChanges()
方法告知用户所做的更改1}}(即如果用户更改了两个DataSet
控件的Checked
属性,则会更新2行。编辑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会进行一些初始化,这是数据绑定正常工作所必需的。
我希望这个例子可以节省其他人花在调查时间上的时间。
答案 0 :(得分:1)
考虑这些更正,问题将得到解决:
CheckBox
控件添加数据绑定时,请将更新模式设置为OnPropertyChanged
。CheckedChanged
个CheckBox
控件事件并调用Invalidate
网格方法以显示网格中的更改。CheckedChanged
中,还可以致电EndEdit
所绑定的BindingSource
CheckBox
。EndEdit
usnig BeginInvoke
,以便完成复选框(包括更新数据源)的所有流程。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