Host Controls in Windows Forms DataGridView Cells  
Author Message
Niro





PostPosted: Windows Forms Data Controls and Databinding, Host Controls in Windows Forms DataGridView Cells Top

Hi all,
I'm trying to create a new cell within the new DataGridView (.NET 2 Beta).
For example, I'm trying to create a cell which contain a RichTextBox, I succeed with the appearance of the rich text box controll but there is a lot of irregular behavior.

Does someone did it already(not particular with RichTextBox) and can send an example.
Unfortunately Microsoft example in the link below doesn't accessible.

http://www.hide-link.com/

10X in advance,
Nir Oren



Windows Forms31  
 
 
theBainster





PostPosted: Windows Forms Data Controls and Databinding, Host Controls in Windows Forms DataGridView Cells Top

I have successfully created a Calendar Control (after fixing Microsofts example).  Here's the code.  It should be saved to a file and then added to your project.  You'll then see CalendarControl as an option in the dropdown list of available controls types in the datagridview.

#region Using directives

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

#endregion

 


    public class CalendarColumn : DataGridViewColumn
    {
        public CalendarColumn()
            : base(new CalendarCell())
        {
        }

        public override DataGridViewCell CellTemplate
        {
            get
            {
                return base.CellTemplate;
            }
            set
            {
                // Ensure that the cell used for the template is a CalendarCell.
                if (value != null &&
                    !value.GetType().IsAssignableFrom(typeof(CalendarCell)))
                {
                    throw new InvalidCastException("Must be a CalendarCell");
                }
                base.CellTemplate = value;
            }
        }
    }

    public class CalendarCell : DataGridViewTextBoxCell
    {

        public CalendarCell()
            : base()
        {
            // Use the short date format.
            this.Style.Format = "d";
        }

        public override void InitializeEditingControl(int rowIndex, object
            initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
        {
            // Set the value of the editing control to the current cell value.
            base.InitializeEditingControl(rowIndex, initialFormattedValue,
                dataGridViewCellStyle);
            CalendarEditingControl ctl = DataGridView.EditingControl as CalendarEditingControl;

        }

        public override Type EditType
        {
            get
            {
                // Return the type of the editing contol that CalendarCell uses.
                return typeof(CalendarEditingControl);
            }
        }

        public override Type ValueType
        {
            get
            {
                // Return the type of the value that CalendarCell contains.
                return typeof(DateTime);
            }
        }

        public override object DefaultNewRowValue
        {
            get
            {
                // Use the current date and time as the default value.
                return DateTime.Now;
            }
        }
    }

    class CalendarEditingControl : DateTimePicker, IDataGridViewEditingControl
    {
        DataGridView dataGridView;
        private bool valueChanged = false;
        int rowIndex;

        public CalendarEditingControl()
        {
            this.Format = DateTimePickerFormat.Short;
        }

        // Implements the IDataGridViewEditingControl.EditingControlFormattedValue
        // property.
        public object EditingControlFormattedValue
        {
            get
            {
                return this.Value.ToShortDateString();
            }
            set
            {
                if (value is String)
                {
                    this.Value = DateTime.Parse((string)value);
                }
            }
        }

        // Implements the
        // IDataGridViewEditingControl.GetEditingControlFormattedValue method.
        public object GetEditingControlFormattedValue(
            DataGridViewDataErrorContexts context)
        {
            return EditingControlFormattedValue;
        }

        // Implements the
        // IDataGridViewEditingControl.ApplyCellStyleToEditingControl method.
        public void ApplyCellStyleToEditingControl(
            DataGridViewCellStyle dataGridViewCellStyle)
        {
            this.Font = dataGridViewCellStyle.Font;
            this.CalendarForeColor = dataGridViewCellStyle.ForeColor;
            this.CalendarMonthBackground = dataGridViewCellStyle.BackColor;
        }

        // Implements the IDataGridViewEditingControl.EditingControlRowIndex
        // property.
        public int EditingControlRowIndex
        {
            get
            {
                return rowIndex;
            }
            set
            {
                rowIndex = value;
            }
        }

        // Implements the IDataGridViewEditingControl.EditingControlWantsInputKey
        // method.
        public bool EditingControlWantsInputKey(
            Keys key, bool dataGridViewWantsInputKey)
        {
            // Let the DateTimePicker handle the keys listed.
            switch (key & Keys.KeyCode)
            {
                case Keys.Left:
                case Keys.Up:
                case Keys.Down:
                case Keys.Right:
                case Keys.Home:
                case Keys.End:
                case Keys.PageDown:
                case Keys.PageUp:
                    return true;
                default:
                    return false;
            }
        }

        // Implements the IDataGridViewEditingControl.PrepareEditingControlForEdit
        // method.
        public void PrepareEditingControlForEdit(bool selectAll)
        {
            // No preparation needs to be done.
        }

        // Implements the IDataGridViewEditingControl
        // .RepositionEditingControlOnValueChange property.
        public bool RepositionEditingControlOnValueChange
        {
            get
            {
                return false;
            }
        }

        // Implements the IDataGridViewEditingControl
        // .EditingControlDataGridView property.
        public DataGridView EditingControlDataGridView
        {
            get
            {
                return dataGridView;
            }
            set
            {
                dataGridView = value;
            }
        }

        // Implements the IDataGridViewEditingControl
        // .EditingControlValueChanged property.
        public bool EditingControlValueChanged
        {
            get
            {
                return valueChanged;
            }
            set
            {
                valueChanged = value;
            }
        }

        // Implements the IDataGridViewEditingControl
        // .EditingPanelCursor property.
        public Cursor EditingPanelCursor
        {
            get
            {
                return base.Cursor;
            }
        }

        protected override void OnValueChanged(EventArgs eventargs)
        {
            // Notify the DataGridView that the contents of the cell
            // have changed.
            valueChanged = true;
            this.EditingControlDataGridView.NotifyCurrentCellDirty(true);
            base.OnValueChanged(eventargs);
        }


        public Cursor EditingControlCursor
        {
            get { return base.Cursor; }

        }

 
    }


 
 
Ken Tucker





PostPosted: Windows Forms Data Controls and Databinding, Host Controls in Windows Forms DataGridView Cells Top

You cannot convert a dbnull to a date. Try changing the Initialize Control procedure to this.



Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer, _
ByVal initialFormattedValue As Object, _
ByVal dataGridViewCellStyle As DataGridViewCellStyle)
' Set the value of the editing control to the current cell value.
MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, _
dataGridViewCellStyle)
Dim ctl As CalendarEditingControl = _
CType(DataGridView.EditingControl, CalendarEditingControl)
If Not DBNull.Value.Equals(Me.Value) Then
ctl.Value = CType(Me.Value, DateTime)
End If
End Sub



 
 
