|
|
|
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
|
|