none
Multiple controls in a single DataGridView cell RRS feed

  • Question

  • Hi, I need to put a text box and button within a single DataGridView cell, the user should be able to edit the text or click the button which brings out another dialog. But I am not going to use PropertyGrid, use DataGridView only,

     

    ____________

    text      | button |

     

    this is a single cell, user can click the text to modify it, or click the button

     

    I've tried to modify the calender picker example in microsoft.com, but somehow didn't work out, the user cannot edit the text...

     

    Thanks!

    Thursday, March 29, 2007 10:17 AM

Answers

  • I have another way to do this, we can take the following steps:

    1). Make a usercontrol to host a textbox and a button, as shown in my following sample, the TextAndButtonControl class which inherited from usercontrol.

    2). Create a TextAndButtonControl instance(NOTICE: we just need one), add it into the control collection of the DataGridView, initially make it invisible.

    3). Handle the DataGridView.CellPainting event to draw a textbox and button style on the cell, which make the cell looked like some kind hosting a usercontrol in it.

    4). Handle the DataGridView.CellBeginEdit event to show the usercontrol right in the cell while editing, you can edit in the textbox in the usercontrol and click the button, write your logic in the Click event of the button, I just show a message box for example in this sample.

    5). Handle the DataGridView.CellEndEdit event to update the cell value.

    6). Handle the DataGridView.Scroll event to reset the location and size of the usercontrol while scrolling. Without handling this, the usercontrol would stay still while scrolling.

    That's all. I write a sample for your information. Enjoy taking it.

    Any question about this please no hesitate to let me know.

    Best Regards.
    Zhixin Ye


    Code Snippet

    using System;

    using System.Collections.Generic;

    using System.ComponentModel;

    using System.Data;

    using System.Drawing;

    using System.Text;

    using System.Windows.Forms;

     

    namespace Sample5

    {

        public partial class DgvTextAndButton : Form

        {

            public DgvTextAndButton()

            {

                InitializeComponent();

            }

     

            private void DgvTextAndButton_Load(object sender, EventArgs e)

            {

                DataTable dt = new DataTable();

                dt.Columns.Add("col1");

                dt.Columns.Add("col2");

                for (int j = 0; j < 20; j++)

                {

                    dt.Rows.Add("col1"+j.ToString(),"col2"+j.ToString());

                }

                this.dataGridView1.DataSource = dt;

                this.dataGridView1.Columns[0].Width = 150;

     

                this.txbtnControl = new TextAndButtonControl();

                this.txbtnControl.Visible = false;

                this.dataGridView1.Controls.Add(this.txbtnControl);

     

                //Handle this event to paint a textbox and button style in the cell,

                //this painting avoid using amount of usercontrols, we just need one

                this.dataGridView1.CellPainting += new DataGridViewCellPaintingEventHandler(dataGridView1_CellPainting);

     

                //Handle the cellbeginEdit event to show the usercontrol in the cell while editing

                this.dataGridView1.CellBeginEdit += new DataGridViewCellCancelEventHandler(dataGridView1_CellBeginEdit);

                //Handle the cellEndEdit event to update the cell value

                this.dataGridView1.CellEndEdit += new DataGridViewCellEventHandler(dataGridView1_CellEndEdit);

     

                //Handle the scroll event to reset the location and size of the usercontrol while scrolling

                this.dataGridView1.Scroll += new ScrollEventHandler(dataGridView1_Scroll);

            }

     

            TextAndButtonControl txbtnControl;

     

            void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)

            {

                if (e.ColumnIndex == 0 && e.RowIndex > -1 && e.RowIndex != this.dataGridView1.NewRowIndex)

                {

                    Rectangle textRect = e.CellBounds;

                    textRect.Width -= e.CellBounds.Width / 3;

                    Rectangle btnRect = e.CellBounds;

                    btnRect.X += textRect.Width;

                    btnRect.Width = e.CellBounds.Width / 3;

                    e.Paint(textRect, DataGridViewPaintParts.All);

                    ControlPaint.DrawButton(e.Graphics, btnRect, ButtonState.Normal);

                    StringFormat formater = new StringFormat();

                    formater.Alignment = StringAlignment.Center;

                    e.Graphics.DrawString("click", e.CellStyle.Font, new SolidBrush(e.CellStyle.ForeColor), btnRect, formater);

                    e.Handled = true;

                }

            }

     

            void dataGridView1_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)

            {

                if (e.ColumnIndex == 0 && e.RowIndex > -1 && e.RowIndex != this.dataGridView1.NewRowIndex)

                {

                    Rectangle rect = this.dataGridView1.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, true);

                    this.txbtnControl.Location = rect.Location;

                    this.txbtnControl.Size = rect.Size;

                    this.txbtnControl.Text = this.dataGridView1.CurrentCell.Value.ToString();

                    this.txbtnControl.ButtonText = "click";

                    this.txbtnControl.renderControl();

                    this.txbtnControl.Visible = true;

                }

            }

     

            void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)

            {

                if (e.ColumnIndex == 0 && e.RowIndex > -1 && e.RowIndex != this.dataGridView1.NewRowIndex)

                {

                    this.dataGridView1.CurrentCell.Value = this.txbtnControl.Text;

                    this.txbtnControl.Visible = false;

                }

            }

     

            void dataGridView1_Scroll(object sender, ScrollEventArgs e)

            {

                if (this.txbtnControl.Visible == true)

                {

                    Rectangle r = this.dataGridView1.GetCellDisplayRectangle(

                        this.dataGridView1.CurrentCell.ColumnIndex,

                        this.dataGridView1.CurrentCell.RowIndex,

                        true);

                    this.txbtnControl.Location = r.Location;

                    this.txbtnControl.Size = r.Size;

                }

            }

        }

     

        class TextAndButtonControl : UserControl

        {

            private TextBox textbox1;

            private Button button1;

     

            public TextAndButtonControl()

            {

                this.textbox1 = new TextBox();

                this.Controls.Add(this.textbox1);

     

                this.button1 = new Button();

                this.Controls.Add(this.button1);

     

                this.renderControl();

                this.button1.Click += new EventHandler(button1_Click);

            }

     

            void button1_Click(object sender, EventArgs e)

            {

                MessageBox.Show("Click! The value is:" + this.Text);

            }

     

            public string Text

            {

                get { return this.textbox1.Text; }

                set { this.textbox1.Text = value; }

            }

     

            public string ButtonText

            {

                get { return this.button1.Text; }

                set { this.button1.Text = value; }

            }

     

            public void renderControl()

            {

                this.textbox1.Location = new Point(0, 0);

                this.textbox1.Width = 2 * this.Width / 3;

                this.textbox1.Height = this.Height;

     

                this.button1.Location = new Point(2 * this.Width / 3, 0);

                this.button1.Width = this.Width / 3;

                this.button1.Height = this.Height;

            }

        }

    }

     

    Tuesday, April 3, 2007 3:00 AM

