2

I've seen How to: Host Controls in Windows Forms DataGridView Cells which explains how to host a control for editing a cell in a DataGridView. But how can I host a control for displaying a cell?

I need to display a file name and a button in the same cell. Our UI designer is a graphic designer not a programmer, so I have to match the code to what he's drawn, whether it's possible - or wise - or not. We're using VS2008 and writing in C# for .NET 3.5, if that makes a difference.

UPDATE: The 'net suggests creating a custom DataGridViewCell which hosts a panel as a first step; anyone done that?

Simon
  • 25,468
  • 44
  • 152
  • 266
  • Not in a way I'm happy with. I have a cell class that adds the control to the DataGridView's controls, and tries to keep it displayed in the correct place, but it's far from perfect. – Simon Feb 09 '09 at 09:31

4 Answers4

3

As per your "UPDATE", creating a custom DataGridViewCell is the way this is done. I've done it, and it doesn't require that much modification from the example code available from the MSDN. In my case, I needed a bunch of custom editing controls, so I ended up inheriting from DataGridViewTextBoxCell and DataGridViewColumn. I inserted into my class (the one inherited from DataGridViewTextBoxCell) a new custom control which implemented IDataGridViewEditingControl, and it all just worked.

I suppose that in your case, you could write a PanelDataGridViewCell which would contain a control MyPanelControl which would inherit from Panel and implement IDataGridViewEditingControl.

Shalom Craimer
  • 20,659
  • 8
  • 70
  • 106
  • The IDataGridViewEditingControl approach only works for editing. What about for displaying, too? – Simon Jan 29 '09 at 12:49
  • Actually, just before you edit, it's already showing you the content. So I guess it works for displaying too! – Shalom Craimer Feb 09 '09 at 05:39
  • No, it doesn't work at all. I don't want to display the cell's formatted value, I want to display the control. – Simon Feb 09 '09 at 09:30
2

There are two ways to do this:

1). Cast a DataGridViewCell to a certain cell type that exists. For example, convert a DataGridViewTextBoxCell to DataGridViewComboBoxCell type.

2). Create a control and add it into the controls collection of DataGridView, set its location and size to fit the cell that to be host.

See Zhi-Xin Ye's sample code below which illustrates the tricks:

private void Form_Load(object sender, EventArgs e)
{
    DataTable dt = new DataTable();
    dt.Columns.Add("name");
    for (int j = 0; j < 10; j++)
    {
        dt.Rows.Add("");
    }
    this.dataGridView1.DataSource = dt;
    this.dataGridView1.Columns[0].Width = 200;

    /*
    * First method : Convert to an existed cell type such ComboBox cell,etc
    */

    DataGridViewComboBoxCell ComboBoxCell = new DataGridViewComboBoxCell();
    ComboBoxCell.Items.AddRange(new string[] { "aaa","bbb","ccc" });
    this.dataGridView1[0, 0] = ComboBoxCell;
    this.dataGridView1[0, 0].Value = "bbb";

    DataGridViewTextBoxCell TextBoxCell = new DataGridViewTextBoxCell();
    this.dataGridView1[0, 1] = TextBoxCell;
    this.dataGridView1[0, 1].Value = "some text";

    DataGridViewCheckBoxCell CheckBoxCell = new DataGridViewCheckBoxCell();
    CheckBoxCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
    this.dataGridView1[0, 2] = CheckBoxCell;
    this.dataGridView1[0, 2].Value = true;

    /*
    * Second method : Add control to the host in the cell
    */
    DateTimePicker dtp = new DateTimePicker();
    dtp.Value = DateTime.Now.AddDays(-10);
    //add DateTimePicker into the control collection of the DataGridView
    this.dataGridView1.Controls.Add(dtp);
    //set its location and size to fit the cell
    dtp.Location = this.dataGridView1.GetCellDisplayRectangle(0, 3,true).Location;
    dtp.Size = this.dataGridView1.GetCellDisplayRectangle(0, 3,true).Size;
}

MSDN Reference : how to host different controls in the same column in DataGridView control

Using the 1st method looks like this:

Different Controls in DataGridView Column

Using the 2nd method looks like this:

enter image description here

Additional info: Controls in the same DataGridView column dont render while initializing grid

Community
  • 1
  • 1
Jeremy Thompson
  • 61,933
  • 36
  • 195
  • 321
2

