Introduction
I was searching long and hard to find a good article on how to get a GridViewCell
in a DataGridView
to display a ListView
panel when editing column, similar to the one found here: http://msdn.microsoft.com/en-us/library/7tas5c80.aspx.
I ended up having to make it myself from scratch. At first I thought it would be a quite straight forward job, but I was proven wrong. Since I normally benefit from what others add to Code Project, it is now my time to give something back.
The sample code is quite simple, but it shows you how to implement a ListView
working similar to the date picker from Microsoft.
Background
The reason for making this was a need for a better representation than what was available in a drop-list. I also want the possibility to edit values while selecting value. For this reason I wanted an advanced controller when editing a cell. In lack of any good samples, I decided to make one.
Using the code
Making the control
As with the DateTimePicker sample from Microsoft, I've made derived classes from DataGridViewColumn
and DataGridViewTextBoxCell
to get my own control when editing the cell.
In addition I've overriden the function PositionEditingControl
in my cell to position the control where I want it.
public override void PositionEditingControl(bool setLocation, bool setSize,
Rectangle cellBounds, Rectangle cellClip, DataGridViewCellStyle cellStyle,
bool singleVerticalBorderAdded, bool singleHorizontalBorderAdded,
bool isFirstDisplayedColumn, bool isFirstDisplayedRow)
{
Size
size = new Size(cellBounds.Width + 100, 100);
Point
location = new Point(cellBounds.Location.X, cellBounds.Location.Y + cellBounds.Height);
if ((location.Y + size.Height) > this.DataGridView.TopLevelControl.ClientSize.Height)
location.Y -= (size.Height + cellBounds.Height);
if ((location.X + size.Width) > this.DataGridView.TopLevelControl.ClientSize.Width)
location.X -= 100;
Rectangle
ctlSize = new Rectangle(location, size);
base.PositionEditingControl(setLocation, setSize, ctlSize, ctlSize, cellStyle,
singleVerticalBorderAdded, singleHorizontalBorderAdded,
isFirstDisplayedColumn, isFirstDisplayedRow);
if (m_control != null)
{
location.Offset(DataGridView.Location);
m_control.Location = location;
}
}
Getting value from control
I've chosen to fetch value from control when control is detached from the cell.
public override void DetachEditingControl()
{
…
CustomViewControl ctl = DataGridView.EditingControl as CustomViewControl;
if (ctl != null && ctl.SelectedItems.Count > 0)
{
this.Value = ctl.SelectedItems[0].SubItems[0].ToString();
ctl.EditingControlFormattedValue = String.Empty;
}
}
Then i listen to the CellEndEdit
event on my DataGridView
and fetch the value from the cell there.
private void m_gridView_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
DataGridView
gridView = sender as DataGridView;
if (gridView != null)
{
DataGridViewCell
cell = gridView.CurrentCell as CustomViewCell;
if (cell != null && cell.Value != null)
{
string sValue = cell.Value as string;
}
}
}
Placing the user defined control outside of DataGridView
To get the ListView placed above the DataGridView
, not being limited by the borders of the DataGridView
, I add the control to the top level control, and remove it when I'm done editing.
protected override void OnCreateControl()
{
base.OnCreateControl();
Form1
topLevel = this.TopLevelControl as Form1;
if (topLevel != null && this.Parent != topLevel)
{
topLevel.Controls.Add(this);
m_dataGridView.Controls.Remove(this);
this.BringToFront();
}
}
}
protected override void OnLeave(EventArgs e)
{
base.OnLeave(e);
if (m_dataGridView.IsCurrentCellInEditMode && !m_boEnded)
m_dataGridView.EndEdit();
Form1
parent = this.Parent as Form1;
if (parent != null)
{
parent.Controls.Remove(this);
m_dataGridView.ClearSelection();
}
}
Issues
In my case I want to use different inherited listviews on different columns. I tried making a column control where I use generics to specify what kind of controller I want to use. To do this I made a control that inherits from Panel
, implements IDataGridViewEditingControl
and adds the desired control to that panel. This looked promising, but there are some issues to it. The sample contains the halfway functioning generics control. If anyone know how to get it working, then please tell me how.
If this is solved, than it will be possible to make any panel the control used when editing a DataGridViewCell
.
While the normal control is created every time a cell is edited and deleted when edit is ended, when adding the control as an on the panel, some of the basic handling from windows fails.
Issues to this solution are:
- Flickering when activating control
- Closing control when moving outside of window