如何完美实现在DataGridView单元格中增加多个Button按钮?

实现DataGridView多按钮操作列

在很多WinForm过程中,经常会遇到使用DataGridView进行编辑的场景,用户希望在最后放一个操作列,里面放置两个按钮,一个增加行的按钮,一个删除行的按钮;并且第一行只有增加行的按钮,没有删除行的按钮,大概的界面如下:
DataGridView本身提供了DataGridViewButtonColumn列类型,但问题是只会放置一个Button在单元格里,不能满足我们的需求;通过网络搜索,有很多实现方案,最终选用了通过动态生成按钮的方案,并根据所在单元格的显示范围动态设置大小和位置。

该方案在实现过程有一些细节需要注意,否则会影响用户的使用体验,先记录如下,希望为后面有需要的朋友提供一些帮助,需要的朋友可到文章末尾获取完整源码,直接复制到自己的项目可用。

一、监控DataGridView的RowsAdded、RowsRemoved事件进行按钮的动态生成和移除

首先是动态生成按钮的时机,一定要在DataGridView的RowsAdded和RowsRemoved事件中去做,否则就需要再所有用户增加行的代码处都增加动态生成按钮的代码,对于后期的代码维护很不友好;而放在RowsAdded事件和RowsRemoved事件中可以一劳永逸的解决这个问题,代码如下:

private void dataGridView1_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
	Button btnAdd = new Button();
	btnAdd.Text = "+";
	btnAdd.Click += onAddButtonClick;
	dataGridView1.Controls.Add(btnAdd);

	Button btnRemove = new Button();
	btnRemove.Text = "-";
	btnRemove.Click += onRemoveButtonClick;
	dataGridView1.Controls.Add(btnRemove);

	...
}

private void dataGridView1_RowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
{
    int rowIndex = e.RowIndex;
	if (rowIndex >= 0 && rowIndex < mButtons.Count)
	{
		ActionButtons ab = mButtons[rowIndex];
		ab.AddButton.Visible = false;
		ab.AddButton.Dispose();
		ab.RemoveButton.Visible = false;
		ab.RemoveButton.Dispose();

		...
	}
}
二、使用列表按行顺序保存动态生成的按钮,用以支持按钮移除和按钮位置跟新

由于用户对于DataGridView的行操作是不可以允许,可能删除任意一行,也可能在任意一行后面插入,所以我们需要记录每个按钮对应的行索引,以便于后期更新时使用;为了将增加、删除按钮方便的存储并和对应的行进行映射,我们定义了一个ActionButtons类,将每一行对应的按钮都记录在ActionButtons实例中,并按顺序存入List表中,ActionButtons实例在List列表中的的索引号即是对应的DataGridView行号,代码如下:

internal class ActionButtons
{
    public ActionButtons(Button addButton, Button removeButton)
    {
        AddButton = addButton;
        RemoveButton = removeButton;
    }

    public Button AddButton { get; set; }
    public Button RemoveButton { get; set; }
}

List<ActionButtons> mButtons = new List<ActionButtons>();

private void dataGridView1_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
	Button btnAdd = new Button();
	btnAdd.Text = "+";
	btnAdd.Click += onAddButtonClick;
	dataGridView1.Controls.Add(btnAdd);

	Button btnRemove = new Button();
	btnRemove.Text = "-";
	btnRemove.Click += onRemoveButtonClick;
	dataGridView1.Controls.Add(btnRemove);

	mButtons.Add(new ActionButtons(btnAdd, btnRemove));
}

private void dataGridView1_RowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
{
    int rowIndex = e.RowIndex;
	if (rowIndex >= 0 && rowIndex < mButtons.Count)
	{
		ActionButtons ab = mButtons[rowIndex];
		ab.AddButton.Visible = false;
		ab.AddButton.Dispose();
		ab.RemoveButton.Visible = false;
		ab.RemoveButton.Dispose();

		mButtons.RemoveAt(rowIndex);
	}
}
三、监控DataGridView的Scroll、SizeChanged事件进行按钮的位置更新和显隐控制
在用户操作的过程中,有几个场景会需要对按钮的位置进行设置:
  • 新增的时候对按钮位置进行初始化。
  • 垂直滚动条由无到有时,按钮的位置需要更新;反之亦然。
  • 滚动条发生滚动时,按钮的位置需要更新。
  • DataGridView列宽度发生改变时,按钮的位置需要更新。
  • DataGridView大小发生变化时,按钮的位置需要更新。
同样,有几个场景会需要对按钮进行显隐状态的控制:
  • 当按钮所在行不在DataGridView显示范围内时要对按钮进行隐藏。
  • 当按钮所在行被删除时要对按钮进行隐藏。
  • DataGridView列宽度发生改变时,按钮的位置需要更新。

