:: Home

  login:         
  passwords:  

Winforms Interview Questions

Windows Forms Deployment
Windows Forms Controls
Windows Forms Data Binding
Windows Forms Datagrid
Windows Forms Docking
Windows Forms Keyboard Handling
Windows Forms Layout
Windows Forms Licensing
Windows Forms Menus
Windows Forms Mouse Handling
Windows Forms from MFC
Windows Forms from VB6
Windows Forms Patterns
Windows Forms Printing
Windows Forms Resources
Windows Form Scrolling
Windows Forms Tips
Windows Forms Common Dialogs
Windows Forms Listbox
Windows Forms ComboBox
Windows Forms Rich TextBox
Windows Forms ListView
Windows Forms TreeView
Windows Forms Button
Windows Forms TabControl
Windows Forms TextBox
Windows Forms MDI
Windows Forms Cursors
Windows Forms WebBrowser
Windows Forms PictureBox
Windows Forms Form
Windows Forms MDI
Windows Forms In IE
GDI+Bitmaps&Images
GDI+Font
GDI+Color
GDI+Brushes
GDI Drawing Tips
GDI+ from GDI
GDI Paths Regions
GDI+Pens
Interioerability WIn32
Tools Metadata Viewers
Design Time Serialization
Design Time Custom Designers
Design Time Tips
Design Time Type Editors
Vs.Net Tips
Vs.Net Debugging
Vs.Net Macros
Framework Tips General
Framework Tips Events
Framework Tips General IO
Framework Tips Strings
Framework Tips Threading
Tool Resource Editor
Design Time UI
Framework Tips CGI
Framework Tips XML

WPF Interview Qs

SilverLight Interview Qs

SAP Interview Questions

Oracle Interview Questions

PHP Interview Questions

Ajax Interview Questions

IIS 7.0

OOP Interview Questions

Ruby Interview Questions

Sql Server Interview Questions

SharePoint 2007 Questions

Microsoft Crm Questions

<

Windows Forms Datagrid

The General Winforms Interview Questions consists the most frequently asked questions in Winforms. This list of 100+ questions guage your familiarity with the Winforms platform. The q&a have been collected over a period of time from various blogs, forums and other similar Winforms sites

4.Windows Forms Datagrid

    4.1 How can I programatically set the rowheight of a row in my DataGrid?
    4.2 How can I autosize the rowheights in my datagrid?
    4.3 How do I prevent sorting a single column in my DataGrid?
    4.4 How do I programmatically select a row in DataGrid?
    4.5 How can I translate a point to a cell in the datagrid?
    4.6 I lost the settings in my DataGrid set during design-time?
    4.7How do I prevent a click into the grid highlighting a cell?
    4.8 How do I prevent the datagrid from displaying its append row (the row at the end with an asterisk)?
    4.9 How can I put a combobox in a column of a datagrid?
    4.10 How can I catch a double-click into a column header cell?
    4.11 How can I select the entire row when the user clicks on a cell in the row?
    4.12 How can I get text from a column header in a MouseUp event?
    4.13 How do I hide a column?
    4.14 How do I color a individual cell depending upon its value or some external method?
    4.15 How can I put a checkbox in a column of my DataGrid?
    4.16 How can I restrict the keystrokes that will be accepted in a column of my datagrid?
    4.17 How do I make a field auto increment as new rows are added to my datagrid?
    4.18 How can I prevent a particular cell from being editable?
    4.19 How do I move columns in a datagrid?
    4.20 How can I do cell by cell validation in a datagrid?
    4.21 How do I programmatically determine the selected rows in a datagrid?
    4.22 How can I move rows by dragging the row header cell?
    4.23 I want to do custom handling of special keys such as the Tab key or F2 in the TextBox of a column in the DataGrid. How do I subclass this TextBox to get at it virtual members?
    4.24 How can I have a column of icons in my datagrid?
    4.25 How can I tell if the current row has changed and whether I am on the AddNew row or not?
    4.26 How do I hide the gridlines or set them to a particular color?
    4.27How can I get the selected text in an active gridcell?
    4.28 How do I determine whether a checkbox in my datagrid is checked or not?
    4.29 How can I bind the datagrid to a datasource without using any wizards?
    4.30 How can I bind two datagrids in a Master-Detail relationship?
    4.31 How do I get the row or column that has been clicked on?
    4.32 How do I add an unbound column to my bound datagrid?
    4.33 How do I get the row and column of the current cell in my datagrid?
    4.34 How can I prevent the Enter key from moving to the next cell when the user is actively editing the cell and presses Enter?
    4.35 How do I set the width of a column in my DataGrid?
    4.36 How can I implement OLE Drag & Drop between a DataGrid and another OLE DnD object that supports the Text format?
    4.37 How can I make my DataGrid support a single select mode, and not the default multiselect mode?
    4.38 How can I get celltips or tooltips to vary from cell to cell in my DataGrid?
    4.39 How can I get notification of the changing of a value in a column of comboboxes within my datagrid?
    4.40 How can I make the datagrid have no currentcell?

4.1 How can I programatically set the rowheight of a row in my DataGrid?

You can do this by starting a new thread and executing Application.Run for the status dialog form when the background thread starts running. To communicate changes in percentage use BeginInvoke to executes a specific delegate asynchronously on the thread that the form was created on.
Download the ProgressThread progressthread.zip sample for a complete implementation of a BackgroundThreadStatusDialog class that shows a status dialog in a separate thread.
In order to use BackgroundThreadStatusDialog from your code you have to update the Progress inside your loop and check the IsCanceled state to detect if the user pressed the Cancel button.

private void button1_Click(object sender, System.EventArgs e)
{
BackgroundThreadStatusDialog statusDialog = new BackgroundThreadStatusDialog();
try
{
for (int n = 0; n < 1000; n++)
{

statusDialog.Percent = n/10;
int ticks = System.Environment.TickCount;
while (System.Environment.TickCount - ticks < 10)
;
if (statusDialog.IsCanceled)
return;
}
statusDialog.Close();
MessageBox.Show(statusDialog.IsCanceled ? "Canceled" : "Success");
}
finally
{
statusDialog.Close();
}
}

4.2 How can I autosize the rowheights in my datagrid?

Here is a solution suggested by Matthew Benedict. It uses reflection to access the protected DataGridRows collection so he can set the row height.