Rather than use a datagridview, how about using a TableLayoutPanel instead. Create your control that has a label and a button and events and fill your layout panel with them. Your control becomes the cell so to speak. It doesn't take much to make the table layout panel to look like a datagridview, if that is the layout style you want.

Billy Coover
  • 3,827
  • 5
  • 36
  • 50
1

Usually you should host controls in winfors forms datagridview cells as shown here

But if you need your control to be always visible, what you can do is make a custom column inheriting from DataGridViewImageColumn. Add the control to the datagridview. Set the DefaultCellStyle.Nullvalue of the control to a bitmap of the control you want always shown on the data gridview. Then using the cellMouseEnter event you can display and reposition the control to display over the image cell. This gives the appearance that your custom control is always visible without using as much resources as creating a new instance of your control for every row added to the datagridview. This will help performance quite a bit.

Here is what I did with my custom “AddRemove” usercontrol.

public class AddRemoveColumn : DataGridViewImageColumn
{
    private AddRemove SelectionControl = null;
    private Bitmap SelectionControlImage = null;

    public AddRemoveColumn()
    {
        SelectionControl = new AddRemove();
    }

    #region Set Up Column
    protected override void OnDataGridViewChanged()
    {
        base.OnDataGridViewChanged();
        if (DataGridView != null)
        {
            Activate();
        }
    }

    private void Activate()
    {
        SelectionControl.LostFocus += SelectionControl_LostFocus;
        this.DataGridView.CellMouseEnter += DataGridView_CellMouseEnter;
        this.DataGridView.BackgroundColorChanged += DataGridView_BackgroundColorChanged;

        this.DataGridView.RowHeightChanged += DataGridView_RowHeightChanged;
        SelectionControl.OnAddClicked += AddClicked;
        SelectionControl.OnRemoveClicked += RemoveClicked;


        this.DataGridView.Controls.Add(SelectionControl);
        SelectionControl.Visible = false;

        this.Width = SelectionControl.Width;
        SelectionControl.BackColor = this.DataGridView.BackgroundColor;
        
        this.DataGridView.RowTemplate.Height = SelectionControl.Height +1;

        foreach (DataGridViewRow row in DataGridView.Rows)
        {
            row.Height = SelectionControl.Height+1;
        }

        SetNullImage();
    }
    #endregion

    private void AddClicked(int RowIndex)
    {
        MessageBox.Show("Add clicked on index=" + RowIndex.ToString());
    }

    private void RemoveClicked(int RowIndex)
    {
        MessageBox.Show("Removed clicked on index=" + RowIndex.ToString());
    }

    private void SetNullImage()
    {
        if (SelectionControlImage != null)
        {

            SelectionControlImage.Dispose();
        }
        
        SelectionControlImage = new Bitmap(SelectionControl.Width, SelectionControl.Height);

        SelectionControl.DrawToBitmap(SelectionControlImage, new Rectangle(0, 0, SelectionControlImage.Width, SelectionControlImage.Height));

        this.DefaultCellStyle.NullValue = SelectionControlImage;
    }

    private void DataGridView_RowHeightChanged(object sender, DataGridViewRowEventArgs e)
    {
        if (e.Row.Height <= 40)
        {
            e.Row.Height = 40;
        }

        SelectionControl.Visible = false;
        SetPosition(Index, e.Row.Index);
    }

    private void DataGridView_BackgroundColorChanged(object sender, EventArgs e)
    {
        SelectionControl.BackColor = this.DataGridView.BackgroundColor;


        SetNullImage();

    }

    private void SelectionControl_LostFocus(object sender, EventArgs e)
    {
        SelectionControl.Visible = false;
    }

    private void SetPosition(int ColumnIndex, int RowIndex)
    {
        Rectangle celrec = this.DataGridView.GetCellDisplayRectangle(ColumnIndex, RowIndex, true);//.Rows[e.RowIndex].Cells[e.ColumnIndex].GetContentBounds();

        int x_Offet = (celrec.Width - SelectionControl.Width)/ 2;
        int y_Offet = (celrec.Height - SelectionControl.Height)/2;

        SelectionControl.Location = new Point(celrec.X + x_Offet, celrec.Y + y_Offet);
        SelectionControl.Visible = true;
        SelectionControl.RowIndex = RowIndex;
    }

    private void DataGridView_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
    {
        if (e.ColumnIndex == this.Index)
        {
            SetPosition(e.ColumnIndex, e.RowIndex);
        }
    }
}

What this looks like

Patch
  • 11
  • 3