每次对位置和显隐的更新都需要遍历所有按钮,并且将更新代码放到线程中执行,以避免行增加、删除的同步操作对判断产生影响,具体代码如下:

private void HideAllActionButtons(ActionButtons ab)
{
    dataGridView1.BeginInvoke(() =>
    {
    	ab.AddButton.Visible = ab.RemoveButton.Visible = false;
    });
}

private void UpdateActionButtonsPosition(ActionButtons ab, Rectangle rect, int rowIndex)
{
    dataGridView1.BeginInvoke(() =>
    {
    	ab.AddButton.Location = new Point(rect.Left + 5, rect.Top + 5);
    	ab.AddButton.Size = new Size(rect.Width / 2 - 10, rect.Height - 10);
    	ab.AddButton.Visible = true;

    	ab.RemoveButton.Location = new Point(ab.AddButton.Left + ab.AddButton.Width + 5, rect.Top + 5);
    	ab.RemoveButton.Size = ab.AddButton.Size;
    	ab.RemoveButton.Visible = rowIndex > 0;
    });
}

private void RepositionActionButtons()
{
    Task.Run(() =>
    {
         Thread.Sleep(100);

         int firstRow = dataGridView1.FirstDisplayedScrollingRowIndex;
         int lastRow = firstRow + dataGridView1.DisplayedRowCount(false);

         for (int i = 0; i < mButtons.Count; i++)
         {
             ActionButtons ab = mButtons[i];
             ab.AddButton.Tag = i;
             ab.RemoveButton.Tag = i;

             if (i >= firstRow && i <= lastRow)
             {
                 Rectangle rect = dataGridView1.GetCellDisplayRectangle(btnColIndex, i, false);
                 UpdateActionButtonsPosition(ab, rect, i);
             }
             else
             {
                 HideAllActionButtons(ab);
             }
         }
     });
}

private void dataGridView1_Scroll(object sender, ScrollEventArgs e)
{
    RepositionActionButtons();
}

private void dataGridView1_SizeChanged(object sender, EventArgs e)
{
    RepositionActionButtons();
}

private void dataGridView1_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e)
{
    RepositionActionButtons();
    this.Refresh();
}

注:在列宽度发生改变时,多调用了一个Refresh方法,用于刷新界面;是因为在拖动列改变宽度时有可能会再按钮表面留下拖动的痕迹,通过刷新可以恢复按钮的样式

最终的效果如下图:
以上功能以在实际项目中使用,效果完美,有需要的朋友可通过下方链接获取完整代码,将其中的内容复制到自己的项目中即可。