public void AutoSizeGrid()
{
// DataGrid should be bound to a DataTable for this part to
// work.
int numRows = ((DataTable)gridTasks.DataSource).Rows.Count;
Graphics g = Graphics.FromHwnd(gridTasks.Handle);
StringFormat sf = new StringFormat(StringFormat.GenericTypographic);
SizeF size;
// Since DataGridRows[] is not exposed directly by the DataGrid
// we use reflection to hack internally to it.. There is actually
// a method get_DataGridRows that returns the collection of rows
// that is what we are doing here, and casting it to a System.Array
MethodInfo mi = gridTasks.GetType().GetMethod("get_DataGridRows",
BindingFlags.FlattenHierarchy | BindingFlags.IgnoreCase | BindingFlags.Instance
| BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);

System.Array dgra = (System.Array)mi.Invoke(gridTasks,null);

// Convert this to an ArrayList, little bit easier to deal with
// that way, plus we can strip out the newrow row.
ArrayList DataGridRows = new ArrayList();
foreach (object dgrr in dgra)
{
if (dgrr.ToString().EndsWith("DataGridRelationshipRow")==true)
DataGridRows.Add(dgrr);
}
// Now loop through all the rows in the grid
for (int i = 0; i < numRows; ++i)
{
// Here we are telling it that the column width is set to
// 400.. so size will contain the Height it needs to be.
size = g.MeasureString(gridTasks[i,1].ToString(),gridTasks.Font,400,sf);
int h = Convert.ToInt32(size.Height);
// Little extra cellpadding space
h = h + 8;
// Now we pick that row out of the DataGridRows[] Array
// that we have and set it's Height property to what we
// think it should be.
PropertyInfo pi = DataGridRows[i].GetType().GetProperty("Height");
pi.SetValue(DataGridRows[i],h,null);
// I have read here that after you set the Height in this manner that you should
// Call the DataGrid Invalidate() method, but I haven't seen any prob with not calling it..

}
g.Dispose();
}

4.3 How do I prevent sorting a single column in my DataGrid?

You can implement the IMessageFilter interface in your main form. This amounts to adding an override for PreFilterMessage, and looking for the particular message you need to catch. Here are code snippets that catch an escape key on a keydown. You can download a sample project(C#, VB). In the sample, there are two forms, with several controls. You'll notice that no matter what form or control has input focus, the escape key is caught in the PreFilterMessage override.

[C#]
public class MyMainForm : System.Windows.Forms.Form, IMessageFilter
{
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
public bool PreFilterMessage(ref Message m)
{
Keys keyCode = (Keys)(int)m.WParam & Keys.KeyCode;
if(m.Msg == WM_KEYDOWN && keyCode == Keys.Escape)
{
Console.WriteLine("Ignoring Escape...");
return true;
}
return false;
}
....
....
....
private void MyMainForm_Load(object sender, System.EventArgs e)
{
Application.AddMessageFilter(this);
}
}
[VB.NET]
Public Class MyMainForm
Inherits System.Windows.Forms.Form
Implements IMessageFilter
Private WM_KEYDOWN As Integer = &H100
Private WM_KEYUP As Integer = &H101
Public Function PreFilterMessage(ByRef m As Message) As Boolean
Dim keyCode As Keys = CType(CInt(m.WParam), Keys) And Keys.KeyCode
If m.Msg = WM_KEYDOWN And keyCode = Keys.Escape Then
Console.WriteLine("Ignoring Escape...")
Return True
End If
Return False
End Function 'PreFilterMessage
....
....
....
Private Sub MyMainForm_Load(sender As Object, e As System.EventArgs)
Application.AddMessageFilter(Me)
End Sub 'MyMainForm_Load
End Class 'MyMainForm

4.4 How do I programmatically select a row in DataGrid?

We have a small sample that shows how to do this. Download PropTab.zip. After you download and unzip the sample, build the project. Both the control assembly and a small driver assembly get built. After that add the control to your toolbox using 'Customise toolbox...'. Then drag and drop an instance of the added control onto the form in the driver winforms sub-project. When you select this control the properties window should have a tab with a bitmap 'T' as shown below.
These are the steps involved. Before following these steps please download and take a look at the sample. That will greatly help when following these steps.
Control related steps
Assume that you have a control that has two sets of properties, one set that you wish to have displayed in the main property tab and another set that you wish to have displayed in the second tab.
* Mark those properties that you wish to display in the first tab with the BrowsableAttribute to true.
* Mark those properties that you wish to display in the second tab with the BrowsableAttribute set to false. Create another attribute. Name it anything you want and give it a single boolean property. Initialize this property to true.
Other steps
* Derive a class from System.Windows.Forms.PropertyGridInternal.PropertiesTab. You have to override a few methods. The most important of these is GetProperties. We override GetProperties as shown below to use our CustomAttribute as the filtering attribute instead of the BrowsableAttribute that the PropertyGrid uses by default.

public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object component, Attribute[] attrs)
{
return TypeDescriptor.GetProperties(component, new Attribute[]
{new BrowsableAttribute(false), new CustomTabDisplayAttribute(true)});
}
Create a embedded resource bitmap with the same name as the derived tab's type. This bitmap had to be 16x16.

A brief note of explanation on the sample. The sample shows a user control that displays its own tab for some properties. These properties are marked with the filtering attribute, CustomTabDisplayAttribute. The rest of the properties are just displayed in the normal property tab and need no special attention.
To get a deeper understanding of how this works, please refer to these FAQs on TypeConverters and TypeDescriptors.
* TypeDescriptors
* TypeConverters

4.5 How can I translate a point to a cell in the datagrid?

PE refers to portable executable. The file format used for executable programs and for files to be linked together to form executable programs. It includes both metadata information that describes types and members referenced in your program, and the MSIL that is the code base for the program. Source: .NET Framework SDK Documentation


4.6 I lost the settings in my DataGrid set during design-time?

This is possible if you assign a custom DataGridTableStyle to the DataGrid. The properties in the DataGridTableStyle will then replace certain properties in the DataGrid. Take a look at DataGrid class reference for a list of these properties.


4.7 How do I prevent a click into the grid highlighting a cell?

Here is a technique for binding an arraylist of objects where the objects contain public property that can appear as columns in the datagrid. In this example, the object contains 2 public doubles, one named value and the other named sqrt. To bind this arraylist to a datagrid, add a custom tablestyle that has a MappingName of "ArrayList", and then use the property names as the MappingName for each column. Below are some code snippets. You can download a working project that also has code to delete rows and add new rows to the bound arraylist.

private void Form1_Load(object sender, System.EventArgs e)
{
CreateArrayList();
BindArrayListToGrid();
}
private void BindArrayListToGrid()
{
dataGrid1.DataSource = arrayList1;
//create a custom tablestyle and add two columnstyles
DataGridTableStyle ts = new DataGridTableStyle();
ts.MappingName = "ArrayList";
int colwidth = (dataGrid1.ClientSize.Width - ts.RowHeaderWidth - SystemInformation.VerticalScrollBarWidth - 5) / 2;
//create a column for the value property
DataGridTextBoxColumn cs = new DataGridTextBoxColumn();
cs.MappingName = "value"; //public property name
cs.HeaderText = "Random Number";
cs.Format = "f4";
cs.Width = colwidth;
ts.GridColumnStyles.Add(cs);
//create a column for the sqrt property
cs = new DataGridTextBoxColumn();
cs.MappingName = "sqrt"; //public property name
cs.HeaderText = "Square Root";
cs.Format = "f4";
cs.Width = colwidth;
ts.GridColumnStyles.Add(cs);
dataGrid1.TableStyles.Clear();
dataGrid1.TableStyles.Add(ts);
}
private void CreateArrayList()
{
arrayList1 = new ArrayList();

//add some items
Random r = new Random();
for (int i = 0; i < 20; ++i)
arrayList1.Add(new RandomNumber(r.NextDouble()));
}
//create a struct or class that defines what you want in each row
//the different columns in the row must be public properties
public struct RandomNumber
{
private double number;
public RandomNumber(double d)
{
number = d;
}
public double value
{
get{ return number; }
set{ number = value;}
}
public double sqrt
{
get {return Math.Sqrt(this.value);}
}
}

4.8 How do I prevent the datagrid from displaying its append row (the row at the end with an asterisk)?

The DataGrid class does not have a property that controls whether a new row can be added. But the DataView class does have such a property (along with some others such as AllowEdit and AllowDelete). Here is code that will turn off the append row by getting at the dataview associated with the datagrid.

string connString = @"Provider=Microsoft.JET.OLEDB.4.0;data source=C:\northwind.mdb";
string sqlString = "SELECT * FROM customers";
// Connection object
OleDbConnection connection = new OleDbConnection(connString);
// Create data adapter object
OleDbDataAdapter dataAdapter = new OleDbDataAdapter(sqlString, connection);

// Create a dataset object and fill with data using data adapter's Fill method
DataSet dataSet = new DataSet();
dataAdapter.Fill(dataSet, "customers");
// Attach dataset's DefaultView to the datagrid control
dataGrid1.DataSource = dataSet.Tables["customers"];
//no adding of new rows thru dataview...
CurrencyManager cm = (CurrencyManager)this.BindingContext[dataGrid1.DataSource, dataGrid1.DataMember];
((DataView)cm.List).AllowNew = false;
If your datagrid contains links, then Matthew Miller suggest adding Navigate handler such as the one below to disallow the AddNew.
private void DataGrid1_Navigate(object sender, System.Windows.Forms.NavigateEventArgs ne)
{
if(ne.Forward)
{
CurrencyManager cm = (CurrencyManager)BindingContext[DataGrid1.DataSource,DataGrid1.DataMember];
DataView dv = (DataView) cm.List;
dv.AllowNew = false;
}
}

4.9 How can I put a combobox in a column of a datagrid?

The CollectionEditor allows adding and removing items from a collection at design time. If the items in this collection implement IComponent or if they are derived from Component, the items in your collection can be persisted in code.
Download collectioneditorsample.zip for a complete sample project.
Here are some steps you should follow:
1) Make sure your item is derived from Component or implements Icomponent.
For example:

public class SomeItem : Component
{
private string label = "";
public SomeItem()
{
}
public SomeItem(string label)
{
this.label = label;
}
public string Label
{
get
{
return label;

}
set
{
label = value;
}
}
public override string ToString()
{
return String.Format("SomeItem: ( Label = '{0}' )", label);
}
}
2) Next implement your Collection. You have to implement the Ilist interface. The CollectionEditor will determine the type of instances to be added to your collection using reflection inspecting the return type of the Item property (Indexer).
[
Description("The set of properties to be edited with CollectionEditor."),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
Editor(typeof(System.ComponentModel.Design.CollectionEditor),
typeof(System.Drawing.Design.UITypeEditor))

]
public SomeItemCollection SomeItems
{
get
{
if (someItemCollection == null)
{
someItemCollection = CreateItemCollection();
}
return someItemCollection;
}
}
protected SomeItemCollection CreateItemCollection()
{
return new SomeItemCollection(this);
}
public class SomeItemCollection : IList
{
// Fields
private SomeItemDisplayer owner;
public event EventHandler Changed;
// Constructors
public SomeItemCollection(SomeItemDisplayer owner)
{
this.owner = owner;
}
internal ArrayList InnerArray
{
get
{
return owner.someItems;
}
}
public void OnChanged()
{
if (this.Changed != null)
this.Changed(this, EventArgs.Empty);
}

///
/// The CollectionEditor will determine the type of objects to be created by
/// looking at the property type of the following method. CollectionEditor
/// internally uses reflection to get the PropertyInfo for the "Item" property.
/// This method must be public.
/// public SomeItem this[int index]
{
set
{
if (value == null)
throw new ArgumentNullException("value");
if (index < 0 || index > = this.InnerArray.Count)
throw new ArgumentOutOfRangeException(String.Format("Invalid Argument {0}: {1}", "index", index.ToString()));
this.InnerArray[index] = value;
OnChanged();
}
get
{
if (index < 0 || index > = this.InnerArray.Count)
throw new ArgumentOutOfRangeException("Invalid Argument {0}: {1}", "index", index.ToString());
return (SomeItem) this.InnerArray[index];
}
}
public void AddRange(object[] items)
{
InnerArray.AddRange(items);
OnChanged();
}
///
/// This implementation for the Item property for the IList interface. It's
/// property type is object.
///
object IList.this[int index]
{
set
{
// forward call to public indexer
this[index] = (SomeItem) value;
}
get
{
// forward call to public indexer
return this[index];
}
}
public /*IEnumerable*/ IEnumerator GetEnumerator()
{
return InnerArray.GetEnumerator();
}
public /*ICollection*/ int Count
{
get
{
return InnerArray.Count;
}
}
public /*IList*/ void RemoveAt(int index)
{
if (index < 0 || index > = this.InnerArray.Count)
throw new ArgumentOutOfRangeException(String.Format("Invalid Argument {0}: {1}", "index", index.ToString()));
this.InnerArray.RemoveAt(index);
OnChanged();
}
public /*IList*/ void Remove(object value)
{
int n = this.InnerArray.IndexOf(value,0);
if (n != -1)
this.RemoveAt(n);
}
public /*IList*/ void Insert(int index, object item)
{
if (item == null)
throw new ArgumentNullException("item");
if (index < 0 || index > this.InnerArray.Count)
throw new ArgumentOutOfRangeException(String.Format("Invalid Argument {0}: {1}","index", index.ToString()));
this.InnerArray.Insert(index,item);
OnChanged();
}
public /*IList*/ int IndexOf(object value)
{
if (value == null)
throw new ArgumentNullException(String.Format("Invalid Argument {0}: {1}","value", "null"));
return this.InnerArray.IndexOf(value,0);
}
public /*IList*/ bool IsReadOnly
{
get
{
return false;
}
}
public /*IList*/ void Clear()
{
InnerArray.Clear();
this.owner.Invalidate();
}
public /*IList*/ bool Contains(object value)
{
return this.IndexOf(value) != -1;
}
void System.Collections.ICollection.CopyTo(Array dest, int index)
{
int count = this.InnerArray.Count;
for (int n1 = 0; n1 < count; n1++)
dest.SetValue(this.InnerArray[n1], (n1 + index));
}
int System.Collections.IList.Add(object item)
{
int n = this.InnerArray.Add(item);
OnChanged();
return n;
}
bool System.Collections.IList.IsFixedSize
{
get
{
return false;
}
}
bool System.Collections.ICollection.IsSynchronized

{
get
{
return false;
}
}
object System.Collections.ICollection.SyncRoot
{
get
{
return this;
}
}
}

4.10 How can I catch a double-click into a column header cell?

You can use the DataGrid's double-click event and its HitTest method to catch a double click on a header.

