Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
560 views
in Technique[技术] by (71.8m points)

c# - How to replace the HeaderCells of a DataGridView with custom headers?

When to replace a DataGridViewColumnHeaderCell with a custom HeaderCell (here: CustomDataGridViewColumnHeaderCell), when the DataSource of a DataGridView is already set to a DataTable object and also preserve the text of the previous HeaderCell, which is set to the name of the corresponding Column of the DataTable?

I have a custom header class for a DataGridView:

public class CustomDataGridViewColumnHeaderCell : DataGridViewColumnHeaderCell
{
    // adds a button to the header
}

A DataTable is being set as the DataSource, the Columns are created automatically. How can I replace the default header cells with custom header cells (instances of the above)?

More detail

enter image description here

The problem to solve is, a DataGridView needs to have clickable buttons embedded in its header cells, where the arrow is pointing to in the screenshot above. For that, there is a class CustomDataGridViewColumnHeaderCell. To show data in the DataGridView, a DataTable is being assigned as its data source. For example:

myDataGridView.DataSource = myDataTable;

After doing this, the DataGridView will have columns corresponding to those in the DataTable. I have derived a class from the default header classes, so that the buttons appear inside the column headers.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The HeaderCell of a DataGridViewColumn can be replaced at any time.

Even when the DataSource of the DataGridView is already set: in this case, you may want to set the Style of the current HeaderCell to your new cell, in case some values need to be replicated (when the Column are auto-generated, there's probably not much to copy, but since the Style property can be set, let's set it).

In the example, I'm adding a ReplaceHeaderCell() method to the custom HeaderCell, which receives the instance of the current header, copies some properties then disposes of it before replacing the corresponding Column's HeaderCell with itself.

If the Button we're adding to the HeaderCell is a standard Button Control, this Control must be added to the DataGridView.Controls collection (so it will be visible and brought to front).
If the Button is painted, overriding the Paint method, of course there's nothing to parent :).

Note: the Button is added in the OnDataGridViewChanged() method override. This method is called each time a DataGridView object becomes the owner of the HeaderCell. When the Parent DataGridView changes, the Button is automatically added to its Controls collection.

? Here, the Paint method is overridden anyway: it's used to calculate the HeaderCell Text padding, so the Button won't overlap and hide the Column's text (this procedure needs to be fine-tuned, in relation to the Button's specifics).

To replace the current HeaderCell of a Column (e.g., Column[0]), just create a new instance of the custom HeaderCell and call the ReplaceHeaderCell() method, passing the reference of the HeaderCell to replace:

var newButtonHeaderCell = new DGVButtonHeaderCell();
newButtonHeaderCell.ReplaceHeaderCell(dataGridView1.Columns[0].HeaderCell);

This is how it works:

DataGridView Custom HeaderCell

using System.Drawing;
using System.Windows.Forms;

class DGVButtonHeaderCell : DataGridViewColumnHeaderCell
{
    public readonly Button button;
    private Padding m_ButtonPadding = Padding.Empty;

    public DGVButtonHeaderCell(int buttonWidth = 40)
    {
        button = new Button() { BackColor = Color.Orange, Width = buttonWidth };
        m_ButtonPadding = new Padding(button.Width + 2, 0, 0, 0);
        button.Click += ButtonClick;
    }

    public void ReplaceHeaderCell(DataGridViewColumnHeaderCell previousHeader)
    {
        if (previousHeader == null) return;
        SetStandardValues(previousHeader);
        var dgv = previousHeader.DataGridView;
        previousHeader.Dispose();
        dgv.Columns[previousHeader.OwningColumn.Index].HeaderCell = this;
    }

    private void SetStandardValues(DataGridViewColumnHeaderCell previous)
    {
        if (previous == null) return;
        this.Style = previous.Style;
        button.Font = this.Style.Font ?? previous.DataGridView.ColumnHeadersDefaultCellStyle.Font;
        // etc.
    }

    private void ButtonClick(object sender, EventArgs e)
    {
        OnClick(new DataGridViewCellEventArgs(ColumnIndex, RowIndex));
        MessageBox.Show("You clicked me");
    }

    protected override void OnDataGridViewChanged()
    {
        if (this.DataGridView == null) return;
        this.DataGridView.Controls.Add(button);
    }

    protected override void Paint(Graphics g, Rectangle clipBounds, Rectangle bounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advBorderStyle, DataGridViewPaintParts parts)
    {
        cellStyle.Padding = Padding.Add(m_ButtonPadding, cellStyle.Padding);
        base.Paint(g, clipBounds, bounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advBorderStyle, parts);
        button.Location = new Point(bounds.Left, bounds.Top + 2);
        button.Height = bounds.Height - 4;
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing) {
            button.MouseClick -= ButtonClick;
            button.Dispose();
        }
        base.Dispose(disposing);
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...