【问题标题】:DataGridViewComboBoxColumn odd behaviour, temporarily resizing on CellEnterDataGridViewComboBoxColumn 奇怪的行为,在 CellEnter 上临时调整大小
【发布时间】:2019-03-07 11:54:48
【问题描述】:

我有一个包含许多列的 DataGridView;其中之一是DataGridViewComboBoxColumn。我已经设置了我的DataGridView.EditMode = EditOnEnter

我的行设置为几行文本的高度,因为这向用户显示了所有可用的文本空间,但这似乎会导致 ComboBox 单元格在单击时临时调整大小。

这是我的专栏的屏幕截图:

第二个、第三个和第四个单元格是默认的行高。第一个单元格是第一次单击单元格时 ComboBox 的状态。然后您必须再次单击(在现在的小框中)以实际显示 ComboBox 的内容:

看起来组合框正在调整到文本的高度。

一旦您离开单元格,它会将自身调整回默认行高:

如何阻止这种行为并强制 ComboBox 保持其大小等于行高?

【问题讨论】:

  • 当 ComboBox 显示时,它会自己绘制以匹配内容的高度。如果你想改变它,你需要创建一个自定义的 ComboBox Column
  • 是否可以拦截draw事件并手动设置高度,而不是创建自定义组合框列(我真的不知道该怎么做)?
  • 您所描述的“拦截绘图事件”是您创建自定义控件时发生的情况。在这种情况下,您将从现有的 ComboBox 继承,然后重新绘制它。在 WinForms 下,它自己的前景很艰难。不了解 WPF。
  • 如果您仍然对如何执行此操作感兴趣,我可以给您一些代码来满足您的需求。我以前做过的事情。
  • 我一直对学习如何做新事物很感兴趣。

标签: vb.net datagridview combobox


【解决方案1】:

这是自定义组合框。将此添加到您的项目中,您可以将其放入 Windows 窗体中并用作普通的ComboBox

自定义组合框

<ToolboxItem(true)> _
<ToolboxBitmap(GetType(ComboBox))> _
partial public class CustomComboBox
    Inherits ComboBox
    sub New()
        DrawMode = DrawMode.OwnerDrawFixed
        DropDownStyle = ComboBoxStyle.DropDownList

        ItemHeight = 26
        DropDownHeight = ItemHeight * 6
    end sub

    protected Overrides sub OnDrawItem( e As DrawItemEventArgs)
        if (e.Index >= 0) then
            e.DrawBackground()
            e.DrawFocusRectangle()

            using b as New SolidBrush(ForeColor)
                dim name as String = Items(e.Index).ToString()

                dim textLeft as Int32 = e.Bounds.Left + 3
                dim textTop as Int32 = e.Bounds.Top + (e.Bounds.Height / 2) - (Font.Height / 2)
                dim textWidth  as Int32 = e.Bounds.Width - e.Bounds.Width
                dim textHeight  as Int32 = e.Bounds.Height
                Dim textTarget  as Rectangle = new Rectangle(textLeft, textTop, textWidth, textHeight)

                e.Graphics.DrawString(name, Font, b, textTarget)
            end using
        end if
    end sub
End Class

要更改ComboBox 的高度和分配给其中每个项目的空间量,请更改构造函数中的值ItemHeight


为了将其放入 DataGridView,您需要创建自定义单元格、列和 EditControl。从下面的代码可以看出,这三个都相对容易创建。

CustomComboBoxCell