private void dataGrid1_DoubleClick(object sender, System.EventArgs e)
{
System.Drawing.Point pt = dataGrid1.PointToClient(Cursor.Position);
DataGrid.HitTestInfo hti = dataGrid1.HitTest(pt);
if(hti.Type == DataGrid.HitTestType.ColumnHeader)
{
MessageBox.Show("double clicked clicked column header " + hti.Column.ToString());
}
}

4.11 How can I select the entire row when the user clicks on a cell in the row?

Call the DataGrid.Select method from within its mouseup event.

[C#]
private void dataGrid1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
System.Drawing.Point pt = new Point(e.X, e.Y);
DataGrid.HitTestInfo hti = dataGrid1.HitTest(pt);
if(hti.Type == DataGrid.HitTestType.Cell)
{
dataGrid1.CurrentCell = new DataGridCell(hti.Row, hti.Column);
dataGrid1.Select(hti.Row);
}
}
[VB/NET]
Private Sub dataGrid1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles dataGrid1.MouseUp
Dim pt = New Point(e.X, e.Y)
Dim hti As DataGrid.HitTestInfo = dataGrid1.HitTest(pt)
If hti.Type = DataGrid.HitTestType.Cell Then
dataGrid1.CurrentCell = New DataGridCell(hti.Row, hti.Column)
dataGrid1.Select(hti.Row)
End If
End Sub

4.12 How can I get text from a column header in a MouseUp event?

private void dataGrid1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
System.Drawing.Point pt = new Point(e.X, e.Y);
DataGrid.HitTestInfo hti = dataGrid1.HitTest(pt);
if(hti.Type == DataGrid.HitTestType.Cell)
{
MessageBox.Show(dataGrid1[hti.Row, hti.Column].ToString());
}
else if(hti.Type == DataGrid.HitTestType.ColumnHeader)
{
MessageBox.Show(((DataView) DataGrid1.DataSource).Table.Columns[hti.Column].ToString());
}
}


4.13 How do I hide a column?

// Creating connection and command sting
string conStr = @"Provider=Microsoft.JET.OLEDB.4.0;data source=C:\northwind.mdb";
string sqlStr = "SELECT * FROM Employees";
// Create connection object
OleDbConnection conn = new OleDbConnection(conStr);
// Create data adapter object
OleDbDataAdapter da = new OleDbDataAdapter(sqlStr,conn);
// Create a dataset object and fill with data using data adapter's Fill method
DataSet ds = new DataSet();
da.Fill(ds, "Employees");
// Hide the column and attach dataset's DefaultView to the datagrid control
ds.Tables["Employees"].Columns["LastName"].ColumnMapping = MappingType.Hidden;
dataGrid1.DataSource = ds.Tables["Employees"];

4.14 How do I color a individual cell depending upon its value or some external method?

We give three different methods for doing this.
# The first one overrides Paint in a derived columnstyle and sets the backcolor there.
# The second uses a delegate to set the color in the Paint override.
# The third method adds an event to the derived column style to allow you to set the color in an event handler.

[C#]
public class DataGridColoredTextBoxColumn : DataGridTextBoxColumn
{
protected override void Paint(System.Drawing.Graphics g,
System.Drawing.Rectangle bounds, System.Windows.Forms.CurrencyManager
source, int rowNum, System.Drawing.Brush backBrush, System.Drawing.Brush
foreBrush, bool alignToRight)
{
// the idea is to conditionally set the foreBrush and/or backbrush
// depending upon some criteria on the cell value
// Here, we color anything that begins with a letter higher than 'F'
try{
object o = this.GetColumnValueAtRow(source, rowNum);
if( o!= null)
{
char c = ((string)o)[0];
if( c > 'F')
{
// could be as simple as
// backBrush = new SolidBrush(Color.Pink);
// or something fancier...
backBrush = new LinearGradientBrush(bounds,
Color.FromArgb(255, 200, 200),
Color.FromArgb(128, 20, 20),
LinearGradientMode.BackwardDiagonal);
foreBrush = new SolidBrush(Color.White);
}
}
}
catch(Exception ex){ /* empty catch */ }
finally{
// make sure the base class gets called to do the drawing with
// the possibly changed brushes
base.Paint(g, bounds, source, rowNum, backBrush, foreBrush, alignToRight);
}
}
}
[VB.NET}
Public Class DataGridColoredTextBoxColumn
Inherits DataGridTextBoxColumn
Public Sub New()
End Sub
Protected Overloads Overrides Sub Paint(ByVal g As Graphics, ByVal bounds As Rectangle, ByVal source As CurrencyManager, ByVal rowNum As Integer, ByVal backBrush As Brush, ByVal foreBrush As Brush, ByVal alignToRight As Boolean)
' the idea is to conditionally set the foreBrush and/or backbrush
' depending upon some crireria on the cell value
' Here, we color anything that begins with a letter higher than 'F'
Try
Dim o As Object
o = Me.GetColumnValueAtRow(source, rowNum)
If (Not (o) Is Nothing) Then
Dim c As Char
c = CType(o, String).Substring(0, 1)
If (c > "F") Then
' could be as simple as
' backBrush = new SolidBrush(Color.Pink);
' or something fancier...
backBrush = New LinearGradientBrush(bounds, Color.FromArgb(255, 200, 200), Color.FromArgb(128, 20, 20), LinearGradientMode.BackwardDiagonal)
foreBrush = New SolidBrush(Color.White)
End If
End If
Catch ex As Exception
' empty catch
Finally
' make sure the base class gets called to do the drawing with
' the possibly changed brushes
MyBase.Paint(g, bounds, source, rowNum, backBrush, foreBrush, alignToRight)
End Try
End Sub
End Class

4.15 How can I put a checkbox in a column of my DataGrid?

You create a custom DataTableStyle that contains column styles for each column you want to display. You add the column styles in the order you want them to appear. Here are the steps to add an string column, an int column and a bool check column to a DataGrid

// code assumes you have a DataSet named myDataSet, a table named "EastCoastSales" and a DataGrid myDataGrid
//STEP 1: Create a DataTable style object and set properties if required.
DataGridTableStyle ts1 = new DataGridTableStyle();
//specify the table from dataset (required step)
ts1.MappingName = "EastCoastSales";
// Set other properties (optional step)
ts1.AlternatingBackColor = Color.LightBlue;
//STEP 2: Create a string column and add it to the tablestyle
DataGridColumnStyle TextCol = new DataGridTextBoxColumn();
TextCol.MappingName = "custName"; //from dataset table
TextCol.HeaderText = "Customer Name";
TextCol.Width = 250;
ts1.GridColumnStyles.Add(TextCol);
//STEP 3: Create an int column style and add it to the tablestyle
//this requires setting the format for the column through its property descriptor
PropertyDescriptorCollection pdc = this.BindingContext
[myDataSet, "EastCoastSales"].GetItemProperties();
//now created a formated column using the pdc
DataGridDigitsTextBoxColumn csIDInt =
new DataGridDigitsTextBoxColumn(pdc["CustID"], "i", true);
csIDInt.MappingName = "CustID";
csIDInt.HeaderText = "CustID";
csIDInt.Width = 100;
ts1.GridColumnStyles.Add(csIDInt);
//STEP 4: Add the checkbox
DataGridColumnStyle boolCol = new DataGridBoolColumn();
boolCol.MappingName = "Current";
boolCol.HeaderText = "Info Current";
//uncomment this line to get a two-state checkbox
//((DataGridBoolColumn)boolCol).AllowNull = false;
boolCol.Width = 150;
ts1.GridColumnStyles.Add(boolCol);
//STEP 5: Add the tablestyle to your datagrid's tablestlye collection
myDataGrid.TableStyles.Add(ts1);

4.16 How can I restrict the keystrokes that will be accepted in a column of my datagrid?

You can create a custom column style and handle the KeyPress event of its TextBox member. Below is the code showing how this might be done. You can also download a sample project (C#, VB) that shows an implementation of this idea.

public class DataGridDigitsTextBoxColumn : DataGridTextBoxColumn
{
public DataGridDigitsTextBoxColumn(System.ComponentModel.PropertyDescriptor pd, string format, bool b)
: base(pd, format, b)
{
this.TextBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(HandleKeyPress);
}
private void HandleKeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
//ignore if not digit or control key
if(!char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar))
e.Handled = true;
//ignore if more than 3 digits
if(this.TextBox.Text.Length > = 3 && !char.IsControl(e.KeyChar))
e.Handled = true;
}
protected override void Dispose(bool disposing)
{
if(disposing)
this.TextBox.KeyPress -= new System.Windows.Forms.KeyPressEventHandler(HandleKeyPress);
base.Dispose(disposing);
}
}

