具有特殊属性的DataGridView自定义列

时间:2014-12-09 22:37:43

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

我似乎无法找到有效的解决方案。

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

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

我有一个版本需要我在CellFormatting和RowsAdded事件期间设置语句,但是我想这样做我所要做的就是提供一个SQL语句(即从JobList中选择JobName,其中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的属性包示例。我忽略了将属性保存到磁盘。似乎应该有一种让应用程序处理此任务的方法。到目前为止,没有什么对我有用

结束编辑

非常感谢任何帮助。

感谢。

马歇尔

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

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

我需要覆盖列中的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

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

谢谢大家。