public class CustomComboBoxCell 
    Inherits DataGridViewTextBoxCell

    public overrides sub InitializeEditingControl(rowIndex As Int32 , initialFormattedValue As Object , dataGridViewCellStyle As DataGridViewCellStyle )
        ' Set the value of the editing control to the current cell value. 
        mybase.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle)

        dim ctl as CustomComboBoxEditControl = CType(DataGridView.EditingControl, CustomComboBoxEditControl)
        Dim col As CustomComboBoxColumn = CType(DataGridView.Columns(DataGridView.CurrentCell.ColumnIndex), CustomComboBoxColumn)
        Dim row As DataGridViewRow = CType(DataGridView.Rows(DataGridView.CurrentCell.RowIndex), DataGridViewRow)

        ctl.DataSource = col.DataSource
        ctl.Height = DataGridView.RowTemplate.Height
        ctl.ItemHeight = row.Height - 6

        ' Use the default row value when Value property is null. 
        if (me.Value is Nothing OrElse me.Value is DBNull.Value)
            ctl.Text = me.DefaultNewRowValue
        else
            ctl.Text = me.Value
        end if
    end sub

    public overrides ReadOnly property EditType() As Type
        get
            return GetType(CustomComboBoxEditControl)
        end get
    end Property

    public overrides readonly property FormattedValueType () as Type
        get 
             return GetType(String)
        end get
    end Property

    public overrides ReadOnly property ValueType() As Type
        get
            return GetType(String)
        End Get
    end property

    public overrides ReadOnly property DefaultNewRowValue() as Object
        get
            return String.Empty
        end Get
    end Property

    protected Overrides sub Paint(graphics As Graphics , _
                                  clipBounds As Rectangle , _ 
                                  cellBounds As Rectangle , _
                                  rowIndex As int32 , _
                                  cellState As DataGridViewElementStates , _ 
                                  value As Object , _
                                  formattedValue As Object , _
                                  errorText As String , _
                                  cellStyle As DataGridViewCellStyle , _
                                  advancedBorderStyle As DataGridViewAdvancedBorderStyle , _
                                  paintParts As DataGridViewPaintParts)
        'base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts)
        mybase.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, String.Empty, String.Empty, errorText, cellStyle, advancedBorderStyle, paintParts)

        if (TypeOf value is string)
            Dim valueString As String = Convert.ToString(value)

            if (not String.IsNullOrEmpty(valueString))
                Dim b As Brush = new SolidBrush(cellStyle.ForeColor)

                Dim textLeft as Int32 = cellBounds.Left + 6
                Dim textTop  as Int32 = cellBounds.Top + (cellBounds.Height / 2) - (cellStyle.Font.Height / 2)
                Dim textWidth  as Int32 = cellBounds.Width - cellBounds.Width
                Dim textHeight as Int32 = cellBounds.Height
                Dim textTarget As Rectangle = new Rectangle(textLeft, textTop, textWidth, textHeight)

                graphics.DrawString(valueString, cellStyle.Font, b, textTarget)
            end if
        end if
    end sub
end class

自定义组合框列

public class CustomComboBoxColumn 
    Inherits DataGridViewColumn

    sub new ()
        mybase.new(New CustomComboBoxCell())
    end sub

    public Property DataSource() as Object

    public overrides property CellTemplate() As DataGridViewCell 
        get
            return mybase.CellTemplate
        end get

        set
            Dim targetType as Type = GetType(CustomComboBoxCell)
            ' Ensure that the cell used for the template is a CustomComboBoxCell. 
            if (not IsNothing(value) AndAlso 
                not value.GetType().IsAssignableFrom(targetType))

                Dim errorMessage As String = $"CellTemplate must be of the type {targetType}."
                throw new InvalidCastException(errorMessage)
            end if

            mybase.CellTemplate = value
        end set
    End Property

    public overrides Function Clone() as Object 
        Dim retVal As CustomComboBoxColumn = CType(mybase.Clone(), CustomComboBoxColumn)
        retVal.DataSource = me.DataSource

        return retVal
    End Function
end class

CustomComboBoxEditControl