4.17 How do I make a field auto increment as new rows are added to my datagrid?

DataTable myTable = new DataTable("Customers");
...
DataColumn cCustID = new DataColumn("CustID", typeof(int));
cCustID.AutoIncrement = true;
cCustID.AutoIncrementSeed = 1;
cCustID.AutoIncrementStep = 1;
myTable.Columns.Add(cCustID);

4.18 How can I prevent a particular cell from being editable?

You can do this by deriving a custom column style and overriding its virtual Edit member. Below is an override that will prevent the cell in row 1 of the column from getting the edit focus. You can paste this code in the DataGridDigitsTextBoxColumn sample to see it work.
If you want a more flexible solution, you could expose an event as part of your derived columnstyle that fires right before the call to the baseclass in the Edit override. This would allow the handler of the event to set the enable value depending upon the row and column parameters that are passed as part of the event args. You can download a sample (C#, VB) that implements this technique. The sample also fires the event right before painting the cell to decide whether to paint a gray background for the disabled cell. You could modify the eventargs to include a backcolor, and use this event to color cells based on row and column values.

//this override will prevent the cell in row 1 from getting the edit focus
protected override void Edit(System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Rectangle bounds, bool readOnly, string instantText, bool cellIsVisible)
{
if(rowNum == 1)
return;
base.Edit(source, rowNum, bounds, readOnly, instantText, cellIsVisible);
}

4.19 How do I move columns in a datagrid?

[C#]
public void MoveColumn(DataGrid _dataGrid, string _mappingName, int fromCol, int toCol)
{
if(fromCol == toCol) return;
DataGridTableStyle oldTS = _dataGrid.TableStyles[_mappingName];
DataGridTableStyle newTS = new DataGridTableStyle();
newTS.MappingName = _mappingName;
for(int i = 0; i < oldTS.GridColumnStyles.Count; ++i)
{
if(i != fromCol && fromCol < toCol)
newTS.GridColumnStyles.Add(oldTS.GridColumnStyles[i]);
if(i == toCol)
newTS.GridColumnStyles.Add(oldTS.GridColumnStyles[fromCol]);
if(i != fromCol && fromCol > toCol)
newTS.GridColumnStyles.Add(oldTS.GridColumnStyles[i]);
}
_dataGrid.TableStyles.Remove(oldTS);
_dataGrid.TableStyles.Add(newTS);
}
//sample usage
private void button1_Click(object sender, System.EventArgs e)
{
MoveColumn(myDataGrid, "Customers", 3, 1);
}
[VB.NET]
Public Sub MoveColumn(_dataGrid As DataGrid, _mappingName As String, fromCol As Integer, toCol As Integer)
If fromCol = toCol Then
Return
End If
Dim oldTS As DataGridTableStyle = _dataGrid.TableStyles(_mappingName)
Dim newTS As New DataGridTableStyle()
newTS.MappingName = _mappingName
Dim i As Integer
i = 0
While i < oldTS.GridColumnStyles.Count
If i <> fromCol And fromCol < toCol Then
newTS.GridColumnStyles.Add(oldTS.GridColumnStyles(i))
End If
If i = toCol Then
newTS.GridColumnStyles.Add(oldTS.GridColumnStyles(fromCol))
End If
If i <> fromCol And fromCol > toCol Then
newTS.GridColumnStyles.Add(oldTS.GridColumnStyles(i))
End If
i = i + 1
End While
_dataGrid.TableStyles.Remove(oldTS)
_dataGrid.TableStyles.Add(newTS)
End Sub 'MoveColumn
'sample usage
Private Sub button1_Click(sender As Object, e As System.EventArgs)
MoveColumn(myDataGrid, "Customers", 3, 1)
End Sub 'button1_Click

4.20 How can I do cell by cell validation in a datagrid?

There are problems trying to implement cell by cell validation using the grid's Validating event architecture. The problem is that the grid is not the object handling the data. Instead, a TextBox or some other control is the control managing the changing of the cell contents. One way to implement the validation at the grid level is to handle the CurrentCellChanged event, and if the previous cell's value is not proper, then return to that cell. You can download a sample that implements this process. The sample only handles the validation from cell to cell movement. If you want to handle the validation when the user clicks on the forms Close button, then you would have to add a special event handler for this and do one last validation at this point.


4.21 How do I programmatically determine the selected rows in a datagrid?

The method DataGrid.IsSelected can tell you if a particular row is selected. So, you could use IsSelected in a loop through all your rows to finds if multiple rows have been selected. Depending upon the size of your datagrid, this may be a viable solution. If not, you could track the selections yourself by monitoring the key actions and the mouse actions. This would be more work. Thanks to John Hughes to the suggestion to use the dataview.

[C#]
public ArrayList GetSelectedRows(DataGrid dg)
{
ArrayList al = new ArrayList();
CurrencyManager cm = (CurrencyManager)this.BindingContext[dg.DataSource, dg.DataMember];
DataView dv = (DataView)cm.List;
for(int i = 0; i < dv.Count; ++i)
{
if(dg.IsSelected(i))
al.Add(i);
}
return al;
}
private void button1_Click(object sender, System.EventArgs e)
{
string s = "Selected rows:";
foreach(object o in GetSelectedRows(dataGrid1))
{
s+=""+o.ToString();
}
MessageBox.Show(s);
}
[VB.NET]
Public Function GetSelectedRows(ByVal dg As DataGrid) As System.Collections.ArrayList
Dim al As New ArrayList()
Dim cm As CurrencyManager = Me.BindingContext(dg.DataSource, dg.DataMember)
Dim dv As DataView = CType(cm.List, DataView)
Dim i As Integer
For i = 0 to dv.Count - 1
If dg.IsSelected(i) Then
al.Add(i)
End If
End Next
Return al
End Function 'GetSelectedRows
Private Sub button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim s As String = "Selected rows:"
Dim o As Object
For Each o In GetSelectedRows(dataGrid1)
s += " " + o.ToString()
Next o
MessageBox.Show(s)
End Sub 'button1_Click

4.22 How can I move rows by dragging the row header cell?

One way to implement this is to derive a DataGrid and override the virtual OnMouseDown, OnMouseMove and OnMouseUp methods. In your overrides, if the mousedown is on a row header, track the initial mousedown row, and as it moves, draw a line to indicate a target position. Then on the mouseup, handle moving the row.
You can download a sample (C#, VB) that illustrates how this might be done.


4.23 want to do custom handling of special keys such as the Tab key or F2 in the TextBox of a column in the DataGrid. How do I subclass this TextBox to get at it virtual members?

You have to access a property called the Binding Context and then retrieve the BindingContext associated with the dataset and data member that you used for binding. After you have access to this object you just set the position property. You can move backward and forward through the dataset.

form.BindingContext[this.dataSet, "Customers"].Position -= 1;
Remember that when you scroll through the dataset all associated controls will scroll since they all depend on the same context. This is useful if you want to have several controls that display sections of a row operate in tandem.

4.24 How can I have a column of icons in my datagrid?

You need to derive a custom column style, override its Paint method and draw the image. In the attached samples, (VB and C#), there are two custom column styles. One style is a stand-alone unbound column that just displays an image. The second custom column style adds the image to the left side of a bound column. In both cases, the actual image that is displayed is from an imagelist passed into the column in its constructor. The index of the image to be drawn on a particular row is determined by a delegate passed into the column style through its constructor.


4.25 How can I tell if the current row has changed and whether I am on the AddNew row or not?

The DataGrid's CurrentCellChanged event is hit even if you just change cells in the current row. If you want an event that is only hit when you change rows, then you have to look at the binding manager. This object has both a CurrentChanged event and a PositionChanged event which are hit when you change rows.
To decide whether you are on the AddNew row or not, you can again use the binding manager and compare the number of rows it returns with the number of rows in your data table. Below is some code snippets showing how you might get at this information.

private System.Windows.Forms.DataGrid dataGrid1;
private BindingManagerBase bindingManager;
private void Form1_Load(object sender, System.EventArgs e)
{
// Creating connection and command sting
string conStr = @"Provider=Microsoft.JET.OLEDB.4.0;data source=C:\northwind.mdb";
string sqlStr = "SELECT * FROM Employees";
// Create connection object
OleDbConnection conn = new OleDbConnection(conStr);
// Create data adapter object
OleDbDataAdapter da = new OleDbDataAdapter(sqlStr,conn);
// Create a dataset object and fill with data using data adapter's Fill method
DataSet ds = new DataSet();
da.Fill(ds, "Employees");
dataGrid1.DataSource = ds.Tables["Employees"];
bindingManager = this.BindingContext[dataGrid1.DataSource];
bindingManager.PositionChanged += new System.EventHandler(RowChanged);
}
private void RowChanged(object sender, System.EventArgs e)
{
Console.WriteLine("RowChanged " + bindingManager.Position.ToString() );
bool lastRow = bindingManager.Count > ((DataTable)dataGrid1.DataSource).Rows.Count;
if(lastRow)
Console.WriteLine("lastRow");
}

4.26 How do I hide the gridlines or set them to a particular color?

If your datagrid does not have a custom TableStyle associated with it, then there are two DataGrid properties that control the color and visibility of the gridlines.

DataGrid..GridLineColor
DataGrid.GridLineStyle
If you have a custom TableStyle. you need to set the color within the TableStyle. Here is code that makes the line color the same as the background color, and hence hides the gridlines.
private void Form1_Load(object sender, System.EventArgs e)
{
// Set the connection and sql strings
// assumes your mdb file is in your root
string connString = @"Provider=Microsoft.JET.OLEDB.4.0;data source=C:\northwind.mdb";
string sqlString = "SELECT * FROM customers";
OleDbDataAdapter dataAdapter = null;
DataSet _dataSet = null;
try
{
// Connection object
OleDbConnection connection = new OleDbConnection(connString);
// Create data adapter object
dataAdapter = new OleDbDataAdapter(sqlString, connection);
// Create a dataset object and fill with data using data adapter's Fill method
_dataSet = new DataSet();
dataAdapter.Fill(_dataSet, "customers");
connection.Close();
}
catch(Exception ex)
{
MessageBox.Show("Problem with DB access-\n\n connection: "
+ connString + "\r\n\r\n query: " + sqlString
+ "\r\n\r\n\r\n" + ex.ToString());
this.Close();
return;
}
// Create a table style that will hold the new column style
// that we set and also tie it to our customer's table from our DB
DataGridTableStyle tableStyle = new DataGridTableStyle();
tableStyle.MappingName = "customers";
tableStyle.GridLineColor = dataGrid1.BackColor;
dataGrid1.TableStyles.Add(tableStyle);
dataGrid1.DataSource = _dataSet.Tables["customers"];
}

4.27 How can I get the selected text in an active gridcell?

You need to get the DataGridTextBoxColumn.TextBox member, and retrieve the SelectedText from it for the active cell. The sample shows how you can do this as part of handling this TextBox's rightClick. This code assumes you have specifically added DataGridTextBoxColumn for each column style.

private void HandleMouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
if(e.Button == MouseButtons.Right)
{
DataGridTableStyle ts = dataGrid1.TableStyles["customers"];
DataGridTextBoxColumn cs = (DataGridTextBoxColumn)ts.GridColumnStyles[dataGrid1.CurrentCell.ColumnNumber];
MessageBox.Show("Selected: " + cs.TextBox.SelectedText);
}
}

4.28 How do I determine whether a checkbox in my datagrid is checked or not?

If the column is a boolean column, you can just cast the object returned by the indexer to a bool and use it.

f((bool)dataGridTopics[row, column])
MessageBox.Show("I am true");
else
MessageBox.Show("I am false");

4.29 How can I bind the datagrid to a datasource without using any wizards?

Here is a really simple data binding sample. Just drag and drop a datagrid onto a default Windows Forms application. Follow the steps below to bind this grid to the NorthWind db in SQL server.

/ Create a connection
SqlConnection connection = new SqlConnection(this.GetConnectionString());
// Create a data adapter. Think of the data adapter as an object that knows how to get the data from the
// data source into a dataset
SqlDataAdapter dataAdapter = new SqlDataAdapter(this.GetCommandText(), connection);

// fill the dataset using the data adapter
DataSet dataSet = new DataSet("Customers");
dataAdapter.Fill(this.dataSet, "Customers");

// bind to the grid
grid.DataSource = this.dataSet; // the big picture
grid.DataMember = "Customers"; // the specific table that we want to bind to

// The connection text looks like this
// If your SQL server is running on the default port, you can remove the port attribute.
private string GetConnectionString()
{
string server = "your_server_name";
string serverPort = "port_address";
string catalog = "NorthWind";
string password = "user_pass";
string userId = "user_name";
string connectionString = "data source={0},{1};initial catalog={2};" +
"password={3}; user id={4}; packet size=4096";
return string.Format(connectionString,
server, serverPort, catalog, password, userId);
}
// The command text looks like this

private string GetCommandText()
{
string commandText = "Select * from customers";
return commandText;
}

4.30 How can I bind two datagrids in a Master-Detail relationship?

1) Load both Master and Details queries in a dataset.

// I am using the SQL server NorthWind database
this.dataAdapterMaster.Fill(this.dataSet, "Customers");
this.dataAdapterDetails.Fill(this.dataSet, "Orders");
2) Bind the master data grid to the Master dataset table.
// The master view
grid.DataSource = this.dataSet;
grid.DataMember = "Customers";
3) Create a relationship that describes how the two tables relate to each other. A primary key foreign key relationship is defined by two attributes.
The primary key column in the master table
The foreign key column in the details table
The created relationship is added to the dataset
this.dataSet.Relations.Add("CustomersToOrders",
dataSet.Tables["Customers"].Columns["CustomerID"],
dataSet.Tables["Orders"].Columns["CustomerID"]);
4) Set the data member for the details table to be the name of relationship that was added to the dataset.
/ The name of the relation is to be used as the DataMember for the // details view details.DataSource = this.dataSet;
// use the relationship called "CustomersToOrders in the Customers table.
// Remember that we called the relationship "CustomersToOrders".
details.DataMember = "Customers.CustomersToOrders";

