0

I have a DGV on a tab control, with it's parent tab not the initially visible tab (i.e. dgv on TabPage2 with TabPage1 visible).

The grids data-source is populated and assigned on form load, in Data-binding complete I'm adding new columns and assigning an event handler to their header cell click, all OK so far.

However when I change tab to make the DGV visible data-binding complete fires for a second time, at this stage the event handler has been lost, what causes the event to fire for a second time?

What causes the Event handler to have been lost? Is there a way to work around this?

Thanks

Edit:

The cause appears to be the column is reverting to the underlying type, i.e. in DataBindingComplete I add a column DGVTextColumn which inherits from DataGridViewTextBoxColumn, the second time that DataBindingComplete fires the column is of the type DataGridViewTextBoxColumn rather than the inherited type DGVTextColumn if that helps anyone?

The important factor appears to be the column being bound, taking an empty form and adding a tab control to it, place a datagridview in tabpage2, revert back to tabpage1 paste the code at the bottom into the forms code.

Private Sub DataGridView1_DataBindingComplete(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewBindingCompleteEventArgs) Handles DataGridView1.DataBindingComplete

 If Not DataGridView1.Columns.Contains("colBool") Then
        Dim col As New DGVColumn
        col.Name = "colBool"
        col.DataPropertyName = "Checked" 'When this is included the checkbox appears unticked, however commenting this line out means that the checkbox appears ticked upon changing to TabPage2
        col.Checked = True
        DataGridView1.Columns.Add(col)
    End If
End Sub

Code to reproduce:

Imports System.ComponentModel
Imports System.Windows.Forms.VisualStyles

Public Class Form1
Private list As BindingList(Of iPerson)

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    list = New BindingList(Of iPerson)

    list.Add(New Person(0, "Steve", "Smith"))
    list.Add(New Person(1, "Gomez", "Juan"))
    list.Add(New Person(1, "Jones", "Paul"))

    DataGridView1.DataSource = list
End Sub

Private Sub DataGridView1_DataBindingComplete(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewBindingCompleteEventArgs) Handles DataGridView1.DataBindingComplete

    If Not DataGridView1.Columns.Contains("colBool") Then
        Dim col As New DGVColumn
        col.Name = "colBool"
        col.DataPropertyName = "Checked"
        col.Checked = True
        DataGridView1.Columns.Add(col)
    End If
End Sub

Private Interface iPerson

    ReadOnly Property Forename() As String
    ReadOnly Property Surname() As String
    ReadOnly Property Checked() As Boolean
End Interface

Private Class Person
    Implements iPerson

    Private _ID As Integer
    Private _Forename As String
    Private _Surname As String
    Private _checked As Boolean

    Public Sub New(ByVal ID As Integer, ByVal Forename As String, ByVal Surname As String)

        _ID = ID
        _Forename = Forename
        _Surname = Surname
        _checked = False
    End Sub

    Public ReadOnly Property Forename() As String Implements iPerson.Forename
        Get
            Return _Forename
        End Get
    End Property

    Public ReadOnly Property Surname() As String Implements iPerson.Surname
        Get
            Return _Surname
        End Get
    End Property

    Public ReadOnly Property Checked() As Boolean Implements iPerson.Checked
        Get
            Return _checked
        End Get
    End Property
End Class

Private Class DGVHeaderCell
    Inherits DataGridViewColumnHeaderCell

    Private checkBoxLocation As Point
    Private checkBoxSize As Size
    Private _checked As Boolean = False
    Private _cellLocation As New Point()

    Public Event OnCheckBoxClicked()

    Public Sub New()
    End Sub

    Protected Overrides Function GetPreferredSize(ByVal graphics As System.Drawing.Graphics, ByVal cellStyle As System.Windows.Forms.DataGridViewCellStyle, ByVal rowIndex As Integer, ByVal constraintSize As System.Drawing.Size) As System.Drawing.Size
        Dim calcSize = MyBase.GetPreferredSize(graphics, cellStyle, rowIndex, constraintSize)

        calcSize.Height += CheckBoxRenderer.GetGlyphSize(graphics, System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal).Height + 3
        Return calcSize
    End Function

    Protected Overrides Sub Paint(ByVal graphics As System.Drawing.Graphics, ByVal clipBounds As System.Drawing.Rectangle, ByVal cellBounds As System.Drawing.Rectangle, ByVal rowIndex As Integer, ByVal dataGridViewElementState 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)

        MyBase.Paint(graphics, clipBounds, cellBounds, rowIndex, dataGridViewElementState, "", "", errorText, cellStyle, advancedBorderStyle, paintParts)

        Dim p As New Point()
        Dim s As Size = CheckBoxRenderer.GetGlyphSize(graphics, System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal)

        p.X = CInt(cellBounds.Location.X + (cellBounds.Width / 2) - (s.Width / 2))
        p.Y = cellBounds.Location.Y + cellBounds.Height - s.Height - 3

        Dim rect As Rectangle = cellBounds

        rect.Height -= CheckBoxRenderer.GetGlyphSize(graphics, System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal).Height

        Using txtBrush As New SolidBrush(cellStyle.ForeColor)
            Dim format As New StringFormat
            format.Alignment = StringAlignment.Center
            format.LineAlignment = StringAlignment.Center
            graphics.DrawString(formattedValue.ToString, cellStyle.Font, txtBrush, rect, format)
        End Using

        _cellLocation = cellBounds.Location
        checkBoxLocation = p
        checkBoxSize = s
        Dim _cbState As CheckBoxState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal
        If _checked Then
            _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal
        End If

        CheckBoxRenderer.DrawCheckBox(graphics, checkBoxLocation, _cbState)
    End Sub

    Protected Overrides Sub OnMouseClick(ByVal e As DataGridViewCellMouseEventArgs)

        Dim p As New Point(e.X + _cellLocation.X, e.Y + _cellLocation.Y)

        If p.X >= checkBoxLocation.X AndAlso p.X <= checkBoxLocation.X + checkBoxSize.Width AndAlso p.Y >= checkBoxLocation.Y AndAlso p.Y <= checkBoxLocation.Y + checkBoxSize.Height Then
            _checked = Not _checked
            RaiseEvent OnCheckBoxClicked()
            Me.DataGridView.InvalidateCell(Me)
        End If
        MyBase.OnMouseClick(e)
    End Sub

    Public Property Checked() As Boolean
        Get
            Return _checked
        End Get
        Set(ByVal value As Boolean)
            _checked = value
        End Set
    End Property
End Class

Friend Class DGVColumn
    Inherits DataGridViewTextBoxColumn

    Private _headerCell As DGVHeaderCell

    Public Sub New()

        _headerCell = New DGVHeaderCell()
        AddHandler _headerCell.OnCheckBoxClicked, AddressOf THISHANDLER

        Me.HeaderCell = _headerCell
        Me.Width = 50
    End Sub

    Private Sub THISHANDLER()

        MsgBox("Clicked")
    End Sub

    Public Property Checked() As Boolean
        Get
            Return _headerCell.Checked
        End Get
        Set(ByVal value As Boolean)
            _headerCell.Checked = value
        End Set
    End Property
End Class

End Class

4

1 回答 1

0

覆盖 HeaderCell 和 Column 的克隆解决了这个问题

    Public Overrides Function Clone() As Object

        Dim Copy As DGVColumn = DirectCast(MyBase.Clone, DGVColumn)
        Copy._headerCell = DirectCast(_headerCell.Clone, DataGridViewColumnHeaderCell)
        Copy.HeaderCell = Copy._HeaderCell
        Return Copy

    End Function
于 2014-08-07T15:28:10.513 回答