Binu Jeesman





PostPosted: Windows Forms Data Controls and Databinding, Host Controls in Windows Forms DataGridView Cells Top

i gotit. nw the problem is that the cell 5 and 6 of my datagridview are calendercolumns. whenever i run the program and exit, the calendercolumns are resetting to postions 0 and 1. i mean the column 0 and column 1 of datagridview are coming as calendercolumn. it is resetting by itself. i didn't add any such codes. even if i am running some other forms of the project it is resetting.

can anybody tell me why



 
 
Karl Erickson





PostPosted: Windows Forms Data Controls and Databinding, Host Controls in Windows Forms DataGridView Cells Top

Just wanted to mention that the code for How to: Host Controls in Windows Forms DataGridView Cells is up to date and includes a line that is missing from the above code. The InitializeEditingControl implementation should be:

    public override void InitializeEditingControl(int rowIndex, object
        initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
    {
        // Set the value of the editing control to the current cell value.
        base.InitializeEditingControl(rowIndex, initialFormattedValue,
            dataGridViewCellStyle);
        CalendarEditingControl ctl =
            DataGridView.EditingControl as CalendarEditingControl;
        ctl.Value = (DateTime)this.Value;
    } 

The last line ensures that the hosted DateTimePicker gets the current cell value when the cell enters edit mode.



 
 
Pasca





PostPosted: Windows Forms Data Controls and Databinding, Host Controls in Windows Forms DataGridView Cells Top

I looked at the code posted by theBanister and it works well. I then changed it from referencing a calendar control to a ComboBox and I had no problem. However, I do want to be able to trap the events of the combobox in a datagrid when a user activates the DropDown and the DropDownClosed events.  For example, if I dragged the ComboBox onto a Form, I can then write specific code for the named control's events. How do I do this when the control is hosted Thanks for helping...anyone Below is the modified code for a hosted combobox.

#region Using directives

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows.Forms;

#endregion

public class ComboBoxColumn : DataGridViewColumn

{

public ComboBoxColumn()

: base(new ComboBoxCell())

{

}

public override DataGridViewCell CellTemplate

{

get

{

return base.CellTemplate;

}

set

{

// Ensure that the cell used for the template is a ComboBoxCell.

if (value != null && !value.GetType().IsAssignableFrom(typeof(ComboBoxCell)))

{

throw new InvalidCastException("Must be a ComboBoxCell");

}

base.CellTemplate = value;

}

}

}

public class ComboBoxCell : DataGridViewTextBoxCell

{

public ComboBoxCell()

: base()

{

// Use the short date format.

// this.Style.Format = "d";

}

public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)

{

// Set the value of the editing control to the current cell value.

base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);

ComboBoxEditingControl ctl = DataGridView.EditingControl as ComboBoxEditingControl;

// ctl.Text = this.Value.ToString();

}

