【问题标题】:DataGridView Custom Column with special properties具有特殊属性的 DataGridView 自定义列
【发布时间】:2014-12-09 22:37:42
【问题描述】:

我似乎找不到可行的解决方案。

我有一个基于 DataGridViewTextboxColumn 的自定义 datagridview 单元格。我正在为此使用 VB 2005。

我正在尝试实现一个设计时属性,我可以使用该属性来存储列中的单元格使用的 SQL 语句。每行的查找相同,但返回的值不同。

我有一个版本,它要求我在 CellFormatting 和 RowsAdded 事件期间设置语句,但我想这样做,所以我所要做的就是提供一个 SQL 语句(即 Select JobName From JobList where JobNo = {0})作为列类中的属性。我想绑定单元格并使用它获得的值作为语句的替换值。我无法让 SQL 语句属性持久化(正确的词?)

我的部分问题是我不确定该属性是否应该放在自定义 COLUMN 或自定义 CELL 定义中。我一直在尝试将属性添加到列中,这似乎是合乎逻辑的,因为我希望在单元格中为每个实例使用相同的值,而不必在每次触发单元格格式事件时设置语句值。

我找到并尝试了这些属性:

<Browsable(True), _
    EditorBrowsable(EditorBrowsableState.Always), _
    Category("Data"), _
    Description("The SQL Query to use for lookup. Make sure it will work with string.format"), _
    DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)> _
Public Property ScalarSQLStatement() As String
End Property

它不起作用。我可以在列编辑器中输入值,但如果我退出编辑器并返回,属性将重置为默认值。


编辑----

我为 DataGridTextboxCell 创建了子类并将其放入其中:

Imports System.ComponentModel

Public Class DataGridViewScalarValue2TextboxCell
    Inherits DataGridViewTextBoxCell

    Private _scalarStatement As String = String.Empty
    Private _ReturnValue As String = String.Empty

    Private _LookupValue As Object

      Public Property LookupValue() As Object
        Get
            Return _LookupValue
        End Get
        Set(ByVal value As Object)
            _LookupValue = value
        End Set
    End Property


    Public Sub New()

        Me._LookupValue = Nothing

        If Me.OwningColumn Is Nothing Then Return

        Me.ScalarStatement = CType(Me.OwningColumn, DataGridViewScalarValue2TextboxColumn).ScalarSQLStatement

    End Sub

    Public Property ScalarStatement() As String
        Get
            Return _scalarStatement
        End Get
        Set(ByVal Value As String)
            _scalarStatement = Value
        End Set
    End Property

    ' Override the Clone method so that the Enabled property is copied. 

    Public Overrides Function Clone() As Object

        Dim Cell As DataGridViewScalarValue2TextboxCell = CType(MyBase.Clone(), _
                DataGridViewScalarValue2TextboxCell)

        Cell._scalarStatement = Me._scalarStatement
        Return Cell

    End Function

    Protected Overrides Function GetFormattedValue(ByVal value As Object, _
                                                   ByVal rowIndex As Integer, _
                                                   ByRef cellStyle As DataGridViewCellStyle, _
                                                   ByVal valueTypeConverter As TypeConverter, _
                                                   ByVal formattedValueTypeConverter As TypeConverter, _
                                                   ByVal context As DataGridViewDataErrorContexts) As Object

        If _LookupValue <> value Then
            _LookupValue = value
            GetReturnValueFromLookupValue()
        End If

        Return MyBase.GetFormattedValue(_ReturnValue, rowIndex, cellStyle, _
                valueTypeConverter, formattedValueTypeConverter, context)

    End Function

    Protected Overrides Sub Paint(ByVal graphics As Graphics, _
                                  ByVal clipBounds As Rectangle, _
                                  ByVal cellBounds As Rectangle, _
                                  ByVal rowIndex As Integer, _
                                  ByVal cellState As DataGridViewElementStates, _
                                  ByVal value As Object, _
                                  ByVal formattedValue As Object, _
                                  ByVal errorText As String, _
                                  ByVal cellStyle As DataGridViewCellStyle, _
                                  ByVal advancedBorderStyle As DataGridViewAdvancedBorderStyle, _
                                  ByVal paintParts As DataGridViewPaintParts)

        If value IsNot Nothing AndAlso _
            (TypeOf value Is String AndAlso Not String.IsNullOrEmpty(value)) OrElse _
            (TypeOf value Is Integer AndAlso Integer.TryParse(value, Nothing)) OrElse _
            (TypeOf value Is Decimal AndAlso Decimal.TryParse(value, 0)) OrElse _
            (TypeOf value Is Date) AndAlso IsDate(value) Then
            _LookupValue = value
            GetReturnValueFromLookupValue()

        Else
            _ReturnValue = String.Empty

        End If

        MyBase.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, _
                _ReturnValue, errorText, cellStyle, advancedBorderStyle, paintParts)

    End Sub

    Private Sub GetReturnValueFromLookupValue()

        If _LookupValue Is Nothing _
         Or OwningColumn Is Nothing _
         Or String.IsNullOrEmpty(_scalarStatement) Then
            _ReturnValue = Nothing
            Return
        End If
        Using conn As New SqlClient.SqlConnection(ConnUtils.MyGCPTableConnectionString)
            conn.Open()

            Dim cmd As SqlClient.SqlCommand = conn.CreateCommand

            With cmd
                .CommandText = String.Format(_scalarStatement, _LookupValue)

                .CommandType = CommandType.Text

                Dim objResult = .ExecuteScalar

                If objResult IsNot Nothing Then
                    _ReturnValue = objResult

                End If

            End With

            conn.Close()
        End Using

    End Sub

    Public Overloads Property Value() As Object

        Get
            Return _ReturnValue
        End Get

        Set(ByVal value)

            If TypeOf value Is Integer AndAlso value > 0 Then
                If _LookupValue <> value Then
                    _LookupValue = value
                    GetReturnValueFromLookupValue()

                End If
            End If

        End Set

    End Property