4.31 How do I get the row or column that has been clicked on?

You can use the DataGrid's HitTest method, passing it a point in the grid's client coordinate system, and returning a HitTestInfo object that holds all the row and column information that you want.

[C#]
// X & Y are in the grid' coordinates. If they are in screen coordinates, call dataGrid1.PointToClient method
System.Drawing.Point pt = new Point(X, Y);
DataGrid.HitTestInfo hti = dataGrid1.HitTest(pt);
if(hti.Type == DataGrid.HitTestType.Cell)
{
MessageBox.Show(dataGrid1[hti.Row, hti.Column].ToString());
}
else if(hti.Type == DataGrid.HitTestType.ColumnHeader)
{
MessageBox.Show(((DataView) DataGrid1.DataSource).Table.Columns[hti.Column].ToString());
}
[VB.NET]
' X & Y are in the grid' coordinates. If they are in screen coordinates, call dataGrid1.PointToClient method
Dim pt = New Point(X, Y)
Dim hti As DataGrid.HitTestInfo = dataGrid1.HitTest(pt)
If hti.Type = DataGrid.HitTestType.Cell Then
MessageBox.Show(dataGrid1(hti.Row, hti.Column).ToString())
Else If hti.Type = DataGrid.HitTestType.ColumnHeader Then 'assumes datasource is a dataview
MessageBox.Show(CType(DataGrid1.DataSource, DataView).Table.Columns(hti.Column).ToString())
End If
End If

4.32 How do I add an unbound column to my bound datagrid?

The idea is to create the 'bound' table in your dataset, and then add an extra 'unbound' column. The steps are to derive a custom columnstyle that overrides Paint where you calculate and draw the unbound value. You can also override Edit to prevent the user from selecting your unbound column. Then to get your datagrid to use this special column style, you create a tablestyle and add the column styles to it in the order you want the columns to appear in the datagrid. Here are code snippets that derive the column and use the derived column.

// custom column style that is an unbound column
public class DataGridUnboundColumn : DataGridTextBoxColumn
{
protected override void Edit(System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Rectangle bounds, bool readOnly, string instantText, bool cellIsVisible)
{
//do not allow the unbound cell to become active
if(this.MappingName == "UnBound")
return;
base.Edit(source, rowNum, bounds, readOnly, instantText, cellIsVisible);
}
protected override void Paint(System.Drawing.Graphics g, System.Drawing.Rectangle bounds, System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Brush backBrush, System.Drawing.Brush foreBrush, bool alignToRight)
{
//clear the cell
g.FillRectangle(new SolidBrush(Color.White), bounds);
//compute & draw the value
//string s = string.Format("{0} row", rowNum);
// col 0 + 2 chars from col 1
DataGrid parent = this.DataGridTableStyle.DataGrid;
string s = parent[rowNum, 0].ToString() + ((parent[rowNum, 1].ToString())+ " ").Substring(0,2);
Font font = new Font("Arial", 8.25f);
g.DrawString(s, font, new SolidBrush(Color.Black), bounds.X, bounds.Y);
font.Dispose();
}
}
//code that uses this unbound column
private void Form1_Load(object sender, System.EventArgs e)
{
// Set the connection and sql strings
// assumes your mdb file is in your root
string connString = @"Provider=Microsoft.JET.OLEDB.4.0;data source=C:\northwind.mdb";
string sqlString = "SELECT * FROM customers";
OleDbDataAdapter dataAdapter = null;
DataSet _dataSet = null;
try
{
// Connection object
OleDbConnection connection = new OleDbConnection(connString);
// Create data adapter object
dataAdapter = new OleDbDataAdapter(sqlString, connection);
// Create a dataset object and fill with data using data adapter's Fill method
_dataSet = new DataSet();
dataAdapter.Fill(_dataSet, "customers");
connection.Close();
}
catch(Exception ex)
{
MessageBox.Show("Problem with DB access-\n\n connection: "
+ connString + "\r\n\r\n query: " + sqlString
+ "\r\n\r\n\r\n" + ex.ToString());
this.Close();
return;
}
// Create a table style that will hold the new column style
// that we set and also tie it to our customer's table from our DB
DataGridTableStyle tableStyle = new DataGridTableStyle();
tableStyle.MappingName = "customers";
// since the dataset has things like field name and number of columns,
// we will use those to create new columnstyles for the columns in our DB table
int numCols = _dataSet.Tables["customers"].Columns.Count;
//add an extra column at the end of our customers table
_dataSet.Tables["customers"].Columns.Add("Unbound");
DataGridTextBoxColumn aColumnTextColumn ;
for(int i = 0; i < numCols; ++i)
{
aColumnTextColumn = new DataGridTextBoxColumn();
aColumnTextColumn.HeaderText = _dataSet.Tables["customers"].Columns[i].ColumnName;
aColumnTextColumn.MappingName = _dataSet.Tables["customers"].Columns[i].ColumnName;
tableStyle.GridColumnStyles.Add(aColumnTextColumn);
//display the extra column after column 1.
if( i == 1)
{
DataGridUnboundColumn unboundColStyle = new DataGridUnboundColumn();
unboundColStyle.HeaderText = "UnBound";
unboundColStyle.MappingName = "UnBound";
tableStyle.GridColumnStyles.Add(unboundColStyle);
} }
// make the dataGrid use our new tablestyle and bind it to our table
dataGrid1.TableStyles.Clear();
dataGrid1.TableStyles.Add(tableStyle);
dataGrid1.DataSource = _dataSet.Tables["customers"];
}
}

