Windows 窗体 DataGridView 中的手风琴

Accordion in Windows Forms DataGridView

我需要在 Windows Form DataGridView 中实现某种手风琴效果。当用户选择一行时,该行会展开以显示更多信息,如果可能,还会显示一些按钮或其他控件。问题是,我完全不知道该怎么做。我试图在网上搜索,但没有发现任何东西可以引导我朝着正确的方向创建它。我希望有人能告诉我该怎么做? (不必是代码示例)

我创建了下面的模型来显示我想做的事情。

我考虑过调整列高并重写 OnPaint 方法。我只需要在第一个版本中显示一些文本。如果那是可能的,那就太好了。我知道将来我需要放置一些按钮或其他控件来对所选项目执行各种操作。如果实现起来非常复杂,我现在将跳过该部分。我知道我可以使用文本工具提示和按钮列等。但就我而言,我需要将其作为手风琴来完成。

此致 汉斯·米林…

这并不难做到。最好的方法是创建一个专用的 UserControl 并将其覆盖在正确的位置。

要为其腾出空间,只需更改行的 Height,跟踪该行,以便在失去选择时恢复它。

您还必须决定是否可以展开不止一行,以及用户应该如何展开和重置该行..

UserControl 可以有一个函数 displayRowData(DataGridViewRow row),您可以调用它来显示您在其 Labels 等中感兴趣的字段。

您还应该计划 Buttons 应如何与 DataGridView..

交互

如果你只想一次展开一行,你可以在前面创建UC,将DGV设为Parent并隐藏它。稍后在用户交互时,例如单击该行或某个单元格,您可以将其移动到右行并显示它..

如果可以展开多行,您将需要创建多个 UCs 并在 List..

中跟踪它们

这是一个鼓励你的小例子..:[=​​32=]

int normalRowHeight = -1;
UcRowDisplay display = new UcRowDisplay();
DataGridViewRow selectedRow = null;

public Form1()
{
    InitializeComponent();

    // create one display object
    display = new UcRowDisplay();
    display.Visible = false;
    display.Parent = DGV;
    display.button1Action = someAction;
}

填写 DGV 后

    // store the normal row height
    normalRowHeight = DGV.Rows[0].Height;

您至少需要这些活动:

private void DGV_SelectionChanged(object sender, EventArgs e)
{
    if (selectedRow != null) selectedRow.Height = normalRowHeight;
    if (DGV.SelectedRows.Count <= 0)
    {
        selectedRow = null;
        display.Hide();
        return;
    }
    // assuming multiselect = false
    selectedRow = DGV.SelectedRows[0];
    // assuming ColumnHeader show with the same height as the rows
    int y = (selectedRow.Index + 1) * normalRowHeight;
    display.Location = new Point(1, y);
    // filling out the whole width of the DGV.
    // maybe you need more, if the DGV is scrolling horizontally
    // or less if you show a vertical scrollbar.. 
    display.Width = DGV.ClientSize.Width;
    // make room for the display object
    selectedRow.Height = display.Height;
    // tell it to display our row data
    display.displayRowData(selectedRow);
    // show the display
    display.Show();
}

private void DGV_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e)
{
    // enforce refresh on the display
    display.Refresh();
}

这里是显示中按钮触发的测试动作object:

public void someAction(DataGridViewRow row)
{
    Console.WriteLine(row.Index + "  " + row.Cells[2].Value.ToString());
}

当然,您需要一个 UserControl。这是一个简单的,有两个标签,一个按钮和一个额外的标签来关闭显示:

public partial class UcRowDisplay : UserControl
{
    public UcRowDisplay()
    {
        InitializeComponent();
    }

    public delegate void someActionDelegate(DataGridViewRow row);
    public someActionDelegate button1Action { get; set; } 
    DataGridViewRow  myRow = null;

    public void displayRowData(DataGridViewRow row)
    {
        myRow  = row;
        label1.Text = row.Cells[1].Value.ToString();
        label2.Text = row.Cells[0].Value.ToString();
        rowDisplayBtn1.Text = row.Cells[2].Value.ToString();
    }

    private void rowDisplayBtn1_Click(object sender, EventArgs e)
    {
        button1Action(myRow);
    }

    private void label_X_Click(object sender, EventArgs e)
    {
        myRow.Selected = false;
        this.Hide();
    } 
}

它包含三个 Labels 和一个 Button。在设计器中它看起来像这样:

请注意,为了让我更轻松一些,我已将 DGV 修改为

  • 没有行标题;有的话修改位置。
  • 假定列 header 与行具有相同的高度。
  • 所有(正常)行具有相同的高度。
  • 将 DGV 设置为 multiselect=false 和 read-only

更新

这是滚动的简单示例:

private void DGV_Scroll(object sender, ScrollEventArgs e)
{
    DGV.PerformLayout();
    var ccr = DGV.GetCellDisplayRectangle(0, selectedRow.Index, true);
    display.Top = ccr.Top + normalRowHeight;  // **
    display.Visible = (ccr.Top >= 0 && ccr.Height > 0);
}

此 (**) 假定所选行应保持可见。同样的计算应该发生在 SelectionChanged 事件中!发生水平滚动时应该发生什么由您决定..

请注意,在当前行之前添加或删除行时,需要进行类似的更新。所以它应该被移动到你调用这些事件的函数..