Live Chat Icon For mobile
Live Chat Icon

WinForms FAQ

Find answers for the most frequently asked questions
Expand All Collapse All

You can dynamically remove and inseret TabPages into the TabControl.TabPages collection to hide and show tabs at runtime.

        TabPage tabPageSave = null;
        private void button1_Click(object sender, EventArgs e)
        {
            //hide a tab by removing it from the TabPages collection
            this.tabPageSave = tabControl1.SelectedTab;
            this.tabControl1.TabPages.Remove(this.tabPageSave);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            //show a tab by adding it to the TabPages collection
            if (this.tabPageSave != null)
            {
                int loc = tabControl1.SelectedIndex;
                this.tabControl1.TabPages.Insert(loc, this.tabPageSave);
            }
        }
Permalink

The default behavior is to make the client container use the Control color from the Control panel. You can change this behavior by making the
MDI Client container use the form’s BackColor and Image. To do this, after the call to InitializeComponents(), add the code below. You can
also download a working MDI Client project that has this code in it.

//set back color
foreach(Control c in this.Controls)
{
  if(c is MdiClient)
  {
    c.BackColor = this.BackColor;
    c.BackgroundImage = this.BackgroundImage;
  }
}
Permalink

One solution is to use a panel that has a picturebox placed on it with DockStyle.Fill. This will make the picturebox assume the size of the panel. In addition, set the DockPadding.All property to the width of the desired border. Then in the Panel’s OnPaint method, call the baseclass and then paint the desired borders.

Here are both VB and C# projects that illustrate how you might go about this. The derived PicturePanel class has properties that allow you to set the bordersize and color as well as the image that is to be displayed. This sample retrieves the image from an embedded resource. It also uses double buffering to minimize flashing as you resize the control.

Permalink

If you add a DataGridTableStyle to your Datagrid, then you can use the ColWidth property of the GridColumnStyles to set the width of each column. To dynamically set these widths as the grid is resized, you can handle the SizeChanged event of the the DataGrid. In your handler, you can compute the width of each column by dividing the client width minus the width of the row header column by the number of columns. Now there are a couple of technical points. You have to adjust for a possible vertical scrollbar. And, you have to adjust things for possible integer rounding in the calculations. To handle this last problem, the attached samples (both VB and C#) apply the single computed width to all but the last column. And this last column is just given all the space left. This means the last column may differ in width from the other columns by a couple of pixels.

Permalink

You could do so as shown in the code below :

[C#]
  Form form1 = new Form();
  Bitmap bmp = imageList1.Images[index] as Bitmap;
  form1.Icon = Icon.FromHandle(bmp.GetHicon());

[VB.NET]
  Dim form1 As Form =  New Form()  
  Dim bmp As Bitmap =  imageList1.Images(index) as Bitmap  
  form1.Icon = Icon.FromHandle(bmp.GetHicon())

Please refer to the sample attached here that illustrates this.

Permalink

One way you can do this is to derive the DataGrid and override its WndProc method to handle the WM_SETCURSOR method yourself. In your override, you can do hit testing to decide when you want to set the cursor, or when you want to call the base class and let it set the cursor. Here are sample projects (VB and C#) showing the technique.

Public Class MyDataGrid
  Inherits DataGrid
  Private Const WM_SETCURSOR As Integer = 32

  Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    If m.Msg <> WM_SETCURSOR Then
      MyBase.WndProc(m)
    Else
      ’see if you want the cursor - in col 1, rows 2, 3, 4
      Dim pt As Point = Me.PointToClient(Control.MousePosition)
      Dim hti As DataGrid.HitTestInfo = Me.HitTest(pt.X, pt.Y)
      If hti.Column = 1 AndAlso hti.Row > 1 AndAlso hti.Row < 5 Then
          Cursor.Current = Cursors.Hand
        ’if not, call the baseclass
       Else
        MyBase.WndProc(m)
      End If
    End If
  End Sub ’WndProc

  Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
    If Cursor.Current.Equals(Cursors.Hand) Then
      MessageBox.Show(''My MouseDown'')
    Else
      MyBase.OnClick(e)
    End If
  End Sub ’OnMouseDown
End Class ’MyDataGrid
Permalink

Say you have a Parent table related to a Child table related to a GrandChildTable, and you want to bind a TextBox to a column in the GrandChild table.

To do this you have to have nested relations defined, and you use this nesting to create the DataMember parameter for the the DataBinding that you add to the TextBox. Below are some code snippets. They show the DataMember as ”ParentToChild.ChildToGrandChild.Name” which creates a path from the parent table down to the field in the nested relation by stringing the relation names together separated with a period. You can also download both C# and VB projects.


       Dim dSet As New DataSet()

        ’get the tables
        Dim parentTable As DataTable = GetParentTable()
        Dim childTable As DataTable = GetChildTable()
        Dim grandChildTable As DataTable = GetGrandChildTable()
        dSet.Tables.AddRange(New DataTable() {parentTable, childTable, grandChildTable})

        ’setup the relations
        Dim parentColumn As DataColumn = parentTable.Columns(''parentID'')
        Dim childColumn As DataColumn = childTable.Columns(''ParentID'')
        dSet.Relations.Add(''ParentToChild'', parentColumn, childColumn)

        parentColumn = childTable.Columns(''childID'')
        childColumn = grandChildTable.Columns(''ChildID'')
        dSet.Relations.Add(''ChildToGrandChild'', parentColumn, childColumn)

        Me.TextBox1.DataBindings.Add(''Text'', parentTable, ''ParentToChild.ChildToGrandChild.Name'')
Permalink

Here is a sample (both VB and C#) that illustrates how to have a parent table which has a related child table, which also has a related grandchild table.

Below are some code snippets. The trick is to always make the main parent table be the DataSource for all the grid, and then set the DataMember to be the relation name where teh relation starts at the parent table. This means the DisplayMember for the Child table is ”ParentToChild”, the name of that relation. And, the DisplayMember for the grandchild grid is ”ParentToChild.ChildToGrandChild” which defines the relation starting at the parent grid through the child grid.

Dim dSet As New DataSet()

        ’get the tables
        Dim parentTable As DataTable = GetParentTable()
        Dim childTable As DataTable = GetChildTable()
        Dim grandChildTable As DataTable = GetGrandChildTable()
        dSet.Tables.AddRange(New DataTable() {parentTable, childTable, grandChildTable})

        ’setup the relations
        Dim parentColumn As DataColumn = parentTable.Columns(''parentID'')
        Dim childColumn As DataColumn = childTable.Columns(''ParentID'')
        dSet.Relations.Add(''ParentToChild'', parentColumn, childColumn)

        parentColumn = childTable.Columns(''childID'')
        childColumn = grandChildTable.Columns(''ChildID'')
        dSet.Relations.Add(''ChildToGrandChild'', parentColumn, childColumn)

        ’set the grids
        Me.dataGrid1.DataSource = parentTable

        Me.dataGrid2.DataSource = parentTable 
        Me.dataGrid2.DataMember = ''ParentToChild''

        Me.dataGrid3.DataSource = parentTable 
        Me.dataGrid3.DataMember = ''ParentToChild.ChildToGrandChild''

        Me.dataGrid1.AllowNavigation = False
        Me.dataGrid2.AllowNavigation = False
        Me.dataGrid3.AllowNavigation = False
Permalink

There is no direct support in the framework to do this. You have to use the GetShortPathName function using PInvoke.

This is how the signature for this function looks like:


[DllImport(''kernel32.dll'', SetLastError=true, CharSet=CharSet.Auto)]
    public  static extern int  GetShortPathName(string  longPath, [MarshalAs(UnmanagedType.LPTStr)]StringBuilder  ShortPath, [MarshalAs(UnmanagedType.U4)]int  bufferSize);

Download C# Sample GetShortPathName.zip
Download VB.NET sample GetShortPathName_VB.zip

Permalink

The Windows Forms DataGrid does not support the selection of a range of cells other than a group of rows. To select a row, you can click on its row header. But if you want to select a rectangular group of cells, you cannot.

To add this support, you can catch the mousedown on a cell, and in mousemove, track whether the mouse is dragged with the button down to other cells. If so, draw a selection rectangle over this group of cells. The attached samples (C#, VB) show how this might be done. The drawing of the selection rectangle is done in an OnPaint override. The samples also try to handle autoscrolling as you hit a grid border so the selection process can continue for cells that are not currently visible. To make the selection rectangle look nicer, the code also hides the current cell when it draws a selection rectangle.

The samples have a derived DataGrid that has a public member, SelectedRange, that holds the TopLeft and BottomRight coordinates of the selected range. The derived grid also has a public event, SelectionChanging, that fires prior to the selection changing as you drag your mouse. The event passes both the old selection and the new selection, and allows you to cancel the action if you choose.

Permalink

Yes, you can use the FormatMessage Win32 API. Sample projects for C# and VB.NET are enclosed. This is how the declaration looks like:


    [DllImport(''Kernel32.dll'')]
    public static extern int FormatMessage(int flags, IntPtr source, int messageId, int languageId, StringBuilder
      buffer, int size, IntPtr arguments );

Called like so:


// You can call FormatMessage to get a descriptive error message
        StringBuilder sbFormatMessage = new StringBuilder(1024);
        retVal = Interop.FormatMessage(Interop.FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero, Marshal.GetLastWin32Error(), 0, sbFormatMessage, 
          sbFormatMessage.Capacity, IntPtr.Zero);

Download C# sample, formatmessage.zip
Download VB.NET sample, formatmessage_VB.zip

Permalink

Here is a VB and C# sample showing how you might do this.

The idea is to add textboxes and labels to a panel with whatever layout you want to use.

Then use DataBindings.Add calls to bind the textboxes to columns in your grid (actually in your grid’s datasource). Then, since the grid and the textboxes share the same BindingContext, you can move the BindingManagerBase.Position property to scroll through the rows in your grid, and the textboxes will remain in sync. You can hide the grid just to show the textboxes if you do not want the grid to have a presence. Also, any edits in the textboxes will appear in the DataGrid and vice versa.

Permalink

You can derive a custom columnstyle and override its Paint method to draw the image. Here are both C# and VB.Net samples.

The Paint override offers three ways to draw the bitmap in the cell; size it to fit the cell bounds, size it proportionally to fit the cell bounds, and draw it with it’s original size (clipping the bitmap to fit in the cell bounds). In the sample, the DataGrid is anchored on all four sides, so as you size the form, the DataGrid sizes as well. The dataGrid1_Resized event is handled to dynamically change the PreferredRowHeight and PreferredColumnWidth so the cells occupy all of the datagrid’s client area. So, as you size the form, the grid cells size also so you can easily see the bitmaps.

Permalink

You can use the CurrentCellChanged event to detect when the currentcell changes position. But this event will not allow you to catch the start of a cell being edited.

One way you can do this is to catch the TextChanged event in the embedded TextBox within the cell. If the text changes, then it might be the beginning of a cell edit provided the text in the TextBox differs from the stored value from the DataSource. The reason you need to check for a different value between the grid DataSource and the TextBox contents is that the TextChanged event is fired initially when the TextBox is initialized when the cell becomes current and moves the value from the grid ataSource to the TextBox. You also have to ignore subsequent hits of TextChanged as the same cell continues to be edited. Here is both a VB and C# sample that implements this strategy to flagged current cell start editing.

Permalink

To use custom columnstyles in the designer, you need to do three things.

1) Derive a CustomColumnStyle to implement the functionality you want.
2) Derive a DataGridTableStyle class and add a new GridColumnStyles property that uses this derived CollectionEditor. This GridColumnStyle hides the baseclass member.
3) Derive a DataGrid and add a new TableStyles collection that uses your derived tablestyle.

Both steps 2 and 3 will require you to derive a CollectionEditor and override CreateNewItemTypes to use the derived classes from each step in the designer.

Here is a sample project showing how you might do these things.

Permalink

You can control the columns displayed in the DataGrid through the DataGrid.TableStyle[0].GridColumnStyles collection. To do so, create a DataGridTableStyle and set its MappingName property to point to the name of your DataTable which is the DataSource for the DataGrid. Next, add this DataGridTableStyle to the DataGrid.TableStyles property. Finally, set the DataGrid.DataSource property to the DataTable. Doing things in this order, guarantees that the DataGridTableStyle.GridColumnStyles collection will be fully populated showing all the DataTable columns in the the DataGrid.

Then to add and remove columns from the DataGrid, you only have to add and remove DataGridColumnStyle objects from this DataGrid.TableStyle[0].GridColumnStyles collection. Removing them is straight-forward through a Remove method call. But inserting them requires more work as there is no InsertAt method defined for this collection. To handle this problem, you can create a new array of DataGridColumnStyles, and populate this array in the necessary order to reflect the DataGrid with an inserted column. Then you can clear the old collection, and create a new collection with this new array. You really are not creating all new DataGridColumnStyle objects, but are simply reordering the existing ones in a new collection.

Here is a sample project containing both C# and VB.NET code showing how you might do this.

Permalink

GDI+ has direct support for reading and outputting animated images.

To get at the individual frames, you can use the image’s FrameDimensionList, and then call SelectActiveFrame, by passing in the dimension and the zero based frame index.

First, create a new FrameDimension object:


FrameDimension dimension = new System.Drawing.Imaging.FrameDimension(myImage.FrameDimensionsList[0]);

Once you have the dimension, you can get the frame count:


int frameCount = myImage.GetFrameCount(dimension);

Now, that you know the frame count of the image, you can call SelectActiveFrame by passing in the dimension and the frame index (zero based).


myImage.SelectActiveFrame(dimension,1);

If the image is being viewed at runtime, then the call to SelectActiveFrame will set the current frame, and then begin to loop through again (unless it is a jpeg image). The attached sample works around this by saving the image to a MemoryStream for display – thereby capturing the one frame that is chosen.

Sample Application w/Source Code C# or VB

Permalink

One way to do this is to use a derived columnstyle, override the Paint method and do the text drawing yourself, using whatever font or colors you like. If you add an event to your derived column style that is fired immediately before the text is drawn, and use the event args to get the font and color information, you can let the event handler completely determine the look of any cell.

The attached samples (C#, VB) use this technique to create a grid that looks like the grid in the picture. Both color and font varies on a cell basis or row basis in this picture.

Permalink

The problem is that the first click of a double click may be caught by the datagrid (and used to activate the cell) while the second click goes to the TextBox for the columnstyle object. This means the TextBox thinks this is a singleclick, and does not fire its doubleclick event. One solution is to mark the time of the click caught by the datagrid. Then look at this time in the TextBox’s mousedown handler to see if in fact the single click being looked at by the TextBox is part of a double click. You can download a sample (C#, VB) that illustrates how this might be done.

Permalink

One possible solution is that as you move off the cell, you get the typed value, and modify it before it is passed onto the DataGrid for its standard processing.

One problem is that there are several ways to leave the cell, and you would want to handle each (depending upon your needs). You can leave the cell by clicking on another cell, by tabbing off the cell, by arrowing off the cell, and by pressing Enter on an edited cell.

Another problem is that you want to catch these actions early enough to make a change in the typed value that can be passed onto the DataGrid itself. To catch the typed entries before they are handed off to the DataGrid, you have to gain access to the DataGridTextBoxColumn’s embedded TextBox. This requires that your DataGrid either has explicitly had these columns styles added to it, or that you minimally add a DataGridTableStyle to your DataGrid so the Framework generates ColumnsStyle objects for you.

To solve these problems, you can derive the dataGrid and override OnMouseDown, OnProcessDialogKey, and OnProcessPreviewKey. The last override will handle both the arrowing off the cell and pressing Enter on an edited cell.

You can download a sample (C#, VB.NET) that implements this technique.

Permalink

To format column output, you do not have to explicitly add DataGridColumns for each column provided you do add a DataGridTableStyle to your DataGrid prior to setting the DataSource property. When you set the DataSource property with a DataGrid that has a tablestyle with an empty columnstyle collection, the framework generates default columnstyle objects for each column in the datasource. You can then access these columnstyles directly and set properties in them such as Format, HeaderText and Width.

Download working samples here (VB.NET, C#).

Dim dataTableName As String = ''theTable''

 ’add a tablestyle to the grid so there will be custom columnstyles available
 ’    after the datasource has been set....
Dim ts As New DataGridTableStyle()
ts.MappingName = dataTableName

Me.dataGrid1.TableStyles.Add(ts)
Me.dataGrid1.DataSource = GetTheTable(dataTableName)

’now default customcolumnstyles have been created, so we can use them to set properties
Dim dgtbc As DataGridTextBoxColumn

’format the int
dgtbc = dataGrid1.TableStyles(0).GridColumnStyles(0)
If Not (dgtbc Is Nothing) Then
  dgtbc.Format = ''n0''
End If

’format the double
dgtbc = dataGrid1.TableStyles(0).GridColumnStyles(1) ’
If Not (dgtbc Is Nothing) Then
  dgtbc.Format = ''f3'' ’ 0r ''#.000''; 
End If

’format the double as currency
dgtbc = dataGrid1.TableStyles(0).GridColumnStyles(2) ’
If Not (dgtbc Is Nothing) Then
  dgtbc.Format = ''c4''
End If

’format the date
dgtbc = dataGrid1.TableStyles(0).GridColumnStyles(3) ’
If Not (dgtbc Is Nothing) Then
  dgtbc.Format = ''d'' ’ or  ''g'' or ''u'' or whatever format you want to see
  dgtbc.Width = 100 ’size it
End If
Permalink

One way to do this is to make the TextBox either a public property or a public field. Then you will be able to access it through the instance of its parent form. So, if TextBox1 is a public member of FormA and myFormA is an instance of FormA, then you can use code such as

[VB.NET]
  Dim strValue as String = myFormA.TextBox1.Text

[C#]
  string strValue = myFormA.TextBox1.Text;

anywhere myFormA is known. Here is a VB project illustrating this technique.

Permalink

One way to do this is to use MeasureString to compute the size of the text in each cell, and then take the maximum value. Below is a code snippet that does this. It assumes your datagrid is bound to a datatable. You can download a full working sample. (C#,VB).

public void AutoSizeCol(int col)
{
  float width = 0;
  int numRows = ((DataTable) dataGrid1.DataSource).Rows.Count;
    
  Graphics g = Graphics.FromHwnd(dataGrid1.Handle);
  StringFormat sf = new StringFormat(StringFormat.GenericTypographic);
  SizeF size;

  for(int i = 0; i < numRows; ++ i)
  {
    size = g.MeasureString(dataGrid1[i, col].ToString(), dataGrid1.Font, 500, sf);
    if(size.Width > width)
      width = size.Width;
  }

  g.Dispose();

  dataGrid1.TableStyles[''customers''].GridColumnStyles[col].Width = (int) width + 8; // 8 is for leading and trailing padding
}
Permalink

If you have added a TableStyle for your grid, then the code below should set the right column width to be the empty space from a button click. If you need to dynamically do this in response to the user sizing other columns, then there may be more work. But if you only need to do it at the end of your Form_Load, then this code might be sufficient. It assumes your datasource is a datatable. You can download a sample project (C#, VB).

private void button1_Click(object sender, System.EventArgs e)
{
  int numCols = ((DataTable)(dataGrid1.DataSource)).Columns.Count;

  //the fudge -4 is for the grid borders
  int targetWidth = dataGrid1.ClientSize.Width - SystemInformation.VerticalScrollBarWidth - 4;

  int runningWidthUsed = this.dataGrid1.TableStyles[''customers''].RowHeaderWidth; 
      
  for(int i = 0; i < numCols - 1; ++i)
    runningWidthUsed += this.dataGrid1.TableStyles[''customers''].GridColumnStyles[i].Width;

  if(runningWidthUsed < targetWidth)
    this.dataGrid1.TableStyles[''customers''].GridColumnStyles[numCols - 1].Width = targetWidth - runningWidthUsed;
}
Permalink

There is no event fired when the boolean value changes. In the attached sample (C#, VB), a BoolValueChanged event has been added to a columnstyle derived from DataGridBoolColumn. Catching the changes requires some effort. The strategy is to save the current value when the cell begins being edited. This is done in an override of Edit. Then in a Paint override, checks are done to see if there is a click in the checkbox cell or if the space bar is hit. If either of these situations happen when the cell is actively being edited, the bool value is changed and the event fired.

Permalink

TFor a single row select datagrid, you can get both these behaviors by using a custom column style and overriding its Edit method. In your override, handle unselecting and
selecting the current row, and DO NOT call the base class. Not calling the base class keeps the cell from becoming active. Here is a code snippet suggesting how this might be done. You can download a full working project (CS, VB).

public class DataGridNoActiveCellColumn : DataGridTextBoxColumn
{
  private int SelectedRow = -1;
  protected override void Edit(System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Rectangle bounds, bool readOnly,string instantText,bool cellIsVisible)
  {
    //make sure previous selection is valid
    if(SelectedRow > -1 && SelectedRow < source.List.Count + 1)
      this.DataGridTableStyle.DataGrid.UnSelect(SelectedRow);
    SelectedRow = rowNum;
    this.DataGridTableStyle.DataGrid.Select(SelectedRow);
  }
}

If you want to handle multi-selections, then there is more work to be done. One solution is to still override Edit as above, but have an empty implementation. Do not have the code the handles the selected row and do not call the baseclass. To handle the selections in this case, subclass the datagrid and override its OnMouseDown virtual method to change all cell clicks into row header clicks. Also override OnCurrentCellChanged to handle moving the current cell with the keyboard. You can download a sample (C#, VB) that implements this functionality.

Permalink

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
}

Note: You need to set the ControlStyles to ”UserPaint” in the constructor.

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

One way to do this is to override the form’s WndProc method and check for WM_SYSCOMMAND and SC_CLOSE. Looking for WM_CLOSE in the override is not sufficient as WM_CLOSE is seen in both cases. A sample is available for download (C#, VB).

public const int SC_CLOSE = 0xF060;
public const int WM_SYSCOMMAND = 0x0112;

//_closeClick is a bool member of the form initially set false...
// It can be tested in the Closing event to see how the closing came about.
  
protected override void WndProc(ref System.Windows.Forms.Message m)
{
  if(m.Msg == WM_SYSCOMMAND && (int)m.WParam == SC_CLOSE)
    this._closeClick = true;

  base.WndProc(ref m);
}
Permalink

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
Permalink

This sample (download C#, download VB) derives two custom columnstyles that display buttons. One displays a pushbutton with the cell text used as the button label. The second columnstyle displays text plus a dropdown button similar to a combobox button. Both columnstyles have an event that your form can handle to respond to clicks on the buttons. The row and column of the click are passed as part of the event arguments.

Both columnstyles derive from DataGridTextBoxColumn, and override Paint and Edit. The Edit override does not call the baseclass to avoid allowing the cell going into the edit mode. In the Paint override, the text is drawn, and a bitmap showing the button face is drawn.

There is no mouse handling within a columnstyle. To catch the click action, the columnstyle must handle the datagrid’s MouseDown and MouseUp events. In the columnstyle handlers for these events, the handler draws the depressed button as well as firing the columnstyle’s ColumnButtonClick event.

Your handler for this ColumnButtonClick event should take whatever action as a result of the buttonclick. In the sample projects, the handler just displays a messagebox.

Permalink

There is no text property exposed for a rowheader cell. But you can handle the Paint event and draw header text yourself. You can download sample projects (C#, VB) that illustrate one technique for doing so.

The sample loads the datagrid in the form’s Load event. In addition, this event is used to set the rowheaderwidth of the datagrid, and to remember the point where cell 0,0 is located. This point will allow us to find the toprow number when we need it to start drawing the header text. A handler for the datagrid’s Paint event is used to draw the text. It finds the toprow using the point from the original cell 0,0, and using the toprow determines the correct text for each rowheader. Finally, to avoid the complication of the user changing rowheights, we derive a new grid to prevent this.

private void dataGrid1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
  int row = TopRow();
  int yDelta = dataGrid1.GetCellBounds(row, 0).Height + 1;
  int y = dataGrid1.GetCellBounds(row, 0).Top + 2;
  
  CurrencyManager cm = (CurrencyManager) this.BindingContext[dataGrid1.DataSource, dataGrid1.DataMember];
  while(y < dataGrid1.Height - yDelta && row < cm.Count)
  {
    //get & draw the header text...
    string text = string.Format('row{0}', row);
    e.Graphics.DrawString(text, dataGrid1.Font, new SolidBrush(Color.Black), 12, y);
    y += yDelta;
    row++;
  }
}

Here is a datagrid with red row headers containing text.

Permalink

Here is a sample (C#, VB) that has column selections implemented. It derives a datagrid, adds a columns selection array list, and maintains this list in an override of OnMouseDown. Then to handle actually drawing the selected columns, it uses a derived column style, and sets the backBrush color and foreBrush color to the selection values in the override of the Paint method when it needs to draw a selected cell.

As an alternative to drawing the selection in a derived column style, you could just draw the selection in your derived grid by overriding OnPaint, calling the base class and then looping through the columns selections, filling a rectangle over the selected columns. The problem with this technique is that you would either have to redraw text in column with the correct forecolor, or you would have to use an alphablended color to let the text show through the selection rectangle. Using an alphablended color would eliminate the need to a column style override, but would give a different appearance than the ’standard’ selections that you see in a datagrid. The sample allows the row selections to continue to work so the column style override makes sense here.

Permalink

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 16×16.

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.

Permalink

Type converters let you convert one type to another type. Each type that you declare can optionally have a TypeConverter associated with it using the TypeConverterAttribute. If you do not specify one the class will inherit a TypeConverter from its base class.

The following methods deal with the type conversion work that is performed by TypeConverters.


// for conversion from another type to the type that this type converter is associated with
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType);
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value);

// for conversion of this type to some other type
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType);
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType);

The above functions provide the core of TypeConverters. One reason for the confusion surrounding TypeConverters is the fact that they have other functions that are unrelated to the primary type conversion function. Methods such as public PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value) do not perform type conversion. They are present for another purpose.

Typically when we require access to the properties, events and methods we use a TypeDescriptor. However there are .NET framework elements such as the PropertyGrid that first access the TypeConverter and then query it for a list of Properties (it returns true when public bool GetPropertiesSupported(); is called). If the TypeConverter returns false from public bool GetPropertiesSupported(), then the TypeDescriptor is queried for such information. Deriving a type converter is an easy way to present properties, events etc in any manner that you want especially with the property grid. In your own code also when you access Property information it is good practice to query the TypeConverter first. So, presentation of object properties, methods and events is also a function of the TypeConverter. Unfortunately, the name ’TypeConverter’ does not hint at this function.

Enclosed is a small sample that illustrates the use of a custom Type Converter for conversion. Download TypeConverter.zip. Try the sample and it will be clear what Type Converters primarily do.

For more information on TypeDescriptors please refer to https://www.syncfusion.com/content/en-us/faq/windowsforms/search/705.aspx

Permalink

You can use the first technique listed in this FAQ: How do I color an individual cell depending upon its value or some external method?

The idea is to derive a custom columnstyle. override its Paint method to specially color the background of the currentcell. Also, override the Edit to avoid the current cell becoming active. Below is a code snippet showing how you can do this. You can also download samples(C#, VB).

Public Class DataGridColoredTextBoxColumn
   Inherits DataGridTextBoxColumn
   
   Private column As Integer ’ column where this columnstyle is located...
   Public Sub New()
      column = -2
   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)

    Try
      Dim grid As DataGrid = Me.DataGridTableStyle.DataGrid

      ’first time set the column properly
      If column = -2 Then
        Dim i As Integer
        i = Me.DataGridTableStyle.GridColumnStyles.IndexOf(Me)
        If i > -1 Then
          column = i
        End If
      End If

      If grid.CurrentRowIndex = rowNum And grid.CurrentCell.ColumnNumber = column Then
        backBrush = New LinearGradientBrush(bounds, Color.FromArgb(255, 200, 200), Color.FromArgb(128, 20, 20), LinearGradientMode.BackwardDiagonal)
        foreBrush = New SolidBrush(Color.White)
      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

   Protected Overloads Overrides Sub Edit(ByVal source As System.Windows.Forms.CurrencyManager, ByVal rowNum As Integer, ByVal bounds As System.Drawing.Rectangle, ByVal [readOnly] As Boolean, ByVal instantText As String, ByVal cellIsVisible As Boolean)
      ’do nothing... don’t call the basecalss
   End Sub
End Class
Permalink

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);
}
Permalink

You can do this by deriving the DataGrid and overriding OnMouseDown. In your override, do a HitText and if the hit is on a column header that you do not want to sort, do not call the baseclass. Here is a code that sorts all columns except the second column.


[C#] 
//derived class 
public class MyDataGrid : DataGrid
{
    protected override void OnMouseDown(MouseEventArgs e)
    {
        Point pt = new Point(e.X, e.Y);
        DataGrid.HitTestInfo hti = this.HitTest(pt);
        if (hti.Type == HitTestType.ColumnHeader && hti.Column == 1)
        {
            //don't sort col 1 
            return; //don't call baseclass 
        }
        base.OnMouseDown(e);
    }
}

[VB.NET] 
'derived class 
Public Class MyDataGrid
    Inherits DataGrid
    Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
    Dim pt As New Point(e.X, e.Y)
    Dim hti As DataGrid.HitTestInfo = Me.HitTest(pt)
        If hti.Type = HitTestType.ColumnHeader AndAlso hti.Column = 1 Then
    'don't sort col 1 
    Return 'don't call baseclass
        End If
        MyBase.OnMouseDown(e)
    End Sub 'OnMouseDown 
End Class 'MyDataGrid
Permalink

The idea is to load your datatable in a normal fashion. Once the datatable is loaded, you can add an additional column that is computed from the other columns in your datatable. In the sample(CS, VB), we load the CustomerID, CompanyName, ContactName and ContactTitle from the Customers table in the NorthWind database. We then add an additional column that concatenates the ContactName and ContactTitle into one column.

To add the additional column, we create a DataColumn, set a mapping name, and then use the Expression property to define how the column is to be computed.

[C#]
  DataColumn dc = new DataColumn(''Contact'', typeof(string));
  dc.Expression = ''ContactName + ’:’ +ContactTitle'';
  _dataSet.Tables[''customers''].Columns.Add(dc);

[VB.NET]
  Dim dc As DataColumn
  dc = New DataColumn(''Contact'', GetType(System.String))
  dc.Expression = ''ContactName + ’:’ +ContactTitle''
  _dataSet.Tables(''customers'').Columns.Add(dc)

The sample actually shows two datagrids. The first one uses the default binding to display the entire table including our added column. In the second datagrid, we add a custom DataGridTableStyle to only display the CustomerID, CompanyName and our added column.

Permalink

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.

Download a working sample that shows this: simpledata5.zip


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.

Permalink

To be able to write changes back to the datasource, the data adapter object that populates your dataset should have commands set for updating, deleting etc. Fortunately, there is a class called SqlCommandBuilder that generates these commands from our Select command. All we have to do is instantiate this class passing it in the data adapter that we use.

Enclosed is a complete sample: simpledata4.zip


// Command builder will generate the command required to update the
// datasource from your select statement
SqlCommandBuilder commandBuilder = new SqlCommandBuilder(this.dataAdapter);

After this is done whenever you wish to write changes back to the data source simply call Update on the data adapter as shown below.


if(this.dataSet != null && this.dataSet.HasChanges())
     this.dataAdapter.Update(this.dataSet, ''Customers'');
Permalink

Please download this sample before reading the rest of this FAQ. Looking through the sample will help follow the description.

simpledata2.zip

What this boils down to is this:

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'';
Permalink

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.

Complete Sample: simpledata.zip


// 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;
    }
Permalink

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.

Permalink

You create a class the implements the IComparer interface. This interface has a single method that accepts two objects, and returns positive integers if the first object is larger than the second, negative integers if the first object is less than the second object, and returns zero if they are the same. Once you have this class, then you handle the listview’s ColumnClick event to set the property ListViewItemSorter to point to an instance of this class. You can download a sample project. Here are some snippets.

public class SorterClass : IComparer
{
  private int _colNum = 0;
  public SorterClass(int colNum)
  {
    _colNum = colNum;
  }

  //this routine should return -1 if xy and 0 if x==y.
  // for our sample we’ll just use string comparison
  public int Compare(object x, object y)
  {
    System.Windows.Forms.ListViewItem item1 = (System.Windows.Forms.ListViewItem) x;
    System.Windows.Forms.ListViewItem item2 = (System.Windows.Forms.ListViewItem) y;
    if(int.Parse(item1.SubItems[_colNum].Text) < int.Parse(item2.SubItems[_colNum].Text))
      return -1;
    else if(int.Parse(item1.SubItems[_colNum].Text) > int.Parse(item2.SubItems[_colNum].Text))
      return 1;
    else
      return 0;
  }
}

//usage...
private void listView1_ColumnClick(object sender, System.Windows.Forms.ColumnClickEventArgs e)
{
  //don’t sort col 0
  if(e.Column > 0)
  {
    SorterClass sc = new SorterClass(e.Column);
    listView1.ListViewItemSorter = sc;
  }
}
Permalink

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);
  }
}
Permalink

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. You can also download a working project.

// 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);
Permalink

Try calling one of the overloads of MeasureString that takes an StringFormat parameter and pass it StringFormat.GenericTypographic. Here is some code that shows the same string and font giving different results depending upon the StringFormat parameter.

StringFormat sf = new  StringFormat(StringFormat.GenericTypographic);

SizeF size =  e.Graphics.MeasureString('this has some words', Font, 500, sf);
SizeF size1 = e.Graphics.MeasureString('this has some words', Font);
string s = string.Format('GenericTypographic={0:f2}   GenericDefault={1:f2}', size.Width, size1.Width);
MessageBox.Show(s);

Permalink

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

3) Reference this collection in your control or component that should be designable. You need to supply a DesignerSerializationVisibility and an Editor attribute:


    private SomeItemCollection someItemCollection = null;
    ArrayList someItems = new ArrayList();
    
    [
    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);
    }

Permalink

By default, a control’s background color will be the same as the container’s background. In the picture below, the form’s background was set to Color.Red. In design mode, you can see the four controls have the red background, but since the form’s Image was set to a bitmap, you cannot see the form’s red background. To make the controls’ background transparent, you can set the alpha blend value of their background to zero. In code, you could use:

  public Form1()
  {
    InitializeComponent();

    checkBox1.BackColor = Color.FromArgb(0, checkBox1.BackColor);
    button1.BackColor = Color.FromArgb(0, button1.BackColor);
    linkLabel1.BackColor = Color.FromArgb(0, linkLabel1.BackColor);
    label1.BackColor = Color.FromArgb(0, label1.BackColor);
    // Or use the System.Drawing.Color.Transparent color.
  }

In design mode, you can set the alpha blend value to zero by typing the four component in the property grid. So, for each control’s BackColor property, you would type 0,255,0,0 to make it a transparent red.

Here is a VB project using this idea.

Permalink

The TextBox property of the DataGridTextBoxColumn is ReadOnly, so you just cannot set a new derived TextBox into it. One solution is to derive a TextBox, and then have it use the derived TextBox in the DataGridTextBoxColumn instead of the ‘generic’ TextBox that is there. This is the same technique used in our combobox in a column sample. In that sample, the generic textbox was ‘replaced’ with a combobox. Here, we replace it with a derived TextBox where we can easily override virtual members.

 

A reasonable question is why not just use the event mechanism of the existing TextBox to modify behavior. Events like KeyPress would allow us to do some things PROVIDED they get hit. Due to the key processing architecture of the FrameWork, for some special keys, these key events are not always fired. And if they are fired, sometimes it is impossible to avoid the default processing if this is the intent you have. Overriding a virtual function, doing something special, and then NOT calling the baseclass is a standard way of avoiding default processing.

 

In this sample (both VB and CS), we override PreProcessMessage and avoid processing the Keys.Tab key. You can modify the code to not process Keys.F2 as well. DataGridTextBoxColumn is subclassed so it can use our derived TextBox.

Permalink

Type conversion is usually possible in one of the following ways:

1) Using explicit methods in source type like:

  public class SizeF
  {
    ...
    public Size ToSize();
  }

There is usually a corresponding explicit or implicit operator that does the

same conversion. The operators are however not available in VB.Net.

2) Using FromXXX static methods exposed in the destination type:

  class Image
  {
    ...
    public static Image FromStream(Stream);
  }

3) Using implicit or explicit operators defined in the source or destination

type. This will allow you to perform implicit or explicit casts from the

source type to the destination type.

  SizeF sizeF = size; // Possible because type Size has an implicit type conversion operator that converts a Size to SizeF

  PointF pointF = (PointF)sizeF; // Possible because type SizeF has an explicit type conversion operator that converts a SizeF to PointF.

There is usually a corresponding ToXXX method that does the same conversion.
You can use either this or the ToXXX methods in C#.

4) Using TypeConverters.
Some types come with TypeConverters that allow you to convert to or convert

from another type. This conversion is usually not documented and can be

discovered only by writing some test code.

For example, the System.Drawing.Icon type’s TypeConverter converts the Icon

into a byte[]. You can use this functionality in your code as follows:

  TypeConverter tc = TypeDescriptor.GetConverter(typeof(System.Drawing.Icon));
  byte[] blob = tc.ConvertTo(myIcon, typeof(byte[]));

It’s usually time consuming to figure out whether a TypeConverter’s

ConvertTo or ConvertFrom method can perform the necessary conversion.

The attached TypeConverterLookup tool lets you figure that out easily on any type declared in any assembly available in the GAC or available in the same directory as the tool’s exe. If you have types in custom assemblies, then just copy over that assembly to the same directory as the tool, you can then specify the type in the tool.

Permalink

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.

    [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
    

    Method 2
    To use some method to provide the cell color, you can use a similar technique as discussed above. But instead of setting the color based on cell value, call a delegate method instead. This delegate can be passed in using the constructor for the custom column style. You can download a sample that shows how this can be done.

    Method 3
    If you want a more flexible solution, you could expose an event as part of your derived columnstyle that fires in the Paint override. This would allow the handler of the event to set the color 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 actually handles allowing a cell to be editable on a cell by cell basis through an event. This sample colors the back ground of the disabled cell gray. You could modify the eventargs to include a backcolor, and use this event to color cells based on row and column values removing the event call in the Edit override.

    Permalink

    You can use the System.Reflection.Assembly.CreateInstance method to create a
    form from its name. Below is a code snippet. The name in the textbox has to
    be the full name including its namespace. So if there is a class named Form2
    in namespace MyCompanyName, then the textbox would contain
    MyCompanyName.Form2. This snippet also assumes that the class is defined in
    the current executing assembly. If not, you would have to create an instance
    of the assembly that contains the class instead of calling the static method
    GetExecutingAssembly. As noted on this board, using reflection in this
    manner might affect performance.

    You can download working samples (VB.NET, C#).

    [C#]
      try
      {
        Assembly tempAssembly = Assembly.GetExecutingAssembly();
        // if class is located in another DLL or EXE, use something like
        //        Assembly tempAssembly = Assembly.LoadFrom(''myDLL.DLL'');
        // or
        //       Assembly tempAssembly = Assembly.LoadFrom(''myEXE.exe'');
    
        Form frm1 = (Form) tempAssembly.CreateInstance(textBox1.Text);// as Form;
        frm1.Show();
      }
      catch(Exception ex)
      {
        MessageBox.Show(''Error creating: ''+ textBox1.Text);
      }
    
    [VB.NET]
      textBox1.Text = ''MyNameSpace.Form2''
      ......
    
        Try
      Dim tempAssembly As System.Reflection.Assembly = System.Reflection.Assembly.GetExecutingAssembly()
    
       ’ if class is located in another DLL or EXE, use something like
       ’        tempAssembly = Assembly.LoadFrom(''myDLL.DLL'')
      ’ or
       ’       tempAssembly = Assembly.LoadFrom(''myEXE.exe'')
    
      Dim frm1 As Form = CType(tempAssembly.CreateInstance(textBox1.Text), Form) ’ as Form;
       frm1.Show()
      Catch ex As Exception
        MessageBox.Show(''Error creating: '' + ex.ToString())
      End Try
    Permalink

    The LineCap property of the Pen class controls how the ends of your line segments appear. You can set each end independently using the StartCap and EndCap properties of the Pen class. The following code segment produces the picture below.

    private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
     {
       Pen redPen = new Pen(Color.LightSalmon, 20);
       int startX = 80;
       int startY = 30;
       int width = this.ClientSize.Width - 2 * startX;
     Font _font = new Font("Arial Black", 13);
     foreach(LineCap LC in new LineCap[] { LineCap.ArrowAnchor,
               LineCap.DiamondAnchor,
               LineCap.Flat,
               LineCap.Round,
               LineCap.RoundAnchor,
               LineCap.Square,
               LineCap.SquareAnchor,
               LineCap.Triangle})
       {
         redPen.StartCap = LC;
         redPen.EndCap = LC;
         Point p1 = new Point(startX, startY);
         Point p2 = new Point(startX + width, startY);
         e.Graphics.DrawLine(redPen, p1, p2); e.Graphics.DrawString( LC.ToString(), _font, new SolidBrush(Color.Blue), startX + 40, startY - 13 ); startY +=  50;
     }
     }
    Permalink

    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. You can download a working sample.

    
      // 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''];
    
        }
      }
    
    Permalink

    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.
  • Permalink

    The attached EditableList UserControl implements an editable listbox with an in-place TextBox and Button allowing users to directly edit the contents of the list box.

    When the user clicks on a selected item, a textbox and a button is shown over the selected item and the user can directly edit the selected item text. The button can be programmed to show for example a OpenFileDialog to allow user to select a file (useful while implementing a Files list).

    Permalink

    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.

    Permalink

    You can handle the textbox’s KeyPress event and if you want the keypress to be an overwrite, just select the current character so the keypress will replace it. The attached sample has a derived textbox that has an OverWrite property and a right-click menu that allows you to toggle this property.

    The snippet below shows you a KeyPress handler that automatically does an overwrite if the maxlength of the textbox has been hit. This does not require a derived class.

    private void textBox1_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
    {
      if(textBox1.Text.Length == textBox1.MaxLength && textBox1.SelectedText == '''')
      {
        textBox1.SelectionLength = 1;
      }
    }
    Permalink

    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. You can also download a VB sample.

    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);
    
      }
    }
    Permalink

    You can download a working project. Below are VB code snippets showing how you might do these tasks.

    ’load a bitmap from an embedded resource
    Dim Bmp As Bitmap 
    ’ from an embedded resource
    Dim curName As String = ''BitmapVB.sync.bmp''
    Dim strm As System.IO.Stream = Me.GetType().Assembly.GetManifestResourceStream(curName)
    Bmp = New Bitmap(strm)
    PictureBox1.Image = Bmp
    .....
    .....
    ’load a bitmap from a file
    Dim Bmp As Bitmap = Image.FromFile(''c:\sync.bmp'')
    PictureBox1.Image = Bmp
    .....
    .....
    ’modify a bitmap
    Dim Bmp As Bitmap = PictureBox1.Image.Clone
    Dim g As Graphics = Graphics.FromImage(Bmp)
    Dim brush1 As SolidBrush = New SolidBrush(Color.Red)
    g.DrawString(TextBox1.Text, TextBox1.Font, brush1, 10, 10)
    PictureBox2.Image = Bmp
    g.Dispose()
    ....
    ....
    ’save a bitmap as a file
    Dim dlg As SaveFileDialog = New SaveFileDialog()
    dlg.Title = ''Save BMP file''
    dlg.InitialDirectory = ''c:\''
    dlg.Filter = ''bmp files (*.bmp)|*.bmp|All files (*.*)|*.*''
    If dlg.ShowDialog = DialogResult.OK Then
          PictureBox2.Image.Save(dlg.FileName
    End If
    Permalink

    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);}
          }
        }
    Permalink

    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.

    Permalink

    One way to do this is to use a ToolTip control and reset the control text as the mouse moves from cell to cell. Below is a derived DataGrid class that implements this idea. The main points are:

    Have members that track the current hitRow and hitCol where the mouse is.

    • In a MouseMove handler, do a HitTest on the mouse location to see if there is a new hit cell. If so, set the hitRow & hitCol, and hook the tooltip to hold your text according to the cell. In our sample, we just display the string value of the grid cell.
    • Finally, in the MouseMove handler, after setting a new text in the tooltip, set the tooltip active so it can show itself in due time.
    
    public class DataGridCellTips : DataGrid
    {
        private int hitRow;
        private int hitCol;
        private System.Windows.Forms.ToolTip toolTip1;
    
        public DataGridCellTips()
        {
            hitRow = -1;
            hitCol = -1;
            this.toolTip1 = new System.Windows.Forms.ToolTip();
            this.toolTip1.InitialDelay = 1000;
            this.MouseMove += new MouseEventHandler(HandleMouseMove);
        }
    
        private void HandleMouseMove(object sender, MouseEventArgs e)
        {
            DataGrid.HitTestInfo hti = this.HitTest(new Point(e.X, e.Y));
            if (hti.Type == DataGrid.HitTestType.Cell
                && (hti.Row != hitRow || hti.Column != hitCol))
            {     //new hit row 
                hitRow = hti.Row;
                hitCol = hti.Column;
                if (this.toolTip1 != null && this.toolTip1.Active)
                    this.toolTip1.Active = false; //turn it off 
                this.toolTip1.SetToolTip(this, this[hitRow, hitCol].ToString());
                this.toolTip1.Active = true; //make it active so it can show itself 
                //Console.WriteLine('MouseMove '+ hitRow.ToString() + ' ' + hitCol.ToString()); 
            }
        }
    }
    
    Permalink

    Use this custom editor attribute for the property that is of the StringCollection type (this can be used for other Collection types too if you want to allow entry of strings into the collection during design-time).

    
    Editor('System.Windows.Forms.Design.StringCollectionEditor, System.Design', 'System.Drawing.Design.UITypeEditor, System.Drawing'),
    public YourCollection YourProp{get{...}set{...}}
    

    StringCollectionEditor is not public so you have to use the above constructor override and specify the typename. This editor should allow you
    to edit your collection without any problems.

    Permalink

    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. You can download a full project (C#, VB).

    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;
        }
      }
    }
    
    Permalink

    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.

    Permalink

    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();
          }
        }
    
    Permalink

    There are several ways to go about this task. The simplest way involves adding a single combobox to the DataGrid.Controls, and then selectively displaying it as needed when a combobox cell becomes the currentcell. All the work is done in a few event handlers and no overrides or derived classes are necessary. This technique is discussed in Microsoft KB article Q323167.

    The other techniques require you to derive a columnstyle. Attached is a dropdown combobox sample (C#VB) that shows how you can use a combobox in a datagrid. This implementation differs from other available columnstyle samples (gotdotnet.com and C# Corner ) in that it derives from DataGridTextBoxColumn. These other samples derive directly from DataGridColumnStyle, and thus have to add functionality that already exists in DataGridTextBoxColumn. 

    This derived DataGridTextBoxColumn does not implement a databound combobox where you can set its DataSource, DisplayMember, and ValueMember to bind the combobox to a foreign table. If you need such a combobox, there is another sample link referenced at the end of this FAQ that does implement such a databound combobox.

    This sample just attempts to replace the TextBox member of DataGridTextBoxColumn with a standard ComboBox member. Only two overrides need to be handled along with 2 events to get a functional implementation. 

    Here are the notes from the code that list the 3 steps to add a combobox to your datagrid.

    
    // Step 1. Derive a custom column style from DataGridTextBoxColumn 
    //     a) add a ComboBox member 
    // b) track when the combobox has focus in Enter and Leave events 
    // c) override Edit to allow the ComboBox to replace the TextBox 
    // d) override Commit to save the changed data 
    
    
    // Step 2 - Use the combo column style 
    // Add 1 col with combo style 
    DataGridComboBoxColumn ComboTextCol = new DataGridComboBoxColumn();
    ComboTextCol.MappingName = ''custCity'';
    ComboTextCol.HeaderText = ''Customer Address'';
    ComboTextCol.Width = 100;
    ts1.GridColumnStyles.Add(ComboTextCol);
    
    // Step 3 - Additional setup for Combo style 
    // a) make the row height a little larger to handle minimum combo height 
    ts1.PreferredRowHeight = ComboTextCol.ColumnComboBox.Height + 3;
    // b) Populate the combobox somehow. It is a normal combobox, so whatever... 
    ComboTextCol.ColumnComboBox.Items.Clear();
    ComboTextCol.ColumnComboBox.Items.Add(''Chicago'');
    ComboTextCol.ColumnComboBox.Items.Add(''Corvallis'');
    ComboTextCol.ColumnComboBox.Items.Add(''Denver'');
    ComboTextCol.ColumnComboBox.Items.Add(''Great Falls'');
    ComboTextCol.ColumnComboBox.Items.Add(''Kansas City'');
    ComboTextCol.ColumnComboBox.Items.Add(''Los Angeles'');
    ComboTextCol.ColumnComboBox.Items.Add(''Raleigh'');
    ComboTextCol.ColumnComboBox.Items.Add(''Washington'');
    
    // c) set the dropdown style of the combo... 
    ComboTextCol.ColumnComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
    

    Databound ComboBox Sample
    To use a databound combobox, you have to add overrides for SetColumnValueAtRow and GetColumnValueAtRow to switch the DisplayMember and ValueMember as you get and set the data from the underlying table. Also, you cannot have the ComboBox bound with the same BindingContext to the same datasource as the datagrid. You can download a working project (C#VB) that implements a databound combobox in a datagrid.

    Thanks to Gerald Walsh for his suggestion to use the ComboBox.SelectionChangeCommitted event to set the editing flag within our derived columnstyle class.

    Permalink

    The LineJoin property of the Pen class allows you to specify how two lines should be joined. The following code segment produces the picture below.

      private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
      {
        Pen redPen = new Pen(Color.Red, 20);
          
        int startX = 20;
        int startY = 60;
        int width = this.ClientSize.Width / 17;
        int height = this.ClientSize.Height / 2;
    
        foreach(LineJoin LJ in new LineJoin[] { LineJoin.Bevel,
                             LineJoin.Miter,
                             LineJoin.Round})
        {
          redPen.LineJoin = LJ;
    
          Point[] points = {new Point(startX, startY),
                      new Point(startX + width, startY + height),
                      new Point(startX + 2 * width, startY),
                      new Point(startX + 3 * width, startY + height),
                      new Point(startX + 4 * width, startY)};
    
          e.Graphics.DrawLines(redPen, points);
          e.Graphics.DrawString( LJ.ToString(), new Font('Arial Black', 13), 
            new SolidBrush(Color.Blue), startX - 5, startY - 50);
          startX += 4 * width + 40;
        }
      }

    Permalink

    The Brushes Sample shows you how to use four different brushes in several shapes. Below is a code snippet showing how to use hatched and gradient brushes.

    
    Rectangle rect = new Rectangle(35, 190, 100, 100);       
     LinearGradientBrush brush2 = new LinearGradientBrush(rect, 
                               Color.DarkOrange, Color.Aquamarine, 
                              LinearGradientMode.ForwardDiagonal); 
     g.FillEllipse(brush2, 35, 190, 100, 100); 
     
     HatchBrush brush1 = new HatchBrush(HatchStyle.DiagonalCross, 
                              Color.DarkOrange, Color.Aquamarine); 
     g.FillEllipse(brush1, 35, 190, 100, 100);
    
    Permalink

    Yes. You just use the following syntax:

    
    devenv.exe {Solution Name} /command {Macro name}
    

    Example:

    
    devenv.exe 'myproject.sln' /command 'Macros.Syncfusion.Utils.FormatProjectAndClose' 
    

    Dev studio will start up, load the solution, run the macro and then terminate.

    Be sure to check that devenv.exe is available from your command line. Normally, you just have to run VSVARS32.bat to ensure this if you do not already have this in your path.

    Permalink

    You do this inorder to ensure that your component gets disposed along with the contained Form (logical parent).

    All Form derived classes come with an IContainer field into which many of the .Net components like ImageList and Timer add themselves to. The Form will dispose the contents of this IContainer from within its Dispose.

    Scenario 1

    In order for your Component to get added to this IContainer list, all you have to do is provide a constructor that takes IContainer as the one and only argument. The design-time will discover this constructor automatically and use it to initialize your component. You should then add yourself to the IContainer in the constructor implementation.

    Note that for this to work your Component should not have a custom TypeConverter that can convert your type to an InstanceDescriptor.

    Example:

    
    public class MyComponent : Component
    {
      public MyComponent()
      {
      }
    
      public MyComponent(IContainer container)
      {
        container.Add(this);
      }
    }
    

    Scenario 2

    Your components might have more constructors besides the default constructor and you might have a custom TypeConverter that provides an InstanceDescriptor to let your designer use a non-default constructor for initializing your component in code.

    In this case, the above approach will not work because you do not have an IContainer-argument only constructor.

    You now have to recreate what the design-time did for you. You have to provide a custom IDesignerSerializationProvider to do so. The attached ContainerInsertingSerializationProvider class can be used to get the above effect.

    Permalink

    Handle the Paint event for your control or form.

      private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
      {
        Graphics g = e.Graphics;
        Pen pen = new Pen(Color.White, 2);
        SolidBrush redBrush = new SolidBrush(Color.Red);
    
        g.DrawEllipse(pen, 100,150,100,100);
        g.DrawString('Circle', this.Font, redBrush, 80, 150);
    
        g.FillRectangle(redBrush, 140, 35, 20, 40);
        g.DrawString('Rectangle', this.Font, redBrush, 80, 50);
    
        g.DrawLine(pen, 114, 110, 150, 110);
        g.DrawString('Line', this.Font, redBrush, 80, 104);
      }

    Permalink

    Subclass Button and add a custom Paint event handler that does you custom drawing.

      public class MyButton : System.Windows.Forms.Button
      {
        public MyButton()
        {
          this.Paint += new System.Windows.Forms.PaintEventHandler(this.button1_Paint);
        }
        private void button1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
          //custom drawing
          Pen pen2 = new Pen(Color.Red);
          pen2.Width = 8;
          e.Graphics.DrawLine(pen2, 7, 4, 7, this.Height - 4);
          pen2.Width = 1;
          e.Graphics.DrawEllipse(pen2, this.Width - 16 , 6, 8, 8); 
        }
      }

    Permalink

    You can create a Graphics object from the base bitmap, and then use this Graphics object to draw the second bitmap with a transparent color that allows the base bitmap to show through.

      Bitmap Circle = (Bitmap)Image.FromFile(@'c:\circle.bmp');
      Bitmap MergedBMP = (Bitmap)Image.FromFile(@'c:\cross.bmp');
      Graphics g = Graphics.FromImage(Circle); 
      MergedBMP.MakeTransparent(Color.White);
      g.DrawImage(MergedBMP,0,0);
      g.Dispose();
    
      pictureBox1.Image = Circle;

    Permalink

    We have two suggestions with sample projects how you host a WebBrowser control inside a form and display HTML contents and listen to events such as NavigateComplete or BeforeNavigate. Of course there are many other ways to do this.

    Download htmlviewer.zip for two sample projects for the suggestions discussed below.

    1) The first suggestion is to generate an ActiveX wrapper for shdocvw using the aximp tool.
    The command line for this tool should be as follows:

      
      aximp c:\windows\system32\shdocvw.dll
    

    This will generate the following assemblies.

      
      Generated Assembly: D:\Syncfusion\faq\HtmlBrowser\HtmlViewer2\SHDocVw.dll
      Generated Assembly: D:\Syncfusion\faq\HtmlBrowser\HtmlViewer2\AxSHDocVw.dll
    

    Now you can reference these dlls in your project and use AxWebBrowser. In the attached HtmlViewer2 sample we have derived a HtmlControl class from AxWebBrowser and added some properties that let you specify a CSS Stylesheet and the Html content as a string.

    2) Our second sample lets you bypass the generation of a ActiveX wrapper. You don’t have to include and ship shdocvw.dll and axshdocvw.dll. In the attached HtmlViewer sample, we derived from AxHost and attached our own IWebBrowserEvents interface by overriding the CreateSink, AttachInterfaces and DetachSink methods.

    You can use HtmlControl in your form and specify HTML content by assigning a HTML string to HtmlControl. A cascading style sheet can be specified by assigning a path name to the CascadingStyleSheet property. The sample demonstrates how to use a CSS style sheet that has been embedded as a resource in the assembly.

    Permalink
      private void pictureBox1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
      {
        Graphics g = e.Graphics;
    
        g.TranslateTransform(100.0f, 100.0f);
        g.RotateTransform(-90.0f);
        g.DrawString('Vertical Text', Font, Brushes.Blue, 0.0f, 0.0f);
        g.ResetTransform();
    
        g.TranslateTransform(100.0f, 100.0f);
        g.RotateTransform(-45.0f);
        g.DrawString('Slanted Text', new Font(Font, FontStyle.Bold), Brushes.Red, 0.0f, 0.0f);
        g.ResetTransform();
    
      }

    Permalink

    Alpha-blending refers to allowing a background color to show through a particular color. You use the static Color.FromArgb method to create a alpha-blended color. For example,

      
    SolidBrush redBrushSolid = new SolidBrush(Color.FromArgb(255, 255, 0, 0));
    SolidBrush redBrushMedium = new SolidBrush(Color.FromArgb(120, 255, 0, 0));
    SolidBrush redBrushLight = new SolidBrush(Color.FromArgb(60, 255, 0, 0));
    

    creates three red brushes. The first argument is the alpha-blending value, from 0 to 255. The last three arguments are the RGB values, denoting in this case, red. In the picture below, all three circles use the color red, but each circle has a different alpha blending setting, allowing the white background to show through.

    Permalink

    Shawn Burke responded to this question in a posting on microsoft.public.dotnet.framework.windowsforms newsgroup.

    There is not currently a way to do this built into the framework, but WM_SETREDRAW will do what you’re looking for. It can’t be called recursively, so here’s code for a property you can add to your form to handle it. A VB sample is also available.

    int paintFrozen;
    
    private const int WM_SETREDRAW = 0xB;
    
    [DllImport(''User32'')]
    private static extern bool SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
    
    private bool FreezePainting 
    {
      get { return paintFrozen > 0; }
      set {
        if (value && IsHandleCreated && this.Visible) 
        {
          if (0 == paintFrozen++) 
          {
            SendMessage(Handle, WM_SETREDRAW, 0, 0);
          }
        }
        if (!value) 
        {
          if (paintFrozen == 0)
          {
                                     return;
                               }
    
                               if (0 == --paintFrozen) 
          {
                                    SendMessage(Handle, WM_SETREDRAW, 1, 0);
                                    Invalidate(true);
                               }
                         }
                      }
    }
    Permalink

    In a posting in the Microsoft.Windows.Forms newsgroup, Brian Roder (Microsoft) gives VB.Net code snippets to handle the DragEnter, ItemDrag and DragDrop events that provide a solution to this problem. You can get C# code in this sample, TreeViewDnD. Here is some sample handlers.

    private void treeView2_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
    {
      TreeNode newNode;
      if( e.Data.GetDataPresent(''System.Windows.Forms.TreeNode'', false))
      {
        Point pt;
        TreeNode destinationNode;
        pt = treeView2.PointToClient(new Point(e.X, e.Y));
        destinationNode = treeView2.GetNodeAt(pt);
        newNode = (TreeNode) e.Data.GetData(''System.Windows.Forms.TreeNode'');
        if(!destinationNode.Equals(newNode))
        {
          //destinationNode.Nodes.Add(newNode.Clone());
          destinationNode.Nodes.Add((TreeNode) newNode.Clone());
          destinationNode.Expand();
          //Remove original node
          newNode.Remove();
        }
      }
    }
    
    private void treeView2_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
    {
       e.Effect = DragDropEffects.Move;
    }
    
    private void treeView2_ItemDrag(object sender, System.Windows.Forms.ItemDragEventArgs e)
    {
      DoDragDrop(e.Item, DragDropEffects.Move);
    }
    
    Permalink

    Each type has a ToString method that can be used to accomplished formatting. Also, you can use the String.Format method to format things as well. To format dates, use the ToString member of DateTime. You may want to use the InvariantInfo setting (see below) to get culture-independent strings.

    Permalink

    Share with

    Share on twitter
    Share on facebook
    Share on linkedin

    Couldn't find the FAQs you're looking for?

    Please submit your question and answer.