承载 ComboBox 控件的 DataGridColumnStyle (VB.NET) 重写ComboBox类 Imports System.Windows.Forms Public Class AutoCompleteComboBox Class AutoCompleteComboBox Inherits ComboBox Public Sub New()Sub New() MyBase.New() End Sub Private mResetOnClear As Boolean = False Protected Overrides Sub RefreshItem()Sub RefreshItem(ByVal index As Integer) MyBase.RefreshItem(index) End Sub Protected Overrides Sub SetItemsCore()Sub SetItemsCore(ByVal items As System.Collections.IList) MyBase.SetItemsCore(items) End Sub Public Shadows Sub KeyPress()Sub KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles MyBase.KeyPress Dim intIndex As Integer Dim strEntry As String If Char.IsControl(e.KeyChar) Then If MyBase.SelectionStart <= 1 Then If mResetOnClear Then MyBase.SelectedIndex = 0 MyBase.SelectAll() Else MyBase.Text = String.Empty MyBase.SelectedIndex = -1 End If e.Handled = True Exit Sub End If If MyBase.SelectionLength = 0 Then strEntry = MyBase.Text.Substring(0, MyBase.Text.Length - 1) Else strEntry = MyBase.Text.Substring(0, MyBase.SelectionStart - 1) End If ElseIf (Not Char.IsLetterOrDigit(e.KeyChar)) And (Not Char.IsWhiteSpace(e.KeyChar)) Then '< 32 Or KeyAscii > 127 Then Exit Sub Else If MyBase.SelectionLength = 0 Then strEntry = UCase(MyBase.Text & e.KeyChar) Else strEntry = MyBase.Text.Substring(0, MyBase.SelectionStart) & e.KeyChar End If End If MyBase.DroppedDown = Char.IsLetterOrDigit(e.KeyChar) intIndex = MyBase.FindString(strEntry) If intIndex <> -1 Then MyBase.SelectedIndex = intIndex MyBase.SelectionStart = strEntry.Length MyBase.SelectionLength = MyBase.Text.Length - MyBase.SelectionStart End If e.Handled = True Exit Sub End Sub Public Property ResetOnClear()Property ResetOnClear() As Boolean Get Return mResetOnClear End Get Set(ByVal Value As Boolean) mResetOnClear = Value End Set End PropertyEnd Class 实现类 Option Strict On Option Explicit On Imports System.Collections Imports System.ComponentModel Imports System.Drawing Imports System.Windows.Forms Imports System.Data Public Class DataGridComboBox Class DataGridComboBox Inherits AutoCompleteComboBox Public Sub New()Sub New() MyBase.New() End Sub Public isInEditOrNavigateMode As Boolean = TrueEnd Class Public Class DataGridComboBoxColumnStyle Class DataGridComboBoxColumnStyle Inherits DataGridColumnStyle Private xMargin As Integer = 2 Private yMargin As Integer = 1 Private Combo As DataGridComboBox Private _DisplayMember As String Private _ValueMember As String Private OldVal As String = String.Empty Private InEdit As Boolean = False Public Sub New()Sub New(ByRef DataSource As DataTable, _ ByVal DisplayMember As Integer, _ ByVal ValueMember As Integer) Combo = New DataGridComboBox _DisplayMember = DataSource.Columns.Item(index:=DisplayMember).ToString _ValueMember = DataSource.Columns.Item(index:=ValueMember).ToString With Combo .Visible = False .DataSource = DataSource .DisplayMember = _DisplayMember .ValueMember = _ValueMember End With End Sub Public Sub New()Sub New(ByRef DataSource As DataTable, _ ByVal DisplayMember As String, _ ByVal ValueMember As String) Combo = New DataGridComboBox With Combo .Visible = False .DataSource = DataSource .DisplayMember = DisplayMember .ValueMember = ValueMember End With End Sub Protected Overloads Overrides Sub Abort()Sub Abort(ByVal RowNum As Integer) Debug.WriteLine("Abort()") RollBack() HideComboBox() EndEdit() End Sub Protected Overloads Overrides Function Commit()Function Commit(ByVal DataSource As CurrencyManager, _ ByVal RowNum As Integer) As Boolean HideComboBox() If Not InEdit Then Return True End If Try Dim Value As Object = Combo.SelectedValue If NullText.Equals(Value) Then Value = Convert.DBNull End If SetColumnValueAtRow(DataSource, RowNum, Value) Catch e As Exception RollBack() Return False End Try EndEdit() Return True End Function Protected Overloads Overrides Sub ConcedeFocus()Sub ConcedeFocus() Combo.Visible = False End Sub Protected Overloads Overrides Sub Edit()Sub Edit(ByVal Source As CurrencyManager, _ ByVal Rownum As Integer, _ ByVal Bounds As Rectangle, _ ByVal [ReadOnly] As Boolean, _ ByVal InstantText As String, _ ByVal CellIsVisible As Boolean) Combo.Text = String.Empty Dim OriginalBounds As Rectangle = Bounds OldVal = Combo.Text If CellIsVisible Then Bounds.Offset(xMargin, yMargin) Bounds.Width -= xMargin * 2 Bounds.Height -= yMargin Combo.Bounds = Bounds Combo.Visible = True Else Combo.Bounds = OriginalBounds Combo.Visible = False End If Combo.SelectedValue = GetText(GetColumnValueAtRow(Source, Rownum)) If Not InstantText Is Nothing Then Combo.SelectedValue = InstantText End If Combo.RightToLeft = Me.DataGridTableStyle.DataGrid.RightToLeft Combo.Focus() If InstantText Is Nothing Then Combo.SelectAll() Else Dim [End] As Integer = Combo.Text.Length Combo.Select([End], 0) End If If Combo.Visible Then DataGridTableStyle.DataGrid.Invalidate(OriginalBounds) End If InEdit = True End Sub Protected Overloads Overrides Function GetMinimumHeight()Function GetMinimumHeight() As Integer Return Combo.PreferredHeight + yMargin End Function Protected Overloads Overrides Function GetPreferredHeight()Function GetPreferredHeight(ByVal g As Graphics, _ ByVal Value As Object) As Integer Debug.WriteLine("GetPreferredHeight()") Dim NewLineIndex As Integer = 0 Dim NewLines As Integer = 0 Dim ValueString As String = Me.GetText(Value) Do While NewLineIndex <> -1 NewLineIndex = ValueString.IndexOf("r/n", NewLineIndex + 1) NewLines += 1 End While Loop Return FontHeight * NewLines + yMargin End Function Protected Overloads Overrides Function GetPreferredSize()Function GetPreferredSize(ByVal g As Graphics, _ ByVal Value As Object) As Size Dim Extents As Size = Size.Ceiling(g.MeasureString(GetText(Value), _ Me.DataGridTableStyle.DataGrid.Font)) Extents.Width += xMargin * 2 + DataGridTableGridLineWidth Extents.Height += yMargin Return Extents End Function Protected Overloads Overrides Sub Paint()Sub Paint(ByVal g As Graphics, _ ByVal Bounds As Rectangle, _ ByVal Source As CurrencyManager, _ ByVal RowNum As Integer) Paint(g, Bounds, Source, RowNum, False) End Sub Protected Overloads Overrides Sub Paint()Sub Paint(ByVal g As Graphics, _ ByVal Bounds As Rectangle, _ ByVal Source As CurrencyManager, _ ByVal RowNum As Integer, _ ByVal AlignToRight As Boolean) Dim Text As String = GetText(GetColumnValueAtRow(Source, RowNum)) PaintText(g, Bounds, Text, AlignToRight) End Sub Protected Overloads Sub Paint()Sub Paint(ByVal g As Graphics, _ ByVal Bounds As Rectangle, _ ByVal Source As CurrencyManager, _ ByVal RowNum As Integer, _ ByVal BackBrush As Brush, _ ByVal ForeBrush As Brush, _ ByVal AlignToRight As Boolean) Dim Text As String = GetText(GetColumnValueAtRow(Source, RowNum)) PaintText(g, Bounds, Text, BackBrush, ForeBrush, AlignToRight) End Sub Protected Overloads Overrides Sub SetDataGridInColumn()Sub SetDataGridInColumn(ByVal Value As DataGrid) MyBase.SetDataGridInColumn(Value) If Not (Combo.Parent Is Value) Then If Not (Combo.Parent Is Nothing) Then Combo.Parent.Controls.Remove(Combo) End If End If If Not (Value Is Nothing) Then Value.Controls.Add(Combo) End Sub Protected Overloads Overrides Sub UpdateUI()Sub UpdateUI(ByVal Source As CurrencyManager, _ ByVal RowNum As Integer, ByVal InstantText As String) Combo.Text = GetText(GetColumnValueAtRow(Source, RowNum)) If Not (InstantText Is Nothing) Then Combo.Text = InstantText End If End Sub Private ReadOnly Property DataGridTableGridLineWidth()Property DataGridTableGridLineWidth() As Integer Get If Me.DataGridTableStyle.GridLineStyle = DataGridLineStyle.Solid Then Return 1 Else Return 0 End If End Get End Property Private Sub EndEdit()Sub EndEdit() InEdit = False Invalidate() End Sub Private Function GetText()Function GetText(ByVal Value As Object) As String If Value Is System.DBNull.Value Then Return NullText If Not Value Is Nothing Then Return Value.ToString Else Return String.Empty End If End Function Private Sub HideComboBox()Sub HideComboBox() If Combo.Focused Then Me.DataGridTableStyle.DataGrid.Focus() End If Combo.Visible = False End Sub Private Sub RollBack()Sub RollBack() Combo.Text = OldVal End Sub Private Sub PaintText()Sub PaintText(ByVal g As Graphics, _ ByVal Bounds As Rectangle, _ ByVal Text As String, _ ByVal AlignToRight As Boolean) Dim BackBrush As Brush = New SolidBrush(Me.DataGridTableStyle.BackColor) Dim ForeBrush As Brush = New SolidBrush(Me.DataGridTableStyle.ForeColor) PaintText(g, Bounds, Text, BackBrush, ForeBrush, AlignToRight) End Sub Private Sub PaintText()Sub PaintText(ByVal g As Graphics, _ ByVal TextBounds As Rectangle, _ ByVal Text As String, _ ByVal BackBrush As Brush, _ ByVal ForeBrush As Brush, _ ByVal AlignToRight As Boolean) Dim Rect As Rectangle = TextBounds Dim RectF As RectangleF = RectF.op_Implicit(Rect) ' Convert to RectangleF Dim Format As StringFormat = New StringFormat If AlignToRight Then Format.FormatFlags = StringFormatFlags.DirectionRightToLeft End If Select Case Me.Alignment Case Is = HorizontalAlignment.Left Format.Alignment = StringAlignment.Near Case Is = HorizontalAlignment.Right Format.Alignment = StringAlignment.Far Case Is = HorizontalAlignment.Center Format.Alignment = StringAlignment.Center End Select Format.FormatFlags = Format.FormatFlags Or StringFormatFlags.NoWrap g.FillRectangle(Brush:=BackBrush, Rect:=Rect) Rect.Offset(0, yMargin) Rect.Height -= yMargin g.DrawString(Text, Me.DataGridTableStyle.DataGrid.Font, ForeBrush, RectF, Format) Format.Dispose() End Sub Public ReadOnly Property ComboBox()Property ComboBox() As ComboBox Get Return Combo End Get End PropertyEnd Class 测试代码 Private Sub Form_Load() Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load InitForm(DataGrid1) Dim namesDataTable As DataTable namesDataTable = New DataTable("NamesTable") namesDataTable.Columns.Add("Name", GetType(String)) namesDataTable.Columns.Add("Sex", GetType(String)) Dim namesDataSet As DataSet = New DataSet namesDataSet.Tables.Add(namesDataTable) DataGrid1.DataSource = namesDataSet DataGrid1.DataMember = "NamesTable" AddGridStyle(DataGrid1) AddData(namesDataTable) End Sub Private Sub AddGridStyle() Sub AddGridStyle(ByRef myGrid As DataGrid) Dim myGridStyle As DataGridTableStyle = New DataGridTableStyle myGridStyle.MappingName = "NamesTable" Dim nameColumnStyle As DataGridTextBoxColumn = New DataGridTextBoxColumn nameColumnStyle.MappingName = "Name" nameColumnStyle.HeaderText = "Name" myGridStyle.GridColumnStyles.Add(nameColumnStyle) Dim mydt As DataTable mydt = New DataTable mydt.Columns.Add("id", GetType(String)) mydt.Columns.Add("text", GetType(String)) Dim newRowW As DataRow newRowW = mydt.NewRow() newRowW(0) = "0" newRowW(1) = "女" Dim newRowM As DataRow newRowM = mydt.NewRow() newRowM(0) = "1" newRowM(1) = "男" mydt.Rows.Add(newRowM) mydt.Rows.Add(newRowW) Dim comboxColumnStyle As New DataGridComboBoxColumnStyle(mydt, 1, 1) comboxColumnStyle.MappingName = "Sex" comboxColumnStyle.HeaderText = "Sex" comboxColumnStyle.Width = 100 myGridStyle.GridColumnStyles.Add(comboxColumnStyle) myGrid.TableStyles.Add(myGridStyle) End Sub Private Sub AddData() Sub AddData(ByRef namesDataTable As DataTable) Dim dRow As DataRow = namesDataTable.NewRow() dRow("Name") = "小站" dRow("Sex") = "女" namesDataTable.Rows.Add(dRow) dRow = namesDataTable.NewRow() dRow("Name") = "小李" dRow("Sex") = "男" namesDataTable.Rows.Add(dRow) dRow = namesDataTable.NewRow() dRow("Name") = "小吴" dRow("Sex") = "女" namesDataTable.Rows.Add(dRow) namesDataTable.AcceptChanges() End Sub Private Sub InitForm() Sub InitForm(ByRef myGrid As DataGrid) myGrid.TabStop = True myGrid.TabIndex = 1 End Sub