4.33 You can use the CurrentCell property of the DataGrid.

private void button1_Click(object sender, System.EventArgs e)
{
intcolNum = dataGrid1.CurrentCell.ColumnNumber;
int rowNum = dataGrid1.CurrentCell.RowNumber;
object cellValue = dataGrid1[rowNum, colNum];
string s = string.Format("row={0} col={1} value={2}", rowNum, colNum, cellValue);
MessageBox.Show(s);
}


4.34 How can I prevent the Enter key from moving to the next cell when the user is actively editing the cell and presses Enter?

Override the method ProcessKeyPreview in your DataGrid.

protected override bool ProcessKeyPreview(ref System.Windows.Forms.Message m)
{
Keys keyCode = (Keys)(int)m.WParam & Keys.KeyCode;
if((m.Msg == WM_KEYDOWN || m.Msg == WM_KEYUP)
&& keyCode == Keys.Enter )
return false;
return true;
}

4.35 To set a column width, your datagrid must be using a non-null DataGridTableStyle. Once this is in place, you can set the column width by first getting the tablestyle and then using that object to obtain a column style with which you can set the width. Here are some code snippets showing how you might do this.

//.... make sure your DataGrid is using a tablestyle
dataGrid1.DataSource = _dataSet.Tables["customers"];
DataGridTableStyle dgts = new DataGridTableStyle();
dgts.MappingName = "customers";
dataGrid1.TableStyles.Add(dgts);
//......
//method to set a column with by colnumber
public void SetColWidth(DataGridTableStyle tableStyle, int colNum, int width)
{
try
{
tableStyle.GridColumnStyles[colNum].Width = width;
tableStyle.DataGrid.Refresh();
}
catch{} //empty catch .. do nothing
}
//....
// here is how you might call this method
private void button1_Click(object sender, System.EventArgs e)
{
DataGridTableStyle tableStyle = dataGrid1.TableStyles["customers"];
SetColWidth(tableStyle, 1, 200);
}