public override Type EditType

{

get

{

// Return the type of the editing contol that ComboBoxCell uses.

return typeof(ComboBoxEditingControl);

}

}

public override Type ValueType

{

get

{

// Return the type of the value that ComboBoxCell contains.

return typeof(string);// DateTime);

}

}

public override object DefaultNewRowValue

{

get

{

// Use the current date and time as the default value.

return ""; // DateTime.Now;

}

}

}

public class ComboBoxEditingControl : ComboBox, IDataGridViewEditingControl

{

protected int rowIndex;

protected DataGridView dataGridView;

protected bool valueChanged = false;

public ComboBoxEditingControl()

{

}

protected override void OnTextChanged(EventArgs e)

{

base.OnTextChanged(e);

// Let the DataGridView know about the value change

NotifyDataGridViewOfValueChange();

}

// Notify DataGridView that the value has changed.

protected virtual void NotifyDataGridViewOfValueChange()

{

this.valueChanged = true;

if (this.dataGridView != null)

{

this.dataGridView.NotifyCurrentCellDirty(true);

}

}

#region IDataGridViewEditingControl Members

// Indicates the cursor that should be shown when the user hovers their

// mouse over this cell when the editing control is shown.

public Cursor EditingPanelCursor

{

get

{

return Cursors.IBeam;

}

}

public Cursor EditingControlCursor

{

get

{

return base.Cursor;

}

}

// Returns or sets the parent DataGridView.

public DataGridView EditingControlDataGridView

{

get

{

return this.dataGridView;

}

set

{

this.dataGridView = value;

}

}

 

// Sets/Gets the formatted value contents of this cell.

public object EditingControlFormattedValue

{

set

{

this.Text = value.ToString();

NotifyDataGridViewOfValueChange();

}

get

{

return this.Text;

}

}

// Get the value of the editing control for formatting.

public object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context)

{

return this.Text;

}

// Process input key and determine if the key should be used for the editing control

// or allowed to be processed by the grid. Handle cursor movement keys for the ComboBox