<ToolboxItem(false)>
public class CustomComboBoxEditControl 
    Inherits CustomComboBox
    Implements IDataGridViewEditingControl

    public Sub New ()
        IDataGridViewEditingControl_EditingControlFormattedValue = false
    end sub


    Public Property IDataGridViewEditingControl_EditingControlDataGridView As DataGridView Implements IDataGridViewEditingControl.EditingControlDataGridView
    Public Property IDataGridViewEditingControl_EditingControlValueChanged As Boolean Implements IDataGridViewEditingControl.EditingControlValueChanged
    Public Property IDataGridViewEditingControl_EditingControlRowIndex As Integer Implements IDataGridViewEditingControl.EditingControlRowIndex

    Public Function IDataGridViewEditingControl_GetEditingControlFormattedValue(context As DataGridViewDataErrorContexts) As Object Implements IDataGridViewEditingControl.GetEditingControlFormattedValue
        return IDataGridViewEditingControl_EditingControlFormattedValue
    End Function

    Public ReadOnly Property IDataGridViewEditingControl_RepositionEditingControlOnValueChange As Boolean Implements IDataGridViewEditingControl.RepositionEditingControlOnValueChange
        get
            return False
        End Get
    end property

    Public ReadOnly Property IDataGridViewEditingControl_EditingPanelCursor As Cursor Implements IDataGridViewEditingControl.EditingPanelCursor
        get
            Return MyBase.Cursor
        End Get
    end Property

    Public Property IDataGridViewEditingControl_EditingControlFormattedValue As Object Implements IDataGridViewEditingControl.EditingControlFormattedValue
        get
            return me.selectedItem
        End Get
        Set(value As Object)
            me.SelectedItem = value
        End Set
    end property

    Public Sub IDataGridViewEditingControl_ApplyCellStyleToEditingControl(dataGridViewCellStyle As DataGridViewCellStyle) Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl
        me.Font = dataGridViewCellStyle.Font
        me.ForeColor = dataGridViewCellStyle.ForeColor
        me.BackColor = dataGridViewCellStyle.BackColor
    End Sub

    Public Function IDataGridViewEditingControl_EditingControlWantsInputKey(keyData As Keys, dataGridViewWantsInputKey As Boolean) As Boolean Implements IDataGridViewEditingControl.EditingControlWantsInputKey
        select (keydata and Keys.KeyCode)
            case Keys.Escape, Keys.Up, Keys.Down, Keys.Home, Keys.End, Keys.PageDown, Keys.PageUp
                return true
            Case else
                return Not dataGridViewWantsInputKey
        end Select
    End Function

    Public Sub     IDataGridViewEditingControl_PrepareEditingControlForEdit(selectAll As Boolean) Implements IDataGridViewEditingControl.PrepareEditingControlForEdit
        ' Do nothing
    End Sub

    protected overrides sub OnSelectedValueChanged( eventArgs as EventArgs)
        ' Notify the DataGridView that the contents of the cell have changed.
        Me.IDataGridViewEditingControl_EditingControlValueChanged = true
        Me.IDataGridViewEditingControl_EditingControlDataGridView.NotifyCurrentCellDirty(true)
    end sub
End Class

然后使用以下命令将其添加到表单中:

grid.AutoGenerateColumns = false
grid.RowTemplate.Height = 45

Dim customComboBoxColumn as new CustomComboBoxColumn()
customComboBoxColumn.DataPropertyName = "Custom Drop Down"
customComboBoxColumn.DataSource = DropDownItems1
dataGridView1.Columns.Add(customComboBoxColumn)

Dim regularComboBoxColumn as new DataGridViewComboBoxColumn()
regularComboBoxColumn.DataPropertyName = "Regular Drop Down"
regularComboBoxColumn.DataSource = DropDownItems2
dataGridView1.Columns.Add(regularComboBoxColumn)

这是放置在表单上时的样子。左列是新的CustomComboBoxColumn,右列是标准的DataGridViewComboBoxColumn

【讨论】:

  • 谢谢,这看起来非常有用。与 VB 相比,我的 C# 不是很流利,但我想大致的想法就在那里。对于所有 DataGridView 引用(例如DataGridViewTextBoxCellDataGridViewColumn),我收到了一些缺少的引用/程序集警告
  • 我为 C# 代码而不是 VB.net 道歉,我完全忘了检查语言。今晚我将在 VB.Net 中重新编写它并重新发布代码。缺少引用异常应该通过引用System.Windows.Forms来解决
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-14
  • 2014-06-09
  • 1970-01-01
  • 2017-11-05
相关资源
最近更新 更多