End Class

我对 DataGridViewTextboxColumn 类做了同样的事情。在我的原始帖子中引用了它的主要属性(用于此用途)。这个子类里面有这段代码:

Imports System.ComponentModel


Public Class DataGridViewScalarValue2TextboxColumn
    Inherits DataGridViewTextBoxColumn

    Public Sub New()
        Me.CellTemplate = New DataGridViewScalarValue2TextboxCell()

    End Sub

    '' Fields...
    Private _scalarSQLStatement As String = String.Empty

    <Browsable(True), _
        EditorBrowsable(EditorBrowsableState.Always), _
        Category("Data"), _
        Description("The SQL Query to use for lookup. Make sure it will work with string.format"), _
        DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)> _
    Public Property ScalarSQLStatement() As String
        Get
            Return _scalarSQLStatement
        End Get
        Set(ByVal Value As String)
            _scalarSQLStatement = Value
        End Set
    End Property

在表单中,我使用 CellFormatting 和 RowsAdded 事件,如下所示:

Private Sub dgvCustomCellTypes_CellFormatting(ByVal sender As Object, ByVal e As .DataGridViewCellFormattingEventArgs) Handles dgvCustomCellTypes.CellFormatting

    If e.ColumnIndex = colHaulID2.Index Then
        Dim dgvr As DataGridViewRow = CType(sender, DataGridView).Rows(e.RowIndex)
        Dim HaulCell As DataGridViewScalarValue2TextboxCell = CType(dgvr.Cells(colHaulID2.Index), DataGridViewScalarValue2TextboxCell)
        HaulCell.ScalarStatement = colHaulID2.ScalarSQLStatement
    End If

End Sub

Private Sub dgvCustomCellTypes_RowsAdded(ByVal sender As Object, ByVal e As DataGridViewRowsAddedEventArgs) Handles dgvCustomCellTypes.RowsAdded

    For i As Integer = e.RowIndex To e.RowCount - 1
        Dim dgvr As DataGridViewRow = CType(sender, DataGridView).Rows(i)
        Dim HaulCell As DataGridViewScalarValue2TextboxCell = CType(dgvr.Cells(colHaulID2.Index), DataGridViewScalarValue2TextboxCell)
        HaulCell.ScalarStatement = colHaulID2.ScalarSQLStatement

    Next
End Sub

并将其添加到表单初始化中:

    colHaulID2.ScalarSQLStatement = "Select CompanyName From HaulCompany Where HaulID = {0}"

该列是数据绑定的,并在那里获取其查找值。它工作正常,但不是我正在寻找的解决方案。