4.36 How can I implement OLE Drag & Drop between a DataGrid and another OLE DnD object that supports the Text format?

The attached samples (C#, VB) have a derived datagrid that supports OLE D&D with any OLE D&D provider that handles a Text formatted data object. The derived grid handles six events to allow it to be both a drop source and a drop target. The sample project has two datagrids where you can drag cell text back and forth. You can also open Excel, and drag text between Excel and either datagrid.
Here are the events that are handled in this sample.
# MouseDown - Used to save the row and column of a mousedown, 'mousedowncell'.
# MouseMove - Checks to see if you drag off the mousedowncell, and if so, starts a the DoDragDrop.
# MouseUp - Used to reset the mousedowncell.
# DragEnter - Checks to see if the data object has text, and if so, allows a Copy operation. (This could be changed to support Move/Copy.)
# DragOver - Used to set a NoDrop cursor if you move over the mousedowncell (if mousedowncell has been set).
# DragDrop - Used to drop the text into the datagrid.


4.37 How can I make my DataGrid support a single select mode, and not the default multiselect mode?

One way to do this is to derive a DataGrid, override its OnMouseDown and OnMouseMove methods. In the OnMouseDown, handle selecting and unselecting in your code without calling the base class if the click is on the header. In the OnMouseMove, don't call the baseclass to avoid dragging selections. Below is a code snippet for a sample derived DataGrid

public class MyDataGrid : DataGrid
{
private int oldSelectedRow = -1;
protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e)
{
//don't call the base class if left mouse down
if(e.Button != MouseButtons.Left)
base.OnMouseMove(e);
}
protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
{
//don't call the base class if in header
DataGrid.HitTestInfo hti = this.HitTest(new Point(e.X, e.Y));
if(hti.Type == DataGrid.HitTestType.Cell)
{
if(oldSelectedRow > -1)
this.UnSelect(oldSelectedRow);
oldSelectedRow = -1;
base.OnMouseDown(e);
}
else if(hti.Type == DataGrid.HitTestType.RowHeader)
{
if(oldSelectedRow > -1)
this.UnSelect(oldSelectedRow);
if((Control.ModifierKeys & Keys.Shift) == 0)
base.OnMouseDown(e);
else
this.CurrentCell = new DataGridCell(hti.Row, hti.Column);
this.Select(hti.Row);
oldSelectedRow = hti.Row;
}
}
}

4.38 How can I get celltips or tooltips to vary from cell to cell in my DataGrid?

Felix Wu gives this solution in the microsoft.public.dotnet.frameworks.windowsforms newgroup.
You can download a VB.NET sample.
Override the OnPaint event of the TextBox. For example:

protected override void OnPaint(PaintEventArgs e)
{
SolidBrush drawBrush = new SolidBrush(ForeColor); //Use the ForeColor property
// Draw string to screen.
e.Graphics.DrawString(Text, Font, drawBrush, 0f,0f); //Use the Font property
}
 
public MyTextBox()
{
// This call is required by the Windows.Forms Form Designer.
this.SetStyle(ControlStyles.UserPaint,true);
InitializeComponent();
// TODO: Add any initialization after the InitForm call
}

4.39 How can I get notification of the changing of a value in a column of comboboxes within my datagrid?

This solution is based off the combobox for datagrid columns found in this FAQ. That solution replaces the standard textbox with a combobox. To get notifications of the changes, a delegate is passed into the constructor for the custom column style. This delegate is called anytime the combobox value changes. It passes the row number and value as arguments. You can download sample code (C#, VB) that shows the implementation.


4.40 How can I make the datagrid have no currentcell?

There appears to be no method to turn off a currentcell. When a cell is being edited, it is the TextBox embedded in the columnstyle that has the focus, and is displaying the highlighted text. You will notice in this situation, if you click the grid's title bar above the column headers, this TextEdit control loses focus, and the datagrid appears to have no current cell.
We can simulate this click from code, and use it to expose a method in our datagrid to SetNoCurrentCell. Below is some code to illustrate this idea.

public class MyDataGrid : DataGrid
{
public const int WM_LBUTTONDOWN = 513; // 0x0201
public const int WM_LBUTTONUP = 514; // 0x0202
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern bool SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, Int32
lParam);
public void SetNoCurrentCell()
{
//click on top left corner of the grid
SendMessage( this.Handle, WM_LBUTTONDOWN, 0, 0);
SendMessage( this.Handle, WM_LBUTTONUP, 0, 0);
}
}
Here is some VB code.
Public Class MyDataGrid
Inherits DataGrid
Public WM_LBUTTONDOWN As Integer = 513
Public WM_LBUTTONUP As Integer = 514
Shared _
Function SendMessage(hWnd As IntPtr, msg As Int32, wParam As Int32, lParam As Int32) As Boolean

Public Sub SetNoCurrentCell()
'click on top left corner of the grid
SendMessage(Me.Handle, WM_LBUTTONDOWN, 0, 0)
SendMessage(Me.Handle, WM_LBUTTONUP, 0, 0)
End Sub 'SetNoCurrentCell
End Class 'MyDataGrid

Copyright 2007, Megasolutions Ltd