All replies

  • nathan:

    waht function would you like to fullfill ?

    if you can take tow columns,

    you can use DatagridviewTextboxColumn and DataGridviewButtonColumn in same row,

    and if you want  it looks like one column, you can redraw the column head

      But I think it more code if you custom your own datagridview cell you want.
    Monday, April 2, 2007 3:28 AM
  • Implementing a DataGridViewCell that hosts a Panel seems like a good option.. Afterwards you can add whatever you like to the panel Wink
    Monday, April 2, 2007 5:49 AM
  • I have another way to do this, we can take the following steps:

    1). Make a usercontrol to host a textbox and a button, as shown in my following sample, the TextAndButtonControl class which inherited from usercontrol.

    2). Create a TextAndButtonControl instance(NOTICE: we just need one), add it into the control collection of the DataGridView, initially make it invisible.

    3). Handle the DataGridView.CellPainting event to draw a textbox and button style on the cell, which make the cell looked like some kind hosting a usercontrol in it.

    4). Handle the DataGridView.CellBeginEdit event to show the usercontrol right in the cell while editing, you can edit in the textbox in the usercontrol and click the button, write your logic in the Click event of the button, I just show a message box for example in this sample.

    5). Handle the DataGridView.CellEndEdit event to update the cell value.

    6). Handle the DataGridView.Scroll event to reset the location and size of the usercontrol while scrolling. Without handling this, the usercontrol would stay still while scrolling.

    That's all. I write a sample for your information. Enjoy taking it.

    Any question about this please no hesitate to let me know.

    Best Regards.
    Zhixin Ye


    Code Snippet

    using System;

    using System.Collections.Generic;

    using System.ComponentModel;

    using System.Data;

    using System.Drawing;

    using System.Text;

    using System.Windows.Forms;

     

    namespace Sample5

    {

        public partial class DgvTextAndButton : Form

        {

            public DgvTextAndButton()

            {

                InitializeComponent();

            }

     

            private void DgvTextAndButton_Load(object sender, EventArgs e)

            {

                DataTable dt = new DataTable();

                dt.Columns.Add("col1");

                dt.Columns.Add("col2");

                for (int j = 0; j < 20; j++)

                {

                    dt.Rows.Add("col1"+j.ToString(),"col2"+j.ToString());

                }

                this.dataGridView1.DataSource = dt;

                this.dataGridView1.Columns[0].Width = 150;

     

                this.txbtnControl = new TextAndButtonControl();

                this.txbtnControl.Visible = false;

                this.dataGridView1.Controls.Add(this.txbtnControl);

     

                //Handle this event to paint a textbox and button style in the cell,

                //this painting avoid using amount of usercontrols, we just need one

                this.dataGridView1.CellPainting += new DataGridViewCellPaintingEventHandler(dataGridView1_CellPainting);

     

                //Handle the cellbeginEdit event to show the usercontrol in the cell while editing

                this.dataGridView1.CellBeginEdit += new DataGridViewCellCancelEventHandler(dataGridView1_CellBeginEdit);

                //Handle the cellEndEdit event to update the cell value

                this.dataGridView1.CellEndEdit += new DataGridViewCellEventHandler(dataGridView1_CellEndEdit);

     

                //Handle the scroll event to reset the location and size of the usercontrol while scrolling

                this.dataGridView1.Scroll += new ScrollEventHandler(dataGridView1_Scroll);

            }

     

            TextAndButtonControl txbtnControl;

     

            void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)

            {

                if (e.ColumnIndex == 0 && e.RowIndex > -1 && e.RowIndex != this.dataGridView1.NewRowIndex)

                {

                    Rectangle textRect = e.CellBounds;

                    textRect.Width -= e.CellBounds.Width / 3;

                    Rectangle btnRect = e.CellBounds;

                    btnRect.X += textRect.Width;

                    btnRect.Width = e.CellBounds.Width / 3;

                    e.Paint(textRect, DataGridViewPaintParts.All);

                    ControlPaint.DrawButton(e.Graphics, btnRect, ButtonState.Normal);

                    StringFormat formater = new StringFormat();

                    formater.Alignment = StringAlignment.Center;

                    e.Graphics.DrawString("click", e.CellStyle.Font, new SolidBrush(e.CellStyle.ForeColor), btnRect, formater);

                    e.Handled = true;

                }

            }

     

            void dataGridView1_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)

            {

                if (e.ColumnIndex == 0 && e.RowIndex > -1 && e.RowIndex != this.dataGridView1.NewRowIndex)

                {

                    Rectangle rect = this.dataGridView1.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, true);

                    this.txbtnControl.Location = rect.Location;

                    this.txbtnControl.Size = rect.Size;

                    this.txbtnControl.Text = this.dataGridView1.CurrentCell.Value.ToString();

                    this.txbtnControl.ButtonText = "click";

                    this.txbtnControl.renderControl();

                    this.txbtnControl.Visible = true;

                }

            }

     

            void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)

            {

                if (e.ColumnIndex == 0 && e.RowIndex > -1 && e.RowIndex != this.dataGridView1.NewRowIndex)

                {

                    this.dataGridView1.CurrentCell.Value = this.txbtnControl.Text;

                    this.txbtnControl.Visible = false;

                }

            }

     

            void dataGridView1_Scroll(object sender, ScrollEventArgs e)

            {

                if (this.txbtnControl.Visible == true)

                {

                    Rectangle r = this.dataGridView1.GetCellDisplayRectangle(

                        this.dataGridView1.CurrentCell.ColumnIndex,

                        this.dataGridView1.CurrentCell.RowIndex,

                        true);

                    this.txbtnControl.Location = r.Location;

                    this.txbtnControl.Size = r.Size;

                }

            }

        }

     

        class TextAndButtonControl : UserControl

        {

            private TextBox textbox1;

            private Button button1;

     

            public TextAndButtonControl()

            {

                this.textbox1 = new TextBox();

                this.Controls.Add(this.textbox1);

     

                this.button1 = new Button();

                this.Controls.Add(this.button1);

     

                this.renderControl();

                this.button1.Click += new EventHandler(button1_Click);

            }

     

            void button1_Click(object sender, EventArgs e)

            {

                MessageBox.Show("Click! The value is:" + this.Text);

            }

     

            public string Text

            {

                get { return this.textbox1.Text; }

                set { this.textbox1.Text = value; }

            }

     

            public string ButtonText

            {

                get { return this.button1.Text; }

                set { this.button1.Text = value; }

            }

     

            public void renderControl()

            {

                this.textbox1.Location = new Point(0, 0);

                this.textbox1.Width = 2 * this.Width / 3;

                this.textbox1.Height = this.Height;

     

                this.button1.Location = new Point(2 * this.Width / 3, 0);

                this.button1.Width = this.Width / 3;

                this.button1.Height = this.Height;

            }

        }

    }

     

    Tuesday, April 3, 2007 3:00 AM
  • Works this code with the keyboard? The first key that the user hit? If the editmode is with the keyboard, the usercontrol don't catch the focus. Why?

     

    Sunday, May 20, 2007 11:05 PM
  • My experience with this approach is the same--under certain circumstances, the custom control gets displayed correctly but the underlying cell still has the focus.  So keystrokes go to the underlying cell.  Can we prevent the cell from getting focus, or somehow reroute keystrokes to the custom control?

    thanks.
    Tuesday, May 22, 2007 1:48 PM
  • Hi, I tried this example hoping it would save the day...hehe.
    But I am a little confused. When I ran it, the button is shown even though the row is not in focus. Is this correct?

    Screenshot

    Wednesday, October 17, 2007 7:27 AM
  • Have you do some extra work on it? I run the code and no that problem.

    Wednesday, October 17, 2007 7:42 AM
  • Didn't do anything else then add a datagridview to the form, thats it.
    When you run it, there are no gray buttons in the rows that are not in focus?

    Can you make a screenshot?
    Wednesday, October 17, 2007 7:51 AM
  • Contact me with the email in my profile, then I'll send back with a screenshot to you.

    Wednesday, October 17, 2007 7:55 AM
  • Did you get my email?
    Wednesday, October 17, 2007 10:30 AM
  • I've sent it to you twice, haven't you received them?

    Wednesday, October 17, 2007 10:31 AM
  • Ok, now i got it, but it looks like mine. I thought that when not in edit mode the button was hidden. I misunderstood...

    But now that i mentioned it, can that be done. Only show the button while in edit mode?
    Wednesday, October 17, 2007 10:48 AM
  • Of course! Just don't handle the CellPainting event. Comment all those code out.

    Wednesday, October 17, 2007 11:01 AM
  • COOL... thank you. Looks and works great.
    Wednesday, October 17, 2007 11:08 AM
  •  sfrenkiel wrote:
    My experience with this approach is the same--under certain circumstances, the custom control gets displayed correctly but the underlying cell still has the focus.  So keystrokes go to the underlying cell.  Can we prevent the cell from getting focus, or somehow reroute keystrokes to the custom control?

    thanks.


    Ofcourse now I encounter this problem, did you ever have a chance to look at it?
    Wednesday, October 17, 2007 11:38 AM
  •  sfrenkiel wrote:
    My experience with this approach is the same--under certain circumstances, the custom control gets displayed correctly but the underlying cell still has the focus.  So keystrokes go to the underlying cell.  Can we prevent the cell from getting focus, or somehow reroute keystrokes to the custom control?

    thanks.


    A trick to fix this problem is like follows:

     

    Handle the EditingControlShowing event to move focus to the TextAndButtonControl.

     

    .......

     

    void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)

    {

    if (this.dataGridView1.CurrentCell.ColumnIndex == 0)

    {

    this.BeginInvoke(new MethodInvoker(setfocus));

    }

    }

    private void setfocus()

    {

    this.txbtnControl.Focus();

    }

    TextAndButtonControl txbtnControl;

     

    .......

     

     

    Tuesday, November 25, 2008 11:20 AM
  • Hai

    How to redraw  two column to look like a single column in datagridview winforms control


    Regards
    damodar
    Saturday, January 10, 2009 5:31 PM
  • one problem with this approach is that the first keystroke is still on the underline control. any ideas? anyone?

    Sunday, June 28, 2009 2:17 AM
  • Hi timvw

    Do you have the sample code?

    Wednesday, July 8, 2009 4:46 AM
  • Hello Everyone,

    This post has helped me a lot. But I have a problem to ask.

    it is, when the column width is changed by the user, i handle the ColumnWidthChanged event like this.

    If Me.txbtnControl.Visible = True Then

     

    Dim R As Rectangle = Me.DataGridView1.GetCellDisplayRectangle(Me.DataGridView1.CurrentCell.ColumnIndex, Me.DataGridView1.CurrentCell.RowIndex, True)

     

    Me.txbtnControl.Location = R.Location

     

    Me.txbtnControl.Size = R.Size

     

    Me.DataGridView1.PerformLayout()

     

    End If

    The problem is that when i resize the column, when i move the spliter of the column header to resize the column,
    the display on txtbtnControl gets scrambled/distorted by the spliter line. Is there a way to rectify that ?
    I've been experimenting different things but i cant find a way out..


    Thanks in Advance

    Regards - Hemant

    Wednesday, July 8, 2009 6:38 AM
  • the above code works great and very useful, 

    with the help of your code , i show a combobox on cellbeginedit,
     when i scroll through the list items in combobox either by using keyboard or mouse , the cell endedit and scroll to the next row.


    p.s. i have used combo box as a custom control instead of DatagridviewCombo box column because 
    i need to set list item of combo box value depending upon one of the field in the record. 

    in more words , the grid contain a city comboboxcolumn depending upon the city we choose,the items for the postcode combobox (custom control) need to set . everything ok except , we cannot choose the item in the combobox easily by scrolling through the list items.

    hope it make sense .

    waiting for your advice , is there any better way to accomplish 

    annamalai
    Tuesday, July 28, 2009 10:13 AM
  • Hi
    i need something like this. Instead of placing a button i want to display one link button, How can i do this?
    Tuesday, October 27, 2009 6:49 AM
  • Hi Expert,

    Why GetCellDisplayRectangle Dont work in DataGridView.Scroll evnts handlar method...
    i have 2 rows each row on seperate page (scroll page)
    when i scroll from page 2 to page 1 page (Row) GetCellDisplayRectangle returns x=0 , y=0 for my all columns (0,1,2 ...)
    why ? what is work around for this ?
    Need help ....

    Monday, December 21, 2009 6:06 AM
  • This works well, but if you're set to have the cell enter edit mode when a key is pressed, the first key never gets into the textbox. I have the same problem if I try to create my own edit control (described here http://msdn.microsoft.com/en-us/library/7tas5c80.aspx). Any ideas on how to capture that first key and pass it along?
    Wednesday, March 10, 2010 8:09 PM
  • Great! But there is a problem on scrolling. If my txbtnControl is visible and I scroll my grid until CurrentCell is not visible any more, then I scroll to the opposite way, as soon as Current Cell appears, txbtnControl is not painted. This because GetCellDisplayRectangle returns a Rectangle with Heigth = 0 and Width = 0. In this case I found a solution here: http://www.vbforums.com/showthread.php?t=613100 using EndScroll of HScrollBar and VScrollBar. Here's the code I added:

    In DgvTextAndButton_Load:

    HScrollBar hscroll = dataGridView1.Controls.OfType<HScrollBar>().FirstOrDefault();

    VScrollBar vscroll = dataGridView1.Controls.OfType<VScrollBar>().FirstOrDefault();

    if (hscroll != null)

    hscroll.Scroll +=new ScrollEventHandler(OnDataGridViewScroll);

    if (vscroll != null)

    vscroll.Scroll +=new ScrollEventHandler(OnDataGridViewScroll);

    ------------------------------------------------------------------------------------

    The Event Handler:

    void OnDataGridViewScroll(object sender, ScrollEventArgs e)

    {

    if (e.Type == ScrollEventType.EndScroll && bDoEndScroll)

    {

    if (txbtnControl.Visible)

    {

    Rectangle r = dataGridView1.GetCellDisplayRectangle(

    dataGridView1.CurrentCell.ColumnIndex,

    dataGridView1.CurrentCell.RowIndex,

    true);

    txbtnControl.Location = r.Location;

    txbtnControl.Size = r.Size;

    }

    bDoEndScroll = false;

    }

    }

    ----------------------------------------

    In DataGridView1_Scroll:

    if (r.Height == 0 && r.Width == 0 && r.X == 0 && r.Y == 0)

    bDoEndScroll = true; //Class Boolean variable used in OnDataGridViewScroll

     

    Saturday, July 17, 2010 9:39 PM
  • Hello Everyone,

    This post has helped me a lot. But I have a problem to ask.

    it is, when the column width is changed by the user, i handle the ColumnWidthChanged event like this.

    If Me.txbtnControl.Visible = True Then

     

    Dim R As Rectangle = Me.DataGridView1.GetCellDisplayRectangle(Me.DataGridView1.CurrentCell.ColumnIndex, Me.DataGridView1.CurrentCell.RowIndex, True)

     

     

    Me.txbtnControl.Location = R.Location

     

     

    Me.txbtnControl.Size = R.Size

     

    Me.DataGridView1.PerformLayout()

     

     

    End If

    The problem is that when i resize the column, when i move the spliter of the column header to resize the column,
    the display on txtbtnControl gets scrambled/distorted by the spliter line. Is there a way to rectify that ?
    I've been experimenting different things but i cant find a way out..


    Thanks in Advance

    Regards - Hemant

     


    This is what I'm looking for, Thanks for your instruction!
    Wednesday, February 9, 2011 9:41 PM
  • Hi

    Can you please give some code in VB.net for the acheiving the same task (display a button within a cell in datagridview.)

    thanks

     

    Thursday, August 18, 2011 8:36 PM
  • Instead of TextBoxes and Button to edit the TextBox .. I want two DatagridViewLinkColumns ," Edit" and " Delete " links for editing and deleting particular row .. how to achieve this ?? I am editing the row in separate textboxes , checkboxes and not in the GridView itself ...please check  this thread of mine which will elaborate further more about my problem .. I have posted the same question on StackOverFlow but those guys were giving the links to this thread which is in different context

    Avinash


    Wednesday, June 13, 2012 9:04 AM
  • When I press tab, the control has to go to next cell and focus in the textbox in next cell. How is it possible ?

    Any help is greatly appreciated

    Thanks in advance

    Wednesday, August 14, 2013 11:33 PM