VB.NET实现可编辑转置DataGridView(列表/数据表)问题求助
VB.NET实现可编辑转置DataGridView(列表/DataTable绑定)及GetValue/SetValue错误修复
问题概述
需要实现两组DataGridView功能:
- DataGridView1(绑定自定义对象列表):可编辑转置样式;对应DataGridView3为非转置、不可编辑状态
- DataGridView2(绑定DataTable):可编辑转置样式;对应DataGridView4为非转置、不可编辑状态
参考@RezaAghaei的C#转置代码转为VB.NET后,在Default Public Property Item(ByVal i As Integer) As Object属性中出现重载解析失败,没有可访问的GetValue/SetValue接受此参数数量错误。
错误原因
VB.NET中反射的GetValue/SetValue方法、DataTable的行/列访问逻辑与C#存在差异:
- 反射调用属性的
GetValue时必须传入目标对象实例,原代码遗漏了该参数 - DataTable的行值访问不需要额外调用
GetValue,直接通过索引即可操作
解决方案
针对列表和DataTable分别实现转置包装类,修正参数传递逻辑:
1. 列表绑定的转置包装类(用于DataGridView1)
Public Class TransposedList(Of T) Implements IList(Of Object) Private ReadOnly _originalList As IList(Of T) Private ReadOnly _propertyNames As String() Public Sub New(originalList As IList(Of T)) _originalList = originalList _propertyNames = GetType(T).GetProperties().Select(Function(p) p.Name).ToArray() End Sub Public ReadOnly Property Count As Integer Implements ICollection(Of Object).Count Get Return _propertyNames.Length End Get End Property Public ReadOnly Property IsReadOnly As Boolean Implements ICollection(Of Object).IsReadOnly Get Return False End Get End Property Default Public Property Item(index As Integer) As Object Implements IList(Of Object).Item Get If _originalList.Count > 0 Then ' 传入对象实例,获取指定属性值 Return GetType(T).GetProperty(_propertyNames(index)).GetValue(_originalList(0)) End If Return Nothing End Get Set(value As Object) ' 遍历所有对象,设置指定属性值 For Each item In _originalList GetType(T).GetProperty(_propertyNames(index)).SetValue(item, value) Next End Set End Property ' 实现IList接口其他方法(按需抛出不支持异常) Public Sub Add(item As Object) Implements ICollection(Of Object).Add Throw New NotSupportedException() End Sub Public Sub Clear() Implements ICollection(Of Object).Clear Throw New NotSupportedException() End Sub Public Function Contains(item As Object) As Boolean Implements ICollection(Of Object).Contains Return False End Function Public Sub CopyTo(array() As Object, arrayIndex As Integer) Implements ICollection(Of Object).CopyTo For i = 0 To Count - 1 array(arrayIndex + i) = Me(i) Next End Sub Public Function GetEnumerator() As IEnumerator(Of Object) Implements IEnumerable(Of Object).GetEnumerator For i = 0 To Count - 1 Yield Me(i) Next End Function Public Function IndexOf(item As Object) As Integer Implements IList(Of Object).IndexOf Return -1 End Function Public Sub Insert(index As Integer, item As Object) Implements IList(Of Object).Insert Throw New NotSupportedException() End Sub Public Function Remove(item As Object) As Boolean Implements ICollection(Of Object).Remove Throw New NotSupportedException() End Function Public Sub RemoveAt(index As Integer) Implements IList(Of Object).RemoveAt Throw New NotSupportedException() End Sub Private Function IEnumerable_GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator Return GetEnumerator() End Function End Class
2. DataTable绑定的转置包装类(用于DataGridView2)
Public Class TransposedDataTable Implements IList(Of Object) Private ReadOnly _originalTable As DataTable Public Sub New(originalTable As DataTable) _originalTable = originalTable End Sub Public ReadOnly Property Count As Integer Implements ICollection(Of Object).Count Get Return _originalTable.Columns.Count End Get End Property Public ReadOnly Property IsReadOnly As Boolean Implements ICollection(Of Object).IsReadOnly Get Return False End Get End Property Default Public Property Item(index As Integer) As Object Implements IList(Of Object).Item Get If _originalTable.Rows.Count > 0 Then ' 直接通过索引访问DataRow值 Return _originalTable.Rows(0)(index) End If Return DBNull.Value End Get Set(value As Object) ' 遍历所有行,更新对应列的值 For Each row In _originalTable.Rows row(index) = value Next End Set End Property ' 实现IList接口其他方法(按需抛出不支持异常) Public Sub Add(item As Object) Implements ICollection(Of Object).Add Throw New NotSupportedException() End Sub Public Sub Clear() Implements ICollection(Of Object).Clear Throw New NotSupportedException() End Sub Public Function Contains(item As Object) As Boolean Implements ICollection(Of Object).Contains Return False End Function Public Sub CopyTo(array() As Object, arrayIndex As Integer) Implements ICollection(Of Object).CopyTo For i = 0 To Count - 1 array(arrayIndex + i) = Me(i) Next End Sub Public Function GetEnumerator() As IEnumerator(Of Object) Implements IEnumerable(Of Object).GetEnumerator For i = 0 To Count - 1 Yield Me(i) Next End Function Public Function IndexOf(item As Object) As Integer Implements IList(Of Object).IndexOf Return -1 End Function Public Sub Insert(index As Integer, item As Object) Implements IList(Of Object).Insert Throw New NotSupportedException() End Sub Public Function Remove(item As Object) As Boolean Implements ICollection(Of Object).Remove Throw New NotSupportedException() End Function Public Sub RemoveAt(index As Integer) Implements IList(Of Object).RemoveAt Throw New NotSupportedException() End Sub Private Function IEnumerable_GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator Return GetEnumerator() End Function End Class
3. 窗体绑定示例代码
' 自定义列表绑定类 Public Class Person Public Property Name As String Public Property Age As Integer Public Property Email As String End Class Private Sub Form_Load(sender As Object, e As EventArgs) Handles MyBase.Load ' 列表绑定逻辑 Dim personList As New List(Of Person) From { New Person With {.Name = "张三", .Age = 25, .Email = "zhangsan@test.com"}, New Person With {.Name = "李四", .Age = 30, .Email = "lisi@test.com"} } ' DataGridView1:可编辑转置 DataGridView1.DataSource = New TransposedList(Of Person)(personList) ' 设置列标题与行标题 For i = 0 To DataGridView1.Columns.Count - 1 DataGridView1.Columns(i).HeaderText = "值" Next DataGridView1.Columns.Insert(0, New DataGridViewTextBoxColumn() With {.HeaderText = "属性名", .ReadOnly = True}) For i = 0 To DataGridView1.Rows.Count - 1 DataGridView1.Rows(i).Cells(0).Value = GetType(Person).GetProperties()(i).Name Next ' DataGridView3:非转置不可编辑 DataGridView3.DataSource = personList DataGridView3.ReadOnly = True ' DataTable绑定逻辑 Dim dt As New DataTable() dt.Columns.Add("产品名称") dt.Columns.Add("价格") dt.Columns.Add("库存") dt.Rows.Add("手机", 2999, 100) dt.Rows.Add("电脑", 5999, 50) ' DataGridView2:可编辑转置 DataGridView2.DataSource = New TransposedDataTable(dt) ' 设置列标题与行标题 For i = 0 To DataGridView2.Columns.Count - 1 DataGridView2.Columns(i).HeaderText = "值" Next DataGridView2.Columns.Insert(0, New DataGridViewTextBoxColumn() With {.HeaderText = "列名", .ReadOnly = True}) For i = 0 To DataGridView2.Rows.Count - 1 DataGridView2.Rows(i).Cells(0).Value = dt.Columns(i).ColumnName Next ' DataGridView4:非转置不可编辑 DataGridView4.DataSource = dt DataGridView4.ReadOnly = True End Sub
修复说明
- 列表转置类中,调用反射
GetValue/SetValue时明确传入目标对象实例,解决参数数量不足的问题 - DataTable转置类中,直接通过
DataRow(index)访问值,避免错误调用GetValue方法 - 手动添加行标题列,显示原属性名/列名,保证转置后的界面可读性
内容的提问来源于stack exchange,提问作者dlaksmi
相关产品推荐
相关产品推荐

