代码之家  ›  专栏  ›  技术社区  ›  InteXX

为什么重复调用此事件处理程序方法?

  •  1
  • InteXX  · 技术社区  · 6 年前

    这个 DataGridViewComboBoxColumn

    怪诞的最新体现是 ComboBox 事件处理程序,为单个用户操作触发次数不断增加。奇怪的是,增长率是前一个数字的两倍。 1, 2, 4, 8, 16, 32, 64 等)

    首先,我将解释我要完成的任务,并澄清一些术语。

    我有一个 Dictionary(Of Integer, String) . 在我的领域规则中,我称之为 Key 渠道 以及 Value 财产 标签 . 我正在绘制每个 KeyValuePair 三分之一 String 调用的值 . 这个 字典(整数,字符串) 项目是固定的,它们作为用户的视觉帮助而存在,因此用户可以轻松地选择 目标 从一个 List(Of String) .

    我已经决定了 DataGridView

    enter image description here

    注意,已经映射 列表项以几乎不可见的颜色显示,以阻止用户再次尝试使用它们。(当已经映射了 目标 选择映射到不同的 .)

    下面是我的完整代码库,但是为了快速查看,下面是重复的事件处理程序:

    Private Sub ComboBox_SelectionChangeCommitted(Sender As ComboBox, e As EventArgs)
      ' '
      ' Look for other labels that have already been mapped to this target '
      ' '
      If Me.OtherTargetCells.Any(Function(Cell) Cell.FormattedValue = Sender.Text) Then
        If Me.IsInteractiveChange Then
          MsgBox("Target [] is already mapped to Label []. If you want to map Target [] to Label [], you must first set Label [] to [Not mapped].", MsgBoxStyle.Exclamation, Me.DataGridView.FindForm.Text)
    
          Me.IsInteractiveChange = False
          Sender.SelectedIndex = 0
          Me.IsInteractiveChange = True
        End If
      End If
    End Sub
    

    下面是我如何把它们连接起来的:

    Public Sub New()
      Task.Run(Sub()
                 Dim oHandler As DataGridViewEditingControlShowingEventHandler
    
                 While Me.DataGridView Is Nothing
                 End While
    
                 oHandler = New DataGridViewEditingControlShowingEventHandler(AddressOf DataGridView_EditingControlShowing)
    
                 RemoveHandler Me.DataGridView.EditingControlShowing, oHandler
                 AddHandler Me.DataGridView.EditingControlShowing, oHandler
               End Sub)
    End Sub
    
    
    
    Private Sub DataGridView_EditingControlShowing(Sender As DataGridView, e As DataGridViewEditingControlShowingEventArgs)
      Dim oComboBox As ComboBox
    
      If TypeOf e.Control Is ComboBox Then
        oComboBox = e.Control
        oComboBox.DrawMode = DrawMode.OwnerDrawFixed
    
        RemoveHandler oComboBox.DrawItem, New DrawItemEventHandler(AddressOf ComboBox_DrawItem)
        AddHandler oComboBox.DrawItem, New DrawItemEventHandler(AddressOf ComboBox_DrawItem)
    
        RemoveHandler oComboBox.SelectionChangeCommitted, New EventHandler(AddressOf ComboBox_SelectionChangeCommitted)
        AddHandler oComboBox.SelectionChangeCommitted, New EventHandler(AddressOf ComboBox_SelectionChangeCommitted)
      End If
    End Sub
    

    目标 SCC公司 不增加计数,但从中选择 然后 比例尺 是的。)

    我已经尝试了很多,很多,很多可能的解决方案,但这里列举的太多了,其中大部分我都不记得,但没有一个成功。

    如何将处理程序限制为每次选择更改只触发一次?


    Namespace Mapping
      Public Class TargetsColumn
        Inherits DataGridViewComboBoxColumn
    
        Public Sub New()
          Task.Run(Sub()
                     Dim oHandler As DataGridViewEditingControlShowingEventHandler
    
                     While Me.DataGridView Is Nothing
                     End While
    
                     oHandler = New DataGridViewEditingControlShowingEventHandler(AddressOf DataGridView_EditingControlShowing)
    
                     RemoveHandler Me.DataGridView.EditingControlShowing, oHandler
                     AddHandler Me.DataGridView.EditingControlShowing, oHandler
                   End Sub)
        End Sub
    
    
    
        Private Sub DataGridView_EditingControlShowing(Sender As DataGridView, e As DataGridViewEditingControlShowingEventArgs)
          Dim oComboBox As ComboBox
    
          If TypeOf e.Control Is ComboBox Then
            oComboBox = e.Control
            oComboBox.DrawMode = DrawMode.OwnerDrawFixed
    
            RemoveHandler oComboBox.DrawItem, New DrawItemEventHandler(AddressOf ComboBox_DrawItem)
            AddHandler oComboBox.DrawItem, New DrawItemEventHandler(AddressOf ComboBox_DrawItem)
    
            RemoveHandler oComboBox.SelectionChangeCommitted, New EventHandler(AddressOf ComboBox_SelectionChangeCommitted)
            AddHandler oComboBox.SelectionChangeCommitted, New EventHandler(AddressOf ComboBox_SelectionChangeCommitted)
          End If
        End Sub
    
    
    
        Private Sub ComboBox_DrawItem(Sender As ComboBox, e As DrawItemEventArgs)
          Dim sThisTarget As String
          Dim oForeColor As Color
    
          Dim _
            iSeparatorBottom,
            iSeparatorRight,
            iSeparatorLeft As Integer
    
          Dim _
            oSeparatorStart,
            oSeparatorStop As Point
    
          sThisTarget = DirectCast(Me.Items(e.Index), Target).Value
    
          iSeparatorBottom = e.Bounds.Bottom - 2
          iSeparatorRight = e.Bounds.Right
          iSeparatorLeft = e.Bounds.Left
    
          e.DrawBackground()
    
          If e.Index = 0 Then
            oSeparatorStart = New Point(iSeparatorLeft, iSeparatorBottom)
            oSeparatorStop = New Point(iSeparatorRight, iSeparatorBottom)
            oForeColor = SystemColors.HotTrack
    
            e.Graphics.FillRectangle(SystemBrushes.Control, e.Bounds)
            e.Graphics.DrawLine(SystemPens.ControlDark, oSeparatorStart, oSeparatorStop)
          Else
            If Me.OtherTargets.Contains(sThisTarget) Then
              oForeColor = SystemColors.ControlLight
            Else
              oForeColor = e.ForeColor
            End If
          End If
    
          Using oBrush As New SolidBrush(oForeColor)
            e.Graphics.DrawString(sThisTarget, e.Font, oBrush, e.Bounds)
          End Using
    
          If e.State.HasFlag(DrawItemState.Focus) Then e.DrawFocusRectangle()
    
          Me.DataGridView.FindForm.Text = sThisTarget
        End Sub
    
    
    
        Private Sub ComboBox_SelectionChangeCommitted(Sender As ComboBox, e As EventArgs)
          ' '
          ' Look for other labels that have already been mapped to this target '
          ' '
          If Me.OtherTargetCells.Any(Function(Cell) Cell.FormattedValue = Sender.Text) Then
            If Me.IsInteractiveChange Then
              MsgBox("Target [] is already mapped to Label []. If you want to map Target [] to Label [], you must first set Label [] to [Not mapped].", MsgBoxStyle.Exclamation, Me.DataGridView.FindForm.Text)
    
              Me.IsInteractiveChange = False
              Sender.SelectedIndex = 0
              Me.IsInteractiveChange = True
            End If
          End If
        End Sub
    
    
    
        Private ReadOnly Property OtherTargets As List(Of String)
          Get
            Return Me.OtherTargetCells.Select(Function(Cell) DirectCast(Cell.FormattedValue, String)).ToList
          End Get
        End Property
    
    
    
        Private ReadOnly Property CurrentTargetCell As DataGridViewCell
          Get
            Return Me.AllTargetCells(Me.DataGridView.CurrentRow.Index)
          End Get
        End Property
    
    
    
        Private ReadOnly Property AllTargetCells As List(Of DataGridViewCell)
          Get
            Dim oAllCells As IEnumerable(Of DataGridViewCell)
            Dim oRows As IEnumerable(Of DataGridViewRow)
    
            oRows = Me.DataGridView.Rows.Cast(Of DataGridViewRow)
            oAllCells = oRows.SelectMany(Function(Row) Row.Cells.Cast(Of DataGridViewCell))
    
            Return oAllCells.Where(Function(Cell) TypeOf Cell Is DataGridViewComboBoxCell).ToList
          End Get
        End Property
    
    
    
        Private ReadOnly Property OtherTargetCells As List(Of DataGridViewCell)
          Get
            Return Me.AllTargetCells.Where(Function(Cell) Cell.RowIndex <> Me.RowIndex).ToList
          End Get
        End Property
    
    
    
        Private ReadOnly Property RowIndex As Integer
          Get
            Return Me.DataGridView.CurrentRow.Index
          End Get
        End Property
    
    
    
        Private IsInteractiveChange As Boolean = True
        Private ReadOnly ComboBoxes As New Dictionary(Of Integer, ComboBox)
      End Class
    End Namespace
    

    窗体1.vb

    Public Class Form1
      Inherits Form
    
      Public Sub New()
        Dim oColTargets As Mapping.TargetsColumn
        Dim oTargets As IEnumerable(Of String)
        Dim oQuery As Func(Of Target, Boolean)
        Dim sChannel As String
        Dim oTarget As Target
        Dim oMaps As Dictionary(Of Integer, String)
        Dim oMap As Map
    
        Dim _
          oColChannels,
          oColLabels As DataGridViewTextBoxColumn
    
        Me.InitializeComponent()
    
        Me.Targets.Add(New Target("Not mapped"))
    
        sChannel = String.Empty
        oQuery = Function(Target) Target.Value = sChannel
    
        'oTargets = Reader.Client.Create.Call(Function(Service As Reader.IService) Service.GetChannelTargets)'
        oTargets = New List(Of String) From {"Scale", "SCC", "CO", "O2"}
        oTargets.ToList.ForEach(Sub(Target)
                                  Me.Targets.Add(New Target(Target))
                                End Sub)
    
        'oMaps = Reader.Client.Create.Call(Function(Service As Reader.IService) Service.GetChannelMaps)'
        oMaps = New Dictionary(Of Integer, String) From {{3, "Test"}, {7, "SCC"}, {8, "Scale"}, {9, "CO"}, {10, "O2"}}
        oMaps.ToList.ForEach(Sub(Map)
                               sChannel = Map.Value
    
                               If Me.Targets.Any(oQuery) Then
                                 oTarget = Me.Targets.Single(oQuery)
                               Else
                                 oTarget = Me.Targets.First
                               End If
    
                               oMap = New Map With {
                                .Channel = Map.Key,
                                .Label = Map.Value,
                                .Target = oTarget
                               }
    
                               Me.Maps.Add(oMap)
                             End Sub)
    
        oColChannels = New DataGridViewTextBoxColumn With {
          .DataPropertyName = NameOf(Map.Channel),
          .AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader,
          .HeaderText = NameOf(Map.Channel),
          .ReadOnly = True,
          .Name = NameOf(oColChannels)
        }
    
        oColLabels = New DataGridViewTextBoxColumn With {
          .DataPropertyName = NameOf(Map.Label),
          .AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader,
          .HeaderText = NameOf(Map.Label),
          .ReadOnly = True,
          .Name = NameOf(oColLabels)
        }
    
        oColTargets = New Mapping.TargetsColumn With {
          .DataPropertyName = NameOf(Map.Target),
          .AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
          .DisplayMember = NameOf(Target.Value),
          .ValueMember = NameOf(Target.Self),
          .HeaderText = NameOf(Map.Target),
          .DataSource = Me.Targets,
          .Name = NameOf(oColTargets)
        }
    
        dgvMapping.AutoGenerateColumns = False
        dgvMapping.Columns.AddRange({oColChannels, oColLabels, oColTargets})
    
        For Each oColumn As DataGridViewColumn In dgvMapping.Columns
          oColumn.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter
    
          If oColumn.Index = 0 Then
            oColumn.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
          End If
        Next
    
        dgvMapping.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize
        dgvMapping.DataSource = New BindingList(Of Map)(Me.Maps)
    
        If dgvMapping.RowCount = 0 Then
          dgvMapping.Height = 150
        Else
          dgvMapping.Height = ((dgvMapping.RowCount + 0) * dgvMapping.Rows(0).Height) + dgvMapping.ColumnHeadersHeight
        End If
      End Sub
    
    
    
      Private Sub Form1_FormClosing(Sender As Form1, e As FormClosingEventArgs) Handles Me.FormClosing
        Dim oPolicy As Target = Me.Maps.First.Target
        Dim sName As String = Me.Maps.First.Channel
      End Sub
    
    
    
      Private Sub _dgvMapping_DataError(Sender As DataGridView, e As DataGridViewDataErrorEventArgs) Handles dgvMapping.DataError
        MsgBox(e.Exception.Message, MsgBoxStyle.Critical, Me.Text)
      End Sub
    
    
    
      Private Targets As New BindingList(Of Target)
      Private Maps As New List(Of Map)
    End Class
    
    
    
    Public Class Map
      Public Property Channel As Integer
      Public Property Label As String
      Public Property Target As Target
    End Class
    
    
    
    Public Class Target
      Public Sub New(Target As String)
        Me.Value = Target
      End Sub
    
    
    
      Public ReadOnly Property Self As Target
        Get
          Return Me
        End Get
      End Property
    
    
    
      Public ReadOnly Property Value As String
    End Class
    

    窗体1.Designer.vb

    <Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()>
    Partial Class Form1
      Inherits System.Windows.Forms.Form
    
      'Form overrides dispose to clean up the component list.'
      <System.Diagnostics.DebuggerNonUserCode()>
      Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
          If disposing AndAlso components IsNot Nothing Then
            components.Dispose()
          End If
        Finally
          MyBase.Dispose(disposing)
        End Try
      End Sub
    
      'Required by the Windows Form Designer'
      Private components As System.ComponentModel.IContainer
    
      'NOTE: The following procedure is required by the Windows Form Designer'
      'It can be modified using the Windows Form Designer.'
      'Do not modify it using the code editor.'
      <System.Diagnostics.DebuggerStepThrough()>
      Private Sub InitializeComponent()
        Me.dgvMapping = New System.Windows.Forms.DataGridView()
        CType(Me.dgvMapping, System.ComponentModel.ISupportInitialize).BeginInit()
        Me.SuspendLayout()
        ' '
        'dgvMapping'
        ' '
        Me.dgvMapping.AllowUserToAddRows = False
        Me.dgvMapping.AllowUserToDeleteRows = False
        Me.dgvMapping.AllowUserToOrderColumns = True
        Me.dgvMapping.AllowUserToResizeColumns = False
        Me.dgvMapping.AllowUserToResizeRows = False
        Me.dgvMapping.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize
        Me.dgvMapping.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter
        Me.dgvMapping.Location = New System.Drawing.Point(12, 12)
        Me.dgvMapping.Name = "dgvMapping"
        Me.dgvMapping.RowHeadersVisible = False
        Me.dgvMapping.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect
        Me.dgvMapping.Size = New System.Drawing.Size(250, 150)
        Me.dgvMapping.TabIndex = 0
        ' '
        'Form1'
        ' '
        Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
        Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
        Me.ClientSize = New System.Drawing.Size(800, 450)
        Me.Controls.Add(Me.dgvMapping)
        Me.Font = New System.Drawing.Font("Segoe UI", 8.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.Name = "Form1"
        Me.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen
        Me.Text = "Form1"
        CType(Me.dgvMapping, System.ComponentModel.ISupportInitialize).EndInit()
        Me.ResumeLayout(False)
    
      End Sub
    
      Friend WithEvents dgvMapping As DataGridView
    End Class
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   InteXX    6 年前

    固定的。

    我为每个 AddHandler / RemoveHandler 打电话来。

    当我移除实例化并使用简单的表达式时 ComboBox es开始正常工作了。

    Public Sub New()
      Task.Run(Sub()
                 While Me.DataGridView Is Nothing
                 End While
    
                 RemoveHandler Me.DataGridView.EditingControlShowing, AddressOf DataGridView_EditingControlShowing
                 AddHandler Me.DataGridView.EditingControlShowing, AddressOf DataGridView_EditingControlShowing
               End Sub)
    End Sub
    
    
    
    Private Sub DataGridView_EditingControlShowing(Sender As Object, e As DataGridViewEditingControlShowingEventArgs)
      Dim oComboBox As ComboBox
    
      If TypeOf e.Control Is ComboBox Then
        oComboBox = e.Control
        oComboBox.DrawMode = DrawMode.OwnerDrawFixed
    
        RemoveHandler oComboBox.DrawItem, AddressOf ComboBox_DrawItem
        AddHandler oComboBox.DrawItem, AddressOf ComboBox_DrawItem
    
        RemoveHandler oComboBox.SelectionChangeCommitted, AddressOf ComboBox_SelectionChangeCommitted
        AddHandler oComboBox.SelectionChangeCommitted, AddressOf ComboBox_SelectionChangeCommitted
      End If
    End Sub
    

    我不得不放松 Sender 参数类型 Object

    Private Sub DataGridView_EditingControlShowing(Sender As Object, e As DataGridViewEditingControlShowingEventArgs)
    End Sub
    
    Private Sub ComboBox_DrawItem(Sender As Object, e As DrawItemEventArgs)
    End Sub
    
    Private Sub ComboBox_SelectionChangeCommitted(Sender As Object, e As EventArgs)
    End Sub
    

    值得一提的是:我通常更喜欢约束 发件人 调用类型的参数,以便进行更有效的编码,但在这种情况下这是不可能的。尽管如此,唯一的影响是 在一个方法体中的一个位置:

    Dim oQuery = Function(Cell) Cell.FormattedValue = DirectCast(Sender, ComboBox).Text
    

    现在一切如期而至。