我正在尝试重写单元格/列,以便我可以在设计时使用列编辑器将 SQL 语句添加到列中,并依靠数据绑定值来提供查找键。

在我看来,唯一阻止我实现目标的就是在设计时关闭列编辑器后让控件保存它的 SQL 语句。或者在我重新打开列编辑器时防止它被清除。我不确定发生了什么。但我确信有一个解决方案。我在持久性方面做了一些尝试,包括我发现的一个类似 VB6 的属性包示例。我忽略了将属性保存到磁盘。似乎应该有一种方法可以让应用程序处理此任务。到目前为止,没有什么对我有用

结束编辑

任何帮助将不胜感激。

谢谢。

马歇尔

【问题讨论】:

    标签: sql-server vb.net datagridview custom-controls


    【解决方案1】:

    在演示的第一列中双击一个单元格。

    Public Class Form1
      Sub New()
    
        ' This call is required by the designer.'
        InitializeComponent()
    
        ' Add any initialization after the InitializeComponent() call.'
        Dim dtb As New DataTable
        dtb.Columns.Add("C1")
        dtb.Columns.Add("C2")
        dtb.Columns.Add("C3")
        dtb.Rows.Add("1", "2", "3")
        dtb.Rows.Add("2", "3", "4")
        dtb.Rows.Add("3", "4", "5")
        dtb.Rows.Add("4", "5", "6")
        DataGridView1.DataSource = dtb
        DataGridView1.Columns(0).Tag = "Select JobName From JobList where JobNo = {0}"
      End Sub
    
      Private Sub DataGridView1_CellMouseDoubleClick(sender As Object, e As DataGridViewCellMouseEventArgs) Handles DataGridView1.CellMouseDoubleClick
        Dim strSql As String = String.Format(DataGridView1.Columns(e.ColumnIndex).Tag.ToString, DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value.ToString)
        MsgBox(strSql)
      End Sub
    End Class
    

    【讨论】:

    • 所以基本上,在运行时执行此操作而不是在设计时执行此操作?这是我计划在几十个应用程序中使用的控件,并希望不必添加任何不必要的手动代码。我想自己封装列或单元格控件中的所有内容。
    • 我已经为我的子类列和单元格添加了其余代码,并试图更好地解释我正在尝试做什么。
    • 是的,我会推荐这种方法而不是自定义控件。自定义控件很难调试,因为 IDE 需要编译后的控件才能工作 - 因此,如果您正在调试自定义控件和同时使用它的业务应用程序,可能会出现版本问题(特别是如果在自定义控件)。 IDE 不提供该列的 .Tag 属性,这是一个巨大的遗憾。
    • 另一个选项可能是包含 SQL 的隐藏列
    【解决方案2】:

    我找到了我正在寻找的解决方案。

    我需要重写列(而不是单元格)中的 Clone 方法,并确保在运行时将属性传递给单元格。下面我发布我的最终解决方案。

    (我在 Stack Overflow 上找到了我的解决方案 here。)

    Imports System.ComponentModel
    Imports System.ComponentModel.Design
    
    <Serializable()> _
    Public Class DataGridViewScalarValueTextboxColumn
        Inherits DataGridViewTextBoxColumn
    
        Public Sub New()
    
            Me.CellTemplate = New DataGridViewScalarValueTextboxCell()
        End Sub
    
        ' Fields...'
        Private _scalarSQLStatement As String = String.Empty
    
        <Browsable(True), _
            Category("Data"), _
            Description("The SQL Query to use for lookup. Must work with the string.format function if lookup value is used."), _
            DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)> _
        Public Property ScalarSQLStatementCol() As String
            Get
                Return _scalarSQLStatement
            End Get
    
            Set(ByVal Value As String)
                _scalarSQLStatement = Value
            End Set
        End Property
    
        Public Overrides Function Clone() As Object
    
            Dim myClone As DataGridViewScalarValueTextboxColumn = CType(MyBase.Clone, DataGridViewScalarValueTextboxColumn)
            myClone.ScalarSQLStatementCol = ScalarSQLStatementCol
            Return myClone
    
        End Function
    
    End Class
    
    Public Class DataGridViewScalarValueTextboxCell
        Inherits DataGridViewTextBoxCell
    
        Private _ReturnValue As String = String.Empty
    
        Private _LookupValue As Object
    
        Public Property LookupValue() As Object
            Get
                Return _LookupValue
            End Get
            Set(ByVal value As Object)
                _LookupValue = value
            End Set
    
            End Property
    
        Public Sub New()
    
            Me._LookupValue = Nothing
    
        End Sub
    
        Public Overrides Function Clone() As Object
            'Method may not be needed now'
            Dim Cell As DataGridViewScalarValueTextboxCell = CType(MyBase.Clone(), _
                    DataGridViewScalarValueTextboxCell)
            Return Cell
    
        End Function
    
        Protected Overrides Function GetFormattedValue(ByVal value As Object, _
                                                       ByVal rowIndex As Integer, _
                                                       ByRef cellStyle As DataGridViewCellStyle, _
                                                       ByVal valueTypeConverter As TypeConverter, _
                                                       ByVal formattedValueTypeConverter As TypeConverter, _
                                                       ByVal context As DataGridViewDataErrorContexts) As Object
    
            If _LookupValue <> value Then
                _LookupValue = value
                GetReturnValueFromLookupValue()
            End If
    
            Return MyBase.GetFormattedValue(_ReturnValue, rowIndex, cellStyle, _
                    valueTypeConverter, formattedValueTypeConverter, context)
    
        End Function
    
        Protected Overrides Sub Paint(ByVal graphics As Graphics, _
                                      ByVal clipBounds As Rectangle, _
                                      ByVal cellBounds As Rectangle, _
                                      ByVal rowIndex As Integer, _
                                      ByVal cellState As DataGridViewElementStates, _
                                      ByVal value As Object, _
                                      ByVal formattedValue As Object, _
                                      ByVal errorText As String, _
                                      ByVal cellStyle As DataGridViewCellStyle, _
                                      ByVal advancedBorderStyle As DataGridViewAdvancedBorderStyle, _
                                      ByVal paintParts As DataGridViewPaintParts)
    
            If value IsNot Nothing AndAlso _
                (TypeOf value Is String AndAlso Not String.IsNullOrEmpty(value)) OrElse _
                (TypeOf value Is Integer AndAlso Integer.TryParse(value, Nothing)) OrElse _
                (TypeOf value Is Decimal AndAlso Decimal.TryParse(value, 0)) OrElse _
                (TypeOf value Is Date) AndAlso IsDate(value) Then
                _LookupValue = value
                GetReturnValueFromLookupValue()
    
            Else
                _ReturnValue = String.Empty
    
            End If
    
            MyBase.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, _
                    _ReturnValue, errorText, cellStyle, advancedBorderStyle, paintParts)
    
        End Sub
    
        Private Sub GetReturnValueFromLookupValue()
    
            If _LookupValue Is Nothing _
             Or (OwningColumn Is Nothing _
             OrElse CType(Me.OwningColumn, DataGridViewScalarValue2TextboxColumn).ScalarSQLStatementCol.Length = 0) Then
                _ReturnValue = Nothing
                Return
    
            End If
    
            Using conn As New SqlClient.SqlConnection(ConnUtils.MyGCPTableConnectionString)
                conn.Open()
    
                Dim cmd As SqlClient.SqlCommand = conn.CreateCommand
    
                With cmd
                    .CommandText = String.Format(CType(Me.OwningColumn, DataGridViewScalarValue2TextboxColumn).ScalarSQLStatementCol, _LookupValue)
                    .CommandType = CommandType.Text
    
                    Dim objResult = .ExecuteScalar
                    If objResult IsNot Nothing Then
                        _ReturnValue = objResult
    
                    End If
                End With
    
                conn.Close()
            End Using
    
        End Sub
    
        Public Overloads Property Value() As Object
    
            Get
                Return _ReturnValue
            End Get
    
            Set(ByVal value)
    
                If TypeOf value Is Integer AndAlso value > 0 Then
                    If _LookupValue <> value Then
                        _LookupValue = value
                        GetReturnValueFromLookupValue()
    
                    End If
                End If
    
            End Set
    
        End Property
    
    End Class
    

    我确信可以进行更多重构,但它确实有效。只需将该列添加到您的网格中,设置查找的绑定属性并设置 SQL 语句(必须只返回一个值)并运行它。只需要加载源数据。

    谢谢大家。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-09
      • 1970-01-01
      • 2011-11-28
      • 1970-01-01
      • 2013-04-13
      相关资源
      最近更新 更多