下载源码

  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 以下是 VB.NET 实现 DataGridView多个单元格的复制与粘贴的代码: ' 复制选定单元格 Private Sub CopySelectedCells() ' 获取选定单元格的数量 Dim cellCount As Integer = DataGridView1.GetCellCount(DataGridViewElementStates.Selected) ' 如果选定单元格数量为 ,则退出 If cellCount = Then Return End If ' 创建一个 StringBuilder 对象,用于保存复制的内容 Dim sb As New StringBuilder() ' 遍历选定单元格 For i As Integer = To cellCount - 1 ' 获取当前单元格的值 Dim cellValue As Object = DataGridView1.SelectedCells(i).Value ' 如果单元格的值为空,则跳过 If cellValue Is Nothing OrElse cellValue Is DBNull.Value Then Continue For End If ' 将单元格的值添加到 StringBuilder 对象 sb.Append(cellValue.ToString()) ' 如果不是最后一个单元格,则添加制表符 If i < cellCount - 1 Then sb.Append(vbTab) End If Next ' 将 StringBuilder 对象的内容复制到剪贴板 Clipboard.SetText(sb.ToString()) End Sub ' 粘贴到选定单元格 Private Sub PasteToSelectedCells() ' 获取剪贴板的文本 Dim text As String = Clipboard.GetText() ' 如果剪贴板的文本为空,则退出 If String.IsNullOrEmpty(text) Then Return End If ' 将文本按制表符分割成数组 Dim values As String() = text.Split(vbTab) ' 获取选定单元格的数量 Dim cellCount As Integer = DataGridView1.GetCellCount(DataGridViewElementStates.Selected) ' 如果选定单元格数量为 ,则退出 If cellCount = Then Return End If ' 定义一个计数器,用于记录已经粘贴的单元格数量 Dim pasteCount As Integer = ' 遍历选定单元格 For i As Integer = To cellCount - 1 ' 获取当前单元格 Dim cell As DataGridViewCell = DataGridView1.SelectedCells(i) ' 如果当前单元格不可编辑,则跳过 If Not cell.ReadOnly Then ' 如果已经粘贴完所有的单元格,则退出 If pasteCount >= values.Length Then Exit For End If ' 将文本赋值给当前单元格 cell.Value = values(pasteCount) ' 增加计数器 pasteCount += 1 End If Next End Sub 请注意,以上代码仅供参考,具体实现可能需要根据实际情况进行调整。 ### 回答2: 在VB.NET实现DataGridView多个单元格的复制与粘贴,我们可以借助Clipboard对象来实现。 首先,我们需要为DataGridView控件添加两个菜单项,一个是复制菜单项,一个是粘贴菜单项。可以通过右键点击DataGridView控件,在设计器选择"Edit Columns",然后在弹出的编辑列窗口添加两个菜单项。 然后,在DataGridView的CellMouseDown事件,判断鼠标右键点击事件,并且选了某一行,将选行的单元格内容复制到剪贴板。代码如下: ```vb Private Sub DataGridView1_CellMouseDown(sender As Object, e As DataGridViewCellMouseEventArgs) Handles DataGridView1.CellMouseDown If e.Button = MouseButtons.Right AndAlso e.RowIndex > -1 Then DataGridView1.Rows(e.RowIndex).Selected = True Clipboard.SetText(DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value.ToString()) End If End Sub ``` 在DataGridView添加一个粘贴按钮的点击事件,在事件将剪贴板的内容粘贴到选单元格。代码如下: ```vb Private Sub PasteToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles PasteToolStripMenuItem.Click Dim rowIndex As Integer = DataGridView1.SelectedCells(0).RowIndex Dim columnIndex As Integer = DataGridView1.SelectedCells(0).ColumnIndex Dim pasteData As String = Clipboard.GetText() Dim rows As String() = pasteData.Split(vbCrLf.ToCharArray(), StringSplitOptions.RemoveEmptyEntries) For i As Integer = 0 To rows.Length - 1 Dim rowData As String() = rows(i).Split(vbTab.ToCharArray()) For j As Integer = 0 To rowData.Length - 1 If rowIndex < DataGridView1.Rows.Count AndAlso columnIndex < DataGridView1.Columns.Count Then DataGridView1.Rows(rowIndex).Cells(columnIndex).Value = rowData(j) End If columnIndex += 1 Next rowIndex += 1 Next End Sub ``` 这样,我们就实现了在DataGridView多个单元格的复制与粘贴功能。 ### 回答3: 在VB.NET,可以通过以下代码实现DataGridView多个单元格的复制与粘贴功能: 首先,我们需要为DataGridView控件添加键盘快捷键的事件处理程序,以便捕获Ctrl+C(复制)和Ctrl+V(粘贴)的按键事件。在窗体的Load事件,添加以下代码: Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load AddHandler DataGridView1.KeyDown, AddressOf DataGridView1_KeyDown End Sub 然后,在DataGridView的KeyDown事件处理程序,我们可以根据Ctrl键的状态来执行相应的复制和粘贴操作。添加以下代码: Private Sub DataGridView1_KeyDown(sender As Object, e As KeyEventArgs) If e.Control AndAlso e.KeyCode = Keys.C Then CopyCells() ElseIf e.Control AndAlso e.KeyCode = Keys.V Then PasteCells() End If End Sub 接下来,我们需要实现复制和粘贴操作的具体代码。首先是复制操作CopyCells(): Private Sub CopyCells() Dim selectedCells As DataGridViewSelectedCellCollection = DataGridView1.SelectedCells Dim data As New StringBuilder() For Each cell As DataGridViewCell In selectedCells If cell.Value IsNot Nothing Then data.Append(cell.Value.ToString()) End If If cell.ColumnIndex <> DataGridView1.Columns.Count - 1 Then data.Append(vbTab) End If Next Clipboard.SetText(data.ToString()) End Sub 代码,我们使用了一个StringBuilder对象来存储复制的数据,并使用一个制表符(vbTab)分隔不同的单元格值。最后,通过Clipboard.SetText()方法将复制的数据设置到剪贴板。 然后是粘贴操作PasteCells(): Private Sub PasteCells() Dim selectedCells As DataGridViewSelectedCellCollection = DataGridView1.SelectedCells Dim cellValues() As String = Clipboard.GetText().Split(vbTab) For Each cell As DataGridViewCell In selectedCells If cellValues.Length > 0 Then cell.Value = cellValues(0) cellValues = cellValues.Skip(1).ToArray() End If Next End Sub 在粘贴操作,我们首先将剪贴板的文本数据按制表符分隔成一个字符串数组,然后将每个单元格的值依次设置为数组的值。 现在,当用户按下Ctrl+C键时,选单元格值会被复制到剪贴板,而当用户按下Ctrl+V键时,剪贴板的数据会被粘贴到选单元格

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值