这个
DataGridViewComboBoxColumn
怪诞的最新体现是
ComboBox
事件处理程序,为单个用户操作触发次数不断增加。奇怪的是,增长率是前一个数字的两倍。
1, 2, 4, 8, 16, 32, 64
等)
首先,我将解释我要完成的任务,并澄清一些术语。
我有一个
Dictionary(Of Integer, String)
. 在我的领域规则中,我称之为
Key
渠道
以及
Value
财产
标签
. 我正在绘制每个
KeyValuePair
三分之一
String
调用的值
. 这个
字典(整数,字符串)
项目是固定的,它们作为用户的视觉帮助而存在,因此用户可以轻松地选择
目标
从一个
List(Of String)
.
我已经决定了
DataGridView
注意,已经映射
列表项以几乎不可见的颜色显示,以阻止用户再次尝试使用它们。(当已经映射了
目标
选择映射到不同的
.)
下面是我的完整代码库,但是为了快速查看,下面是重复的事件处理程序:
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