// control; otherwise if the DataGridView doesn't want the input key then let the editing control handle it.

public bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey)

{

switch (keyData & Keys.KeyCode)

{

case Keys.Right:

//

// If the end of the selection is at the end of the string

// let the DataGridView treat the key message

//

if (!(this.SelectionLength == 0

&& this.SelectionStart == this.ToString().Length))

{

return true;

}

break;

case Keys.Left:

//

// If the end of the selection is at the begining of the

// string or if the entire text is selected send this character

// to the dataGridView; else process the key event.

//

if (!(this.SelectionLength == 0

&& this.SelectionStart == 0))

{

return true;

}

break;

case Keys.Home:

case Keys.End:

if (this.SelectionLength != this.ToString().Length)

{

return true;

}

break;

case Keys.Prior:

case Keys.Next:

if (this.valueChanged)

{

return true;

}

break;

case Keys.Delete:

if (this.SelectionLength > 0 || this.SelectionStart < this.ToString().Length)

{

return true;

}

break;

}

//

// defer to the DataGridView and see if it wants it.

//

return !dataGridViewWantsInputKey;

}

 

// Prepare the editing control for edit.

public void PrepareEditingControlForEdit(bool selectAll)

{

if (selectAll)

{

SelectAll();

}

else

{

//

// Do not select all the text, but position the caret at the

// end of the text.

//

this.SelectionStart = this.ToString().Length;

}

}

// Indicates whether or not the parent DataGridView control should

// reposition the editing control every time value change is indicated.

// There is no need to do this for the ComboBox.

public bool RepositionEditingControlOnValueChange

{

get

{

return false;

}

}

 

// Indicates the row index of this cell. This is often -1 for the

// template cell, but for other cells, might actually have a value

// greater than or equal to zero.

public int EditingControlRowIndex

{

get

{

return this.rowIndex;

}

set

{

this.rowIndex = value;

}

}

 

 

// Make the ComboBox control match the style and colors of

// the host DataGridView control and other editing controls

// before showing the editing control.

public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle)

{

this.Font = dataGridViewCellStyle.Font;

this.ForeColor = dataGridViewCellStyle.ForeColor;

this.BackColor = dataGridViewCellStyle.BackColor;

// this.TextAlign = translateAlignment(dataGridViewCellStyle.Alignment);

}

 

// Gets or sets our flag indicating whether the value has changed.

public bool EditingControlValueChanged

{

get

{

return valueChanged;

}

set

{

this.valueChanged = value;

}

}

#endregion // IDataGridViewEditingControl.

/// Routine to translate between DataGridView

/// content alignments and text box horizontal alignments.

private static HorizontalAlignment translateAlignment(DataGridViewContentAlignment align)

{

switch (align)

{

case DataGridViewContentAlignment.TopLeft:

case DataGridViewContentAlignment.MiddleLeft:

case DataGridViewContentAlignment.BottomLeft:

return HorizontalAlignment.Left;

case DataGridViewContentAlignment.TopCenter:

case DataGridViewContentAlignment.MiddleCenter:

case DataGridViewContentAlignment.BottomCenter:

return HorizontalAlignment.Center;

case DataGridViewContentAlignment.TopRight:

case DataGridViewContentAl

ignment.MiddleRight:

case DataGridViewContentAlignment.BottomRight:

return HorizontalAlignment.Right;

}

throw new ArgumentException("Error: Invalid Content Alignment!");

}

}

 



 
 
Karl Erickson





PostPosted: Windows Forms Data Controls and Databinding, Host Controls in Windows Forms DataGridView Cells Top

There is already a DataGridViewComboBoxColumn; you don't need to create your own to handle the editing-control events. To add handlers to editing-control events, you must do it in an EditingControlShowing event handler:

private void dataGridView1_EditingControlShowing(object sender,
  DataGridViewEditingControlShowingEventArgs e)
{
  ComboBox combo = e.Control as ComboBox;
  if (combo != null)
  {
    // Remove an existing event-handler, if present, to avoid 
    // adding multiple handlers when the editing control is reused.
    combo.SelectedIndexChanged -=
      new EventHandler(ComboBox_SelectedIndexChanged);

    // Add the event handler. 
    combo.SelectedIndexChanged +=
      new EventHandler(ComboBox_SelectedIndexChanged);
  }
}


 
 
SQLirrel





PostPosted: Windows Forms Data Controls and Databinding, Host Controls in Windows Forms DataGridView Cells Top

Re your post adding the line "  ctl.Value = (DateTime)this.Value;"

This causes an invalidcastexception when clicking on a null cell (new record).

A fix might be to test for a null (new record) cell when it is clicked, then  set it to todays date. Sorry I don't know the correct syntax, but along the lines of:

if (this.Value = null)     this.Value = DateTime.Today
 //       ctl.Value = (DateTime)this.Value;

Regards,

Glen


 
 
Boaz Shalev





PostPosted: Windows Forms Data Controls and Databinding, Host Controls in Windows Forms DataGridView Cells Top

Thank,

It's wark fine , I have only one qustion, what is the different betwee the plus && minus signs:

combo.SelectedIndexChanged -=
new EventHandler(ComboBox_SelectedIndexChanged);

// Add the event handler.
combo.SelectedIndexChanged +=
new EventHandler(ComboBox_SelectedIndexChanged);
why you use the minus sign

Thanks Boaz.


 
 
RemcoJVG





PostPosted: Windows Forms Data Controls and Databinding, Host Controls in Windows Forms DataGridView Cells Top

combo.SelectedIndexChanged -=
new EventHandler(ComboBox_SelectedIndexChanged);

adds eventhandler


// Add the event handler.
combo.SelectedIndexChanged +=
new EventHandler(ComboBox_SelectedIndexChanged);

removes eventhandler

in other cases with string or- integer it is just adding and removing for example

mYint += 10 is the same as MyInt = Myint +10

mYint -= 10 is the same as MyInt = Myint -10

Remco


 
 
Timothy P





PostPosted: Windows Forms Data Controls and Databinding, Host Controls in Windows Forms DataGridView Cells Top

How can I add items to the combobox

 
 
Timothy P





PostPosted: Windows Forms Data Controls and Databinding, Host Controls in Windows Forms DataGridView Cells Top

Hi,

I used the code above to embed a control of my own.
It's a control based on a textbox, but which shows a form with a listbox underneath it to help the user input data. The control works on its own and I called it AutoCompleteTextBox (what's in a name). It works by responding to the KeyDown event of the TextBox.

I tried to embed this control into the DataGrid.
It doesn't throw an error and I can type in the cell, but that should
pop-up doesn't show anymore.

What could I have done wrong

(willing to post my code)


 
 
CDFITGUY





PostPosted: Windows Forms Data Controls and Databinding, Host Controls in Windows Forms DataGridView Cells Top

I implemented the Calendar Column code as posted above by theBainster however, when I try to add rows to the DataGridView programmatically the Calendar Columns do not appear.  According to the post Microsoft has on the MSDN web site, the Clone() method needs to be overridden.  Has anyone done that   If so, please post the code in C#.  Thanks in advance.
 
 
Karl Erickson





PostPosted: Windows Forms Data Controls and Databinding, Host Controls in Windows Forms DataGridView Cells Top

Hi Timothy,

If you are still having your issue, the first thing to check is that the KeyDown event is occurring for your editing control. If it is not, then make sure your EditingControlWantsInputKey implementation is returning true for the keys you are interested in. If this does not resolve your issue, then please post a brief repro snippet.



 
 
Karl Erickson





PostPosted: Windows Forms Data Controls and Databinding, Host Controls in Windows Forms DataGridView Cells Top

Hi CDFITGUY,

I don't think the Clone issue is related to this issue. You should be able to add rows programmatically like this:

