locked
Displaying a usercontrol in a DataGridView cell RRS feed

  • Question

  • I need to display usercontrols in the cell of the DataGridView. The edit control feature, that displays a single control while the cell is in edit mode, is not sufficient. I need to display multiple controls, of different types (plotters, gauges, etc) in multiple cells.

    I read the warnings about this feature not being supported by DataGridView. Unfortunately, I can't seem to find ANY .Net grid on the market that supports this feature. With trepidation, I tried it anyway.

    I made a custom cell that creates a user control and used the Paint override to move the control into the cell display rectangle. It sort of works, but there are a few boogers:

    1. The grid steals the return key message from my usercontrols. Some of my controls use the return key message to commit data, and it isn't received when hosted by the DataGridView
    2. My Text Edit controls don't seem to draw properly with the DataGridView as a property. For example, the flashing edit cursor doesn't appear.

    These problems seem surmountable, which makes me wonder why you don't support this feature. Do you think I can get this working? If not, can you recommend a third party grid that does support hosting usercontrols in multiple cells?

    Thanks,

    Aaron
    Monday, January 29, 2007 6:09 PM

Answers

  •    I do think it's not need to make a custom DataGridViewColumn, it's a very tough work, you should write lots of codes making a Custom column,Custom Cell, and Custom EditingControl,so much to do. I would prefer the following way show in my sample.

       For handling Enter key event while editing cell, to override the EditingControlWantsInputKey in your custom EditingControl which implements the IDataGridViewEditingControl Interface is a way, but not good if you just want to handle the Enter key, it's much easy to sub-class the DataGridView, and override its ProcessDataGridViewKey and ProcessDialogKey event, put attention to this, you must override both events at the same time, i.e.:

       public class custDGV : DataGridView

        {

            protected override bool ProcessDataGridViewKey(KeyEventArgs e)

            {

                if (e.KeyCode == Keys.Enter)

                    return false;

                return base.ProcessDataGridViewKey(e);

            }

     

            protected override bool ProcessDialogKey(Keys keyData)

            {

                if (keyData == Keys.Enter)

                    return false;

                return base.ProcessDialogKey(keyData);

            }

        }

    thus the controls on your usercontrol which hosted in this inherited DataGridView can receive Enter keys now.

    =====================  Sample  ===========================

    using System;

    using System.Collections.Generic;

    using System.ComponentModel;

    using System.Data;

    using System.Drawing;

    using System.Text;

    using System.Windows.Forms;

     

    using System.Runtime.InteropServices;

    namespace Samples

    {

        public partial class DGVwithUserControl : Form

        {

            public DGVwithUserControl()

            {

                InitializeComponent();

            }

     

            My_Controls.UserControl1 uc;

     

            private void DGVwithUserControl_Load(object sender, EventArgs e)

            {

                DataTable dt = new DataTable("dCust");

                dt.Columns.Add("CustID", typeof(int));

                dt.Columns.Add("Custname");

                dt.Columns.Add("Description");

                dt.Columns.Add("Age",typeof(int));

                for (int i = 0; i < 3; i++)

                {

                    DataRow dr = dt.NewRow();

                    dr["CustID"] = i + 1;

                    dr["Custname"] = "Customer " + i.ToString();

                    dr["Description"] = "Joe|Teacher|3";

                    dr["Age"] = 15 + i * 3;

                    dt.Rows.Add(dr);

                }

                this.dataGridView1.DataSource = dt;

     

                uc = new Samples.My_Controls.UserControl1();

                uc.Visible = false;

              this.dataGridView1.Controls.Add(uc);

            }

     

            private void dataGridView1_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)

            {

                if (this.dataGridView1.Focused && this.dataGridView1.CurrentCell.ColumnIndex == 2)

                {

                    this.dataGridView1.CurrentRow.Height = uc.Height;

                    this.dataGridView1.Columns[e.ColumnIndex].Width = uc.Width;

                   

                    uc.Location = this.dataGridView1.GetCellDisplayRectangle(e.ColumnIndex,e.RowIndex, false).Location;

                  uc.Visible = true;

                    if (this.dataGridView1.CurrentCell.Value != DBNull.Value)

                    {

                        string description = this.dataGridView1.CurrentCell.Value.ToString();

                        string[] des = description.Split('|');

                        uc.UserName = des[0];

                        uc.Job = des[1];

                        uc.LengthOfService = Convert.ToInt32(des[2]);

                    }

                }

            }

     

            private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)

            {

                if (this.dataGridView1.Focused && this.dataGridView1.CurrentCell.ColumnIndex == 2)

                {

                    this.dataGridView1.CurrentCell.Value = uc.UserName

                        + "|" + uc.Job + "|" + uc.LengthOfService.ToString();

                    this.dataGridView1.CurrentRow.Height = this.dataGridView1.RowTemplate.Height;

                    this.dataGridView1.AutoResizeColumns();

                    uc.Visible = false;

                }

            }

        }

     

       

        public class custDGV : DataGridView

        {

            protected override bool ProcessDataGridViewKey(KeyEventArgs e)

            {

                if (e.KeyCode == Keys.Enter)

                    return false;

                return base.ProcessDataGridViewKey(e);

            }

     

            protected override bool ProcessDialogKey(Keys keyData)

            {

                if (keyData == Keys.Enter)

                    return false;

                return base.ProcessDialogKey(keyData);

            }

        }

    }

    ---------- The User Control ---------------

    namespace Samples.My_Controls

    {

        public partial class UserControl1 : UserControl

        {

            public UserControl1()

            {

                InitializeComponent();

            }

     

            private void textBox1_KeyDown(object sender, KeyEventArgs e)

            {

                if (e.KeyCode == Keys.Enter)

                {

                    MessageBox.Show("Enter in textbox!");

                }

            }

     

            public string UserName

            {

                get { return this.textBox1.Text; }

                set { this.textBox1.Text = value; }

            }

     

            public string Job

            {

                get {  return this.comboBox1.Text; }

                set { this.comboBox1.Text = value; }

            }

     

            public int LengthOfService

            {

                get { return (int)this.numericUpDown1.Value; }

                set { this.numericUpDown1.Value = value; }

            }

        }

    }

    Hope it helps. 
    Best Regards.
    Zhixin Ye

    Tuesday, February 13, 2007 5:46 AM

All replies

  • Ack.

    Even if you do get this working, you're going to run into the same problem I ran into - the data grid view is simply not designed to refresh so  many custom controls.  It doesn't even appear to do a good job refreshing the ones that are built in (text box, combo box, etc).

    As for your specific questions..

    1.  For your custom cells, you should override the EditingControlWantsInputKey method.  This is where you can specify which keys your cell should handle.  There's an excellent example (which you've probably already seen, but which I will post again for posterity) of creating a custom control and custom cell and handling keyboard input - the Calendar Column.

    2.  When a cell is not currently being edited, the DataGridView merely renders an IMAGE of the control - not the control itself.  So you won't see blinking unless you implement some complex scheme of your own to animate the displayed image (which, while not impossible, is a little hard - although I've done it myself for small images in a single cell - let me know if you'd like that code).

    This, #2, is the fundamental problem with hosting ANY control in the DataGridView, period - even the build in controls.  When the DataGridView switches between hosting the control and displaying its image, you get a very noticeable flicker.  It is not as visible for the default controls, but if you try to use controls that have much color in them, you'll see it very easily.  Double buffering does not solve this problem - it's an actual time delay between erasing the control and displaying the image.

    I do know of one grid-type control that supports what you're asking, but I wouldn't recommend it.  My experience with it has been less than impressive, although I admit I haven't used the newest version of the control.  The site is GlacialComponents, you'll want the GlacialList.  There is a free demo download you can use to try out the component to see if it will suit your purposes.

    Hopefully he's improved his documentation because the last time I tried using it to embed custom controls, I had a horrible time figuring out how to do it, and I'm a smart guy.
    Tuesday, January 30, 2007 3:52 AM
  •    I do think it's not need to make a custom DataGridViewColumn, it's a very tough work, you should write lots of codes making a Custom column,Custom Cell, and Custom EditingControl,so much to do. I would prefer the following way show in my sample.

       For handling Enter key event while editing cell, to override the EditingControlWantsInputKey in your custom EditingControl which implements the IDataGridViewEditingControl Interface is a way, but not good if you just want to handle the Enter key, it's much easy to sub-class the DataGridView, and override its ProcessDataGridViewKey and ProcessDialogKey event, put attention to this, you must override both events at the same time, i.e.:

       public class custDGV : DataGridView

        {

            protected override bool ProcessDataGridViewKey(KeyEventArgs e)

            {

                if (e.KeyCode == Keys.Enter)

                    return false;

                return base.ProcessDataGridViewKey(e);

            }

     

            protected override bool ProcessDialogKey(Keys keyData)

            {

                if (keyData == Keys.Enter)

                    return false;

                return base.ProcessDialogKey(keyData);

            }

        }

    thus the controls on your usercontrol which hosted in this inherited DataGridView can receive Enter keys now.

    =====================  Sample  ===========================

    using System;

    using System.Collections.Generic;

    using System.ComponentModel;

    using System.Data;

    using System.Drawing;

    using System.Text;

    using System.Windows.Forms;

     

    using System.Runtime.InteropServices;

    namespace Samples

    {

        public partial class DGVwithUserControl : Form

        {

            public DGVwithUserControl()

            {

                InitializeComponent();

            }

     

            My_Controls.UserControl1 uc;

     

            private void DGVwithUserControl_Load(object sender, EventArgs e)

            {

                DataTable dt = new DataTable("dCust");

                dt.Columns.Add("CustID", typeof(int));

                dt.Columns.Add("Custname");

                dt.Columns.Add("Description");

                dt.Columns.Add("Age",typeof(int));

                for (int i = 0; i < 3; i++)

                {

                    DataRow dr = dt.NewRow();

                    dr["CustID"] = i + 1;

                    dr["Custname"] = "Customer " + i.ToString();

                    dr["Description"] = "Joe|Teacher|3";

                    dr["Age"] = 15 + i * 3;

                    dt.Rows.Add(dr);

                }

                this.dataGridView1.DataSource = dt;

     

                uc = new Samples.My_Controls.UserControl1();

                uc.Visible = false;

              this.dataGridView1.Controls.Add(uc);

            }

     

            private void dataGridView1_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)

            {

                if (this.dataGridView1.Focused && this.dataGridView1.CurrentCell.ColumnIndex == 2)

                {

                    this.dataGridView1.CurrentRow.Height = uc.Height;

                    this.dataGridView1.Columns[e.ColumnIndex].Width = uc.Width;

                   

                    uc.Location = this.dataGridView1.GetCellDisplayRectangle(e.ColumnIndex,e.RowIndex, false).Location;

                  uc.Visible = true;

                    if (this.dataGridView1.CurrentCell.Value != DBNull.Value)

                    {

                        string description = this.dataGridView1.CurrentCell.Value.ToString();

                        string[] des = description.Split('|');

                        uc.UserName = des[0];

                        uc.Job = des[1];

                        uc.LengthOfService = Convert.ToInt32(des[2]);

                    }

                }

            }

     

            private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)

            {

                if (this.dataGridView1.Focused && this.dataGridView1.CurrentCell.ColumnIndex == 2)

                {

                    this.dataGridView1.CurrentCell.Value = uc.UserName

                        + "|" + uc.Job + "|" + uc.LengthOfService.ToString();

                    this.dataGridView1.CurrentRow.Height = this.dataGridView1.RowTemplate.Height;

                    this.dataGridView1.AutoResizeColumns();

                    uc.Visible = false;

                }

            }

        }

     

       

        public class custDGV : DataGridView

        {

            protected override bool ProcessDataGridViewKey(KeyEventArgs e)

            {

                if (e.KeyCode == Keys.Enter)

                    return false;

                return base.ProcessDataGridViewKey(e);

            }

     

            protected override bool ProcessDialogKey(Keys keyData)

            {

                if (keyData == Keys.Enter)

                    return false;

                return base.ProcessDialogKey(keyData);

            }

        }

    }

    ---------- The User Control ---------------

    namespace Samples.My_Controls

    {

        public partial class UserControl1 : UserControl

        {

            public UserControl1()

            {

                InitializeComponent();

            }

     

            private void textBox1_KeyDown(object sender, KeyEventArgs e)

            {

                if (e.KeyCode == Keys.Enter)

                {

                    MessageBox.Show("Enter in textbox!");

                }

            }

     

            public string UserName

            {

                get { return this.textBox1.Text; }

                set { this.textBox1.Text = value; }

            }

     

            public string Job

            {

                get {  return this.comboBox1.Text; }

                set { this.comboBox1.Text = value; }

            }

     

            public int LengthOfService

            {

                get { return (int)this.numericUpDown1.Value; }

                set { this.numericUpDown1.Value = value; }

            }

        }

    }

    Hope it helps. 
    Best Regards.
    Zhixin Ye

    Tuesday, February 13, 2007 5:46 AM