|
|
5.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();
|
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)
|
MessageBox.Show(statusDialog.IsCanceled ? "Canceled" : "Success");
|
5.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
|
int numRows = ((DataTable)gridTasks.DataSource).Rows.Count;
|
Graphics g = Graphics.FromHwnd(gridTasks.Handle);
|
StringFormat sf = new StringFormat(StringFormat.GenericTypographic);
|
// 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)
|
// 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
|
// Now we pick that row out of the DataGridRows[] Array
|
// that we have and set it's Height property to what we
|
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..
|
5.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.
|
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...");
|
private void MyMainForm_Load(object sender, System.EventArgs e)
|
Application.AddMessageFilter(this);
|
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...")
|
End Function 'PreFilterMessage
|
Private Sub MyMainForm_Load(sender As Object, e As System.EventArgs)
|
Application.AddMessageFilter(Me)
|
5.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.
|
5.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 |
5.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.
|
5.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)
|
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";
|
ts.GridColumnStyles.Add(cs);
|
//create a column for the sqrt property
|
cs = new DataGridTextBoxColumn();
|
cs.MappingName = "sqrt"; //public property name
|
cs.HeaderText = "Square Root";
|
ts.GridColumnStyles.Add(cs);
|
dataGrid1.TableStyles.Clear();
|
dataGrid1.TableStyles.Add(ts);
|
private void CreateArrayList()
|
arrayList1 = new ArrayList();
|
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
|
public RandomNumber(double d)
|
get {return Math.Sqrt(this.value);}
|
5.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";
|
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)
|
CurrencyManager cm = (CurrencyManager)BindingContext[DataGrid1.DataSource,DataGrid1.DataMember];
|
DataView dv = (DataView) cm.List;
|
5.9 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());
|
5.10 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.
|
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);
|
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)
|
5.11 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());
|
5.12 How do I hide a column?
|
 |
There are several ways to hide a column:
1) You can use your DataSet's ColumnMapping property to 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"];
|
5.13 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.
Method 1
You can do this by deriving from DataGridTextBoxColumn and overriding the Paint method to conditionally set the backColor and foreColor. The sample code below colors any cell that starts with a letter higher than 'F'.
You can download a project (C#, VB) using this class.
|
public class DataGridColoredTextBoxColumn : DataGridTextBoxColumn
|
protected override void Paint(System.Drawing.Graphics g,
|
System.Drawing.Rectangle bounds, System.Windows.Forms.CurrencyManager
|
| |