this.dataGridView1.Rows.Add(new object[] { DateTime.Now });
this.dataGridView1.Rows.Add(new object[] { new DateTime(1999, 12, 31)});

The Clone issue is just that when you override an existing type with a Clone method (typically a cell or column type) and you add new properties, you must be sure to override Clone so that the new properties get copied along with everything else. This is necessary because the DataGridView architecture uses the Clone method in various ways behind the scenes, and any properties you add to a type will not get copied if you do not override Clone. The CalendarColumn type does not add any properties, so you don't need to override Clone. For an example of an overridden Clone method, see the DataGridViewDisableButton type in How to: Disable Buttons in a Button Column in the Windows Forms DataGridView Control.



 
 
CDFITGUY





PostPosted: Windows Forms Data Controls and Databinding, Host Controls in Windows Forms DataGridView Cells Top

Thank you very much Karl for your reply. I understand what you said and it makes perfect sense. At least I can abandon the Clone() issue but I am still not sure how to fix my problem.  My DataGridView has a total of 8 controls on it, including 3 CalendarColumn controls.  If I add a row and for example, I want different dates in each of the 3 columns, how would that look programmatically   Originally I added the CalendarColumns at design time. Interestingly, if I add rows by clicking in the DataGridView the new row has the DateTimePickers present.  I don't  understand how to remind the DataGridView to display a CalendarColumn in, for example, columns 2,5 and 7 and assign it values using one .Add() method call. 

I guess I am expecting to see something like:

this.dataGridView1.Rows.Add(calendarColumn[2] {DateTime.now} , calendarColumn[5] { new DateTime(1999, 12, 31), calendarColumn[7] {<Another Date>});

 I sincerely appologize for the unconvention notation and I know this is not how it goes, but hopefully you can see where I am getting lost.  Since the  .Add() method forgot it has CalendarColumns it iseem to me it will have to be told:  it has them, what Cell[Index] contains them and what values they are to receive.  All in one line of code..

Again thank you for taking the time to help me.  Any light you can shed is greatly appreciated.  

 

P.S. 

Karl, The second solution you posted below worked great!  I can't tell you how much I appreciate you taking the time to help me.  I accidently posted that this did not answer my question and for some reason I am unable to correct the mistake or make a reply to your last post.  I will continue to look for a way to correct the error I made responding to your last post.  Thanks again.

 


 
 
Karl Erickson





PostPosted: Windows Forms Data Controls and Databinding, Host Controls in Windows Forms DataGridView Cells Top

The Rows collection has two Add method overloads that let you populate the values. You can either create a DataGridViewRow, populate it however you want, and pass it to the Add method, or you can pass in an object array where each value in the array corresponds to a column.

If you create a new DataGridViewRow, you can set the values for specific cells like the calendar cells and ignore the rest, which will get their values from their DefaultNewRowValue properties.

DataGridViewRow row = new DataGridViewRow();
row.Cells["DueDate"].Value = new DateTime(2006,6,23);
row.Cells["OrderDate"].Value = new DateTime(2006,6,10);
this.dataGridView1.Rows.Add(row);

If you pass an object array to the Add method instead, you have to provide values in the same order as the columns. You pass an object array in this case because each column might have a different value type.

this.dataGridView1.Rows.Add(new object[] {
new DateTime(2006, 6, 23),
"Urgent",
57.34f,
null,
new DateTime(2006,6,10)
});

If you pass in too many values, the extra values are ignored, and the Add method returns false. If you pass in too few values, the remaining cells will get their DefaultNewRowValue property values. This overload is not very convenient if you want to set default values for later columns but you want earlier columns to keep their DefaultNewRowValue values.

When you pass an object array to the Add method, it creates a new row by cloning the DataGridView.RowTemplate row. If you have set the RowTemplate property, but want to use the Add overload that takes a row, you should create your initial row by cloning the RowTemplate yourself.