【发布时间】:2014-04-01 20:58:23
【问题描述】:
我正在实现一个 3 层应用程序(WPF/UI 层、业务层和数据访问层)。
在数据访问层中,我有一个刷新方法,用于查询数据库并将该查询中的行合并到包含业务层对象信息的 DataTables 中。
业务层对象需要检测其对应的行何时发生更改,以便它可以引发通知属性事件,让 UI 知道信息已更改并刷新显示。
我曾考虑在此过程中使用DataTable.RowChanged Event,但我发现当我执行DataTable.Merge(DataTable) Method 时,即使没有更改,也会为 DataTable 中的每一行引发 RowChanged 事件。
出于演示目的,我已经简化了我在内存 DataTables 中的问题使用,并发现同样的事情发生了。
我创建了一个名为 TestingRowChangeAndMerge 的 WPF 应用程序。此项目中有一个名为“MainWindow”的 WPF 窗口和一个名为“TableManager”的类,该类创建 2 个表,当调用“Merge”方法时将合并这些表,并具有指示在此期间引发的 RowChanged 事件数的属性合并过程。
这是我的 MainWindow 的 XAML:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestingRowChangeAndMerge"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:TableManger x:Key="TableManagerResource" />
</Window.Resources>
<Grid DataContext="{StaticResource TableManagerResource}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<DataGrid ItemsSource="{Binding Table1.DefaultView}" AutoGenerateColumns="True" >
</DataGrid>
<StackPanel Grid.Row="1">
<TextBlock Text="{Binding ChangesDetected}" />
<Button x:Name="MergeTables" Content="Merge" Click="MergeTables_Click"/>
</StackPanel>
</Grid>
这是用于处理按钮单击事件的 MainWindow.xaml.VB 代码以及 TableManager 类:
Class MainWindow
Private Sub MergeTables_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
Dim tbManager As TableManger = FindResource("TableManagerResource")
tbManager.MergeTables()
End Sub
End Class
Class TableManger
Implements ComponentModel.INotifyPropertyChanged
Private _table1 As System.Data.DataTable
Private _table2 As System.Data.DataTable
Private _changesDetected As Integer = 0
Public ReadOnly Property Table1
Get
Return _table1
End Get
End Property
Public ReadOnly Property ChangesDetected As Integer
Get
Return _changesDetected
End Get
End Property
Public Sub New()
_table1 = CreateTableWithData()
_table1.AcceptChanges()
AddHandler _table1.RowChanged, New System.Data.DataRowChangeEventHandler(AddressOf Row_Changed)
End Sub
Public Sub MergeTables()
_table2 = _table1.Clone
Dim tableRows As New List(Of System.Data.DataRow)
For Each r In _table1.Rows
Dim dr2 = _table2.NewRow
For Each col As System.Data.DataColumn In _table1.Columns
dr2(col.ColumnName) = r(col.ColumnName)
Next
_table2.Rows.Add(dr2)
tableRows.Add(dr2)
Next
_table2.AcceptChanges()
If _table2.Rows.Count > 0 Then
_table2.Rows(0)(1) = "TB2 Changed"
End If
If _table1.Rows.Count > 0 Then
'_table1.Rows(0)(1) = "TB1 Change"'
_table1.Rows(1)(1) = "TB1 Change"
End If
_changesDetected = 0
Dim perserveChanges As Boolean = True
Dim msAction As System.Data.MissingSchemaAction = System.Data.MissingSchemaAction.Ignore
Dim changes As System.Data.DataTable = _table1.GetChanges()
If changes IsNot Nothing Then
changes.Merge(_table2, perserveChanges, msAction)
_table1.Merge(changes, False, msAction)
Else
_table1.Merge(_table2, False, msAction)
End If
MessageBox.Show(String.Format("Changes in Change Table: {0} {1}Changes Detected: {2}", If((changes Is Nothing), 0, changes.Rows.Count), System.Environment.NewLine, _changesDetected), "Testing")
RaiseEvent PropertyChanged(Me, New ComponentModel.PropertyChangedEventArgs("Table1"))
RaiseEvent PropertyChanged(Me, New ComponentModel.PropertyChangedEventArgs("ChangesDetected"))
End Sub
Private Sub Row_Changed(ByVal sender As Object, ByVal e As System.Data.DataRowChangeEventArgs)
Select Case e.Action
Case System.Data.DataRowAction.Change'
' If e.Row.RowState <> System.Data.DataRowState.Unchanged Then'
_changesDetected += 1
' End If
End Select
End Sub
Private Function CreateTableWithData() As System.Data.DataTable
Dim newTable As New System.Data.DataTable
Dim columnID As New System.Data.DataColumn("ID", GetType(Guid))
Dim columnA As New System.Data.DataColumn("ColumnA", GetType(String))
Dim columnB As New System.Data.DataColumn("ColumnB", GetType(String))
newTable.Columns.AddRange({columnID, columnA, columnB})
newTable.PrimaryKey = {newTable.Columns(0)}
For i = 0 To 5
Dim dr = newTable.NewRow
dr("ID") = Guid.NewGuid
dr("ColumnA") = String.Format("Column A Row {0}", i.ToString)
dr("ColumnB") = String.Format("Column B Row {0}", i.ToString)
newTable.Rows.Add(dr)
Next
Return newTable
End Function
Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
当您单击按钮时,您将看到按钮上方显示的数字从 0 变为 6,这反映了执行表合并时引发的 RowChangedEvents 的数量。
如何更改它以便我可以检测到实际行更改何时发生??
编辑/更新:
我没有在原始代码中调用接受 table2 上的更改,这意味着当我将 table1 和 table2 合并在一起时,所有行都被标记为已修改。我已更新此处的代码以正常工作。
在我解决了这个问题之后,我更改了处理 Row_Change 事件的代码中的逻辑,以便它仅在行的 RowState 不等于 Unchanged 时增加 _changesDetected 计数器,因为我认为这将指示何时行改变了。
但在回答Merge two identical DataTables results in DataRowState.Modified 后,我无法再使用修改后的 RowState,因为 RowStates 不再更改。
所以我回到我原来的问题/问题:
由于所有行都在合并中引发“RowChanged”事件,我怎么知道哪些行实际发生了更改???
【问题讨论】:
-
看起来这与问题非常相关:Merge two identical DataTables results in DataRowState.Modified。但我没有得到任何答案或cmets。 ://
-
刚刚测试过,你是对的:合并后行被标记为已修改!
-
+1 我希望你的“新问”问题会比我的问题引起更多关注;)
-
啊哈我想通了这两个问题。我不得不在 table2 上调用 AcceptChanges 以防止更改行状态(有道理,它们被“添加”到表 2 中,因此在合并后所有行都被正确标记为“修改”)。现在,在 RowChanged 事件中,我只需检查 e.Row.RowState 以确定行的数据是否已更改!非常感谢您让我调查您的问题 :)
-
我在看你的问题....这没有意义。立即试用您的代码
标签: .net wpf vb.net datatable merge