Tuesday, March 31, 2009

Excel and DataGridView in .Net

Stumble del.icio.us Reddit MyWeb! Facebook Google bookmark

Here is a sample code to populate data from excel sheet into DataGridView like this:



One can populate the data in multiple ways one of which is explained here. Note the DataGridView.AutoGenerateColumn property which is not visible in designer because of it Browsable attribute being false. Thus if you have 10 columns in excel but you are interested in showing only 3 columns, here is how you can do it...








// Code for form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WindowsApplication1
{
public partial class Form1 : Form
{
public Form1 ()
{
InitializeComponent();
}

private void Form1_Load (object sender,EventArgs e)
{
DataTable sampleDataTable = new DataTable();

OleDbConnection aConnection = new OleDbConnection(
@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Users\Praveen K. Jha\Documents\book1.xls;Extended Properties=Excel 8.0");

aConnection.Open();

OleDbDataAdapter oleDbCommand = new
OleDbDataAdapter("Select * from [ExcelSheetNameContainingData$]"
, aConnection);
oleDbCommand.Fill(sampleDataTable);
// Now set the DataPropertyName which will tell which excel column maps to which datagridview column
dataGridView1.Columns[0].DataPropertyName= "ExcelSheetColumn1Name";
// At the same time set the header cell text too
dataGridView1.Columns[0].HeaderText ="ExcelSheetColumn1Name";

dataGridView1.Columns[1].DataPropertyName= "ExcelSheetColumn2Name";
dataGridView1.Columns[1].HeaderText ="ExcelSheetColumn2Name";

dataGridView1.Columns[2].DataPropertyName= "ExcelSheetColumn3Name";
dataGridView1.Columns[2].HeaderText ="ExcelSheetColumn3Name";
// Don't allow to automatically generate the columns
dataGridView1.AutoGenerateColumns = false;
dataGridView1.DataSource = sampleDataTable;
}
}
}



Here is the code for the designer part of the form which has the datagridview.


// Code for form1.designer.cs
namespace WindowsApplication1
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;

/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose (bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

#region Windows Form Designer generated code

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent ()
{
this.dataGridView1 = new System.Windows.Forms.DataGridView();
this.Column1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Column2 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Column3 = new System.Windows.Forms.DataGridViewTextBoxColumn();
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
this.SuspendLayout();
//
// dataGridView1
//
this.dataGridView1.AllowUserToAddRows = false;
this.dataGridView1.AllowUserToDeleteRows = false;
this.dataGridView1.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.Column1,
this.Column2,
this.Column3});
this.dataGridView1.Location = new System.Drawing.Point(45,38);
this.dataGridView1.MultiSelect = false;
this.dataGridView1.Name = "dataGridView1";
this.dataGridView1.ReadOnly = true;
this.dataGridView1.RowHeadersVisible = false;
this.dataGridView1.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dataGridView1.ShowCellErrors = false;
this.dataGridView1.ShowRowErrors = false;
this.dataGridView1.Size = new System.Drawing.Size(355,221);
this.dataGridView1.TabIndex = 0;
//
// Column1
//
this.Column1.HeaderText = "Column1";
this.Column1.Name = "Column1";
this.Column1.ReadOnly = true;
//
// Column2
//
this.Column2.HeaderText = "Column2";
this.Column2.Name = "Column2";
this.Column2.ReadOnly = true;
//
// Column3
//
this.Column3.HeaderText = "Column3";
this.Column3.Name = "Column3";
this.Column3.ReadOnly = true;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F,13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(474,356);
this.Controls.Add(this.dataGridView1);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
this.ResumeLayout(false);

}

#endregion

private System.Windows.Forms.DataGridView dataGridView1;
private System.Windows.Forms.DataGridViewTextBoxColumn Column1;
private System.Windows.Forms.DataGridViewTextBoxColumn Column2;
private System.Windows.Forms.DataGridViewTextBoxColumn Column3;
}
}



Hope this helps!!

Continue >>

Monday, March 30, 2009

int and System.Int32 types in .Net

Stumble del.icio.us Reddit MyWeb! Facebook Google bookmark

Ran into a strange erratic problem today related to Reflection/types or propertyinfo etc. I had a piece of code that is supposed to create an instance of an object if that object is not in existence. I have the information of which object has to be created. Let me give you a snap shot of the code first ..


private object CreateObjectInstance(string cmdName)
{
object instance = null;
try
{
Type type = GetObjectType(cmdName); // this will return the object type to be created
instance = type.Assembly.CreateInstance(type.FullName);
foreach (PropertyInfo property in type.GetProperties())
{
foreach (object attribute in
property.GetCustomAttributes(typeof(MyCustomAttributeType), true))
{
// Check to see if the property we are dealing with currently
// is supposed to be displayed.
MyCustomAttributeType attrib = attribute as MyCustomAttributeType;
if (attrib != null)
{
object value = null;
if (string.IsNullOrEmpty(attrib.Default)) // The attribue has a property called
// Default which returns a string
{
value = property.GetValue(instance, null);
}
else
{
value = attrib.Default;
}
// if the object that was created had an integer type get/set property
// I'm trying to do type conversions first
// Do type conversions
if (property.PropertyType == typeof(int)) // this is the problem spot
{
int result;
if (int.TryParse(attrib.Default, out result))
{
value = result;
}
}
// found one to be displayed.set default value
property.SetValue(instance, value, null);
}
}
}
}
catch (Exception exc)
{
//blah blah
}



Now the problem is on some systems (running XP SP2 with .Net framework 2.0 SP1), the line where I have shown the problem spot above, the typeOf() returns a type of Int32 as expected, but the equality operator fails and returns a false. This causes the SetValue method with a string type parameter being sent for an integer type setter property, causing exception (InValidArgumentException). Point of interest is the code does not generate an exception on systems running XP SP2 with .Net framework 2.0 SP2. However, if I change the problemetic line of code to something like this :


if (property.PropertyType == typeof(System.Int32)) // this was the problem spot
{
// blah blah
}



The problem goes away. Since the int and Int32 types are actually the same in .Net, ideally this should never have happened. But the more problemetic thing is it generates exception on one machine and not on he other running a later version of service pack. I checked the versions of System.dll assembly file on both machines. For SP1 it's 2.0.50727.1433. For framework SP2 it's 2.0.50727.3053. I really fail to understand why would this happen.

Continue >>

Sunday, March 22, 2009

ThreeStateTreeView / TriStateTreeView in vb.Net

Stumble del.icio.us Reddit MyWeb! Facebook Google bookmark

Here is a three state treeview control in vb.Net. Not sure if it will cater to everyone's needs. But should do enough.



' Copyright statement for Shweta :)
Namespace Example.Winforms
Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Windows.Forms.VisualStyles


Public Class ThreeStateTreeView
Inherits TreeView

Private Const STATE_UNCHECKED As Integer = 0

'unchecked state
Private Const STATE_CHECKED As Integer = 1

'checked state
Private Const STATE_MIXED As Integer = 2

'mixed state (indeterminate)
Private _InternalStateImageList As ImageList

'state image list
'create a new ThreeStateTreeView
Public Sub New()
MyBase.New

End Sub

'initialize all nodes state image
Public Sub InitializeNodesState(ByVal nodes As TreeNodeCollection)
For Each node As TreeNode In nodes
node.StateImageIndex = 0
If (node.Nodes.Count <> 0) Then
InitializeNodesState(node.Nodes)
End If
Next
End Sub

'update children state image with the parent value
Public Sub UpdateChildren(ByVal parent As TreeNode)
Dim state As Integer = parent.StateImageIndex
For Each node As TreeNode In parent.Nodes
node.StateImageIndex = state
If (node.Nodes.Count <> 0) Then
UpdateChildren(node)
End If
Next
End Sub

'update parent state image base on the children state
Public Sub UpdateParent(ByVal child As TreeNode)
Dim parent As TreeNode = child.Parent
If (parent Is Nothing) Then
Return
End If
If (child.StateImageIndex = STATE_MIXED) Then
parent.StateImageIndex = STATE_MIXED
ElseIf IsChildrenChecked(parent) Then
parent.StateImageIndex = STATE_CHECKED
ElseIf IsChildrenUnchecked(parent) Then
parent.StateImageIndex = STATE_UNCHECKED
Else
parent.StateImageIndex = STATE_MIXED
End If
UpdateParent(parent)
End Sub

'returns a value indicating if all children are checked
Public Shared Function IsChildrenChecked(ByVal parent As TreeNode) As Boolean
Return IsAllChildrenSame(parent, STATE_CHECKED)
End Function

'returns a value indicating if all children are unchecked
Public Shared Function IsChildrenUnchecked(ByVal parent As TreeNode) As Boolean
Return IsAllChildrenSame(parent, STATE_UNCHECKED)
End Function

'returns a value indicating if all children are in the same state
Public Shared Function IsAllChildrenSame(ByVal parent As TreeNode, ByVal state As Integer) As Boolean
For Each node As TreeNode In parent.Nodes
If (node.StateImageIndex <> state) Then
Return false
End If
If ((node.Nodes.Count <> 0) _
AndAlso Not IsAllChildrenSame(node, state)) Then
Return false
End If
Next
Return true
End Function

'build the checked, unchecked and indeterminate images
Private Shared Function GetStateImage(ByVal state As CheckBoxState, ByVal imageSize As Size) As Image
Dim bmp As Bitmap = New Bitmap(16, 16)
Dim g As Graphics = Graphics.FromImage(bmp)
Dim pt As Point = New Point(((16 - imageSize.Width) _
/ 2), ((16 - imageSize.Height) _
/ 2))
CheckBoxRenderer.DrawCheckBox(g, pt, state)
Return bmp
End Function

Protected Overrides Sub OnHandleCreated(ByVal e As EventArgs)
MyBase.OnHandleCreated(e)
If (_InternalStateImageList Is Nothing) Then
_InternalStateImageList = New ImageList
Dim g As Graphics = MyBase.CreateGraphics
Dim glyphSize As Size = CheckBoxRenderer.GetGlyphSize(g, CheckBoxState.UncheckedNormal)
_InternalStateImageList.Images.Add(GetStateImage(CheckBoxState.UncheckedNormal, glyphSize))
_InternalStateImageList.Images.Add(GetStateImage(CheckBoxState.CheckedNormal, glyphSize))
_InternalStateImageList.Images.Add(GetStateImage(CheckBoxState.MixedNormal, glyphSize))
_InternalStateImageList.Images.Add(GetStateImage(CheckBoxState.UncheckedDisabled, glyphSize))
_InternalStateImageList.Images.Add(GetStateImage(CheckBoxState.CheckedDisabled, glyphSize))
_InternalStateImageList.Images.Add(GetStateImage(CheckBoxState.MixedDisabled, glyphSize))
End If
MyBase.StateImageList = _InternalStateImageList
InitializeNodesState(MyBase.Nodes)
End Sub

'check if user click on the state image
Protected Overrides Sub OnMouseClick(ByVal e As MouseEventArgs)
MyBase.OnMouseClick(e)
If (e.Button = MouseButtons.Left) Then
Dim info As TreeViewHitTestInfo = MyBase.HitTest(e.Location)
If ((Not (info.Node) Is Nothing) _
AndAlso (info.Location = TreeViewHitTestLocations.StateImage)) Then
Dim node As TreeNode = info.Node
Select Case (node.StateImageIndex)
Case STATE_UNCHECKED, STATE_MIXED
node.StateImageIndex = STATE_CHECKED
Case STATE_CHECKED
node.StateImageIndex = STATE_UNCHECKED
End Select
UpdateChildren(node)
UpdateParent(node)
End If
End If
End Sub

'check if user press the space key
Protected Overrides Sub OnKeyDown(ByVal e As KeyEventArgs)
MyBase.OnKeyDown(e)
If (e.KeyCode = Keys.Space) Then
If (Not (MyBase.SelectedNode) Is Nothing) Then
Dim node As TreeNode = MyBase.SelectedNode
Select Case (node.StateImageIndex)
Case STATE_UNCHECKED, STATE_MIXED
node.StateImageIndex = STATE_CHECKED
Case STATE_CHECKED
node.StateImageIndex = STATE_UNCHECKED
End Select
UpdateChildren(node)
UpdateParent(node)
End If
End If
End Sub

'swap between enabled and disabled images
Protected Overrides Sub OnEnabledChanged(ByVal e As EventArgs)
MyBase.OnEnabledChanged(e)
Dim i As Integer = 0
Do While (i < 3)
Dim img As Image = _InternalStateImageList.Images(0)
_InternalStateImageList.Images.RemoveAt(0)
_InternalStateImageList.Images.Add(img)
i = (i + 1)
Loop
End Sub
End Class
End Namespace

Continue >>

Thursday, March 19, 2009

Cropping images using Jcrop JQuery

Stumble del.icio.us Reddit MyWeb! Facebook Google bookmark

Try cropping this image ... You will like it ..






Courtesy : deepliquid.com




X1
Y1 X2 Y2 W H







Other jQuery implementations:

Continue >>

Friday, March 13, 2009

C# MDI control with backcolor

Stumble del.icio.us Reddit MyWeb! Facebook Google bookmark

When designing an MDI form one comes across a limitation of having to accept the default gray background of the mdi control. Here is a way out :). MDI control is just like another windows control which has a default backcolor defined. One can change the backcolor if we handle the events properly. I have tried to demonstrate one such way in the OnLoad method of the form.

The code here is just a a small part of a form class with its IsMdiContainer property set to true.

You will need to add the following using directives so as to be able to run the code after placing it in your form class file.


using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;



And here goes the main code:



/// The brush used to paint the background.
private LinearGradientBrush m_bgndBrush;

// *********************************************************************
/// <summary>
/// Raises the System.Windows.Forms.Form.Load event
/// </summary>
/// <param name="e">An <see cref="System.EventArgs"/> that contains the
/// event data.</param>
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);

// Now set the background color for the mdi client
foreach (Control control in this.Controls)
{
if (control is MdiClient)
{
control.Paint += new PaintEventHandler(
this.PaintMdiClientBackground);
control.SizeChanged += new EventHandler(
this.ReSizeMdiClientBackground);
ControlEventHandler eh =
new ControlEventHandler(HandleMdiChildAddRemove);
control.ControlAdded += eh;
control.ControlRemoved += eh;
break;
}
}

} // end OnLoad


// *********************************************************************
/// <summary>
/// Paints the background of MdiClient.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">A <see cref="System.Windows.Forms.PaintEventArgs"/>
/// object that contains the event data.</param>
private void PaintMdiClientBackground(object sender, PaintEventArgs e)
{
// Check to see if the window is just being restored from
// minimised state.
if (this.Height <= 0 || this.Width <= 0) return; // Do not call the base.OnPaintBackground because it will just keep
// repainting the whole area since the backcolor is supposed to be
// transparent.
//base.OnPaintBackground(e);
m_bgndBrush = this.GetDisplayBgndBrush( m_bgndBrush, this.ClientRectangle);
e.Graphics.FillRectangle(m_bgndBrush, this.ClientRectangle); return; }
// end PaintMdiClientBackground

// *********************************************************************
/// <summary>
/// Paints the background of MdiClient.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">A <see cref="System.Windows.Forms.PaintEventArgs"/>
/// object that contains the event data.</param>
private void ReSizeMdiClientBackground(object sender, EventArgs e)
{
((MdiClient) sender).Invalidate();
}


// *********************************************************************
/// <summary>
/// Handles the <see cref="Control.ControlAdded"/> and
/// <see cref="Control.ControlRemoved"/>
/// events to enable or disable the windows mdilist menu item.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">An <see cref="EventArgs"/> object that contains
/// the event data.</param>
private void HandleMdiChildAddRemove(object sender, ControlEventArgs e)
{
// One could just show a Mdi window menu for example
//m_windowsMenu.Visible = this.MdiChildren.Length > 0;
}

//**********************************************************************
/// <summary>
/// Gets a brush used to paint the background area in the viewer's
/// middle (display) area.
/// </summary>
/// <remarks>
/// <para>This method optimizes creation of the brush by checking if
/// the brush's rectangle still matches <paramref name="rect"/>. If so,
/// the existing brush is returned. If they do not match or
/// <paramref name="brush"/> is <see langword="null"/>, a new brush
/// will be created and returned.</para>
/// </remarks>
/// <param name="brush">An existing brush previously returned by
/// this method or <see langword="null"/>.</param>
/// <param name="rect">The rectangular region that defines the starting
/// and ending points of the gradient.</param>
/// <returns>A <see cref="System.Drawing.Brush"/>.</returns>
/// <seealso cref="GetDisplayBgndBrush(Rectangle)"/>
/// <seealso cref="System.Drawing.Drawing2D.LinearGradientBrush"/>
private LinearGradientBrush GetDisplayBgndBrush
(
LinearGradientBrush brush,
Rectangle rect
)
{
if ((brush == null) ||
(brush.Rectangle.Width != (float) rect.Width) ||
(brush.Rectangle.Height != (float) rect.Height))
{
brush = this.GetDisplayBgndBrush(rect);
}
return (brush);
} // end GetDisplayBgndBrush (LinearGradientBrush, Rectangle)

//**********************************************************************
/// <summary>
/// Gets a brush used to paint the background area in the viewer's
/// middle (display) area.
/// </summary>
/// <remarks>
/// <para>This is a gradient brush whose primary use is to draw the
/// display area of the viewer window.</para>
/// </remarks>
/// <param name="rect">The rectangular region that defines the starting
/// and ending points of the gradient.</param>
/// <returns>A <see cref="System.Drawing.Brush"/>.</returns>
/// <seealso cref="GetDisplayBgndBrush(LinearGradientBrush,Rectangle)"/>
/// <seealso cref="System.Drawing.Drawing2D.LinearGradientBrush"/>
private GetDisplayBgndBrush(Rectangle rect)
{
LinearGradientBrush brush = new LinearGradientBrush(rect,
Color.FromArgb(33, 62, 153), // dark skyblue
Color.FromArgb(0, 172, 238), // light skyblue
90.0f, true);
brush.SetBlendTriangularShape(0.5f);
return (brush);
}


Now enjoy your colored mdicontrol. :)

Continue >>

Sortable Unbound DataGridView in C#

Stumble del.icio.us Reddit MyWeb! Facebook Google bookmark

I wrote a custom datagridview from some of my existing code. I had worked on some of my earlier projects and then every now and then I realised that the .Net datagridview has a lot of features but still it might be a good idea to have some more built in support. I truly believe that guys at Microsoft developed the current DataGridView perfectly the way they wanted covering most of the things. Still, if someone wants to customize the control, OOPs comes to rescue. So here it goes. This code implements generic sorting feature for bound (not required :) ) and unbound datagridview.


// Copyright © your's truly All rights reserved.

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Windows.Forms;

namespace Yours.Truly
{
//**************************************************************************
/// <summary>
/// Subclass of the <see cref="System.Windows.Forms.DataGridView"/> class.
/// </summary>
/// <remarks>
/// This class enhances the standard DataGridView control class by adding a
/// sort feature for unbound columns too :)
/// <para>$Id$</para>
/// <author>Yours truly</author>
/// </remarks>
public class CgDataGridView : DataGridView
{
private DataGridViewColumn m_sortedColumn;

//**********************************************************************
/// <summary>
/// Initializes a new instance of the <see cref="CgDataGridView"/>
/// class.
/// </summary>
/// <remarks>
/// The <see cref="System.Windows.Forms.DataGridView.ColumnHeadersDefaultCellStyle"/>
/// will be modified to have its
/// alignment set to <c>DataGridViewContentAlignment.MiddleCenter</c>.
/// </remarks>
public CgDataGridView()
{
// Set our unique default properties.
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;

// Alter the default column header cell style.
DataGridViewCellStyle hdrStyle = this.ColumnHeadersDefaultCellStyle;
hdrStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
}

//**********************************************************************
/// <summary>
/// Occurs when the state of a cell changes in relation to a change in
/// its contents.
/// </summary>
/// <remarks>
/// <para>If the current cell is dirty, this will commit the change. We
/// need to do this so other code can see the change immediately.
/// Normally, changes are only committed when focus leaves the cell.</para>
/// </remarks>
/// <param name="e">An <see cref="System.EventArgs"/> that contains
/// the event data.</param>
protected override void OnCurrentCellDirtyStateChanged(EventArgs e)
{
base.OnCurrentCellDirtyStateChanged(e);
if (this.IsDisposed || null == this) return;

// This method assumes that any editable cells are of a type that
// should be committed immediately. If other writable cell types
// (e.g., DataGridViewTextBoxColumn) are added to the DataGridView,
// code should be added below to handle that.
if (this.IsCurrentCellDirty)
{
this.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}

// *********************************************************************
/// <summary>
/// Changes the selection state of the row with the specified index.
/// </summary>
/// <param name="rowIndex">The index of the row.</param>
/// <param name="selected">true to select the row; false to cancel the
/// selection of the row.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="rowIndex"/> is less than 0 or greater than the
/// number of rows in the control.</exception>
public void SetSelectedRow(int rowIndex, bool selected)
{
base.SetSelectedRowCore(rowIndex, selected);
}

// *********************************************************************
/// <summary>
/// Gets a string array of values contained in this
/// <see cref="DataGridView"/>.
/// </summary>
/// <returns>A string representation of data contained in the grid.</returns>
public List<string[]> ToArray()
{
List<string[]> rowValues = new List<string[]>();
if (this.Columns.Count > 0)
{
foreach (DataGridViewRow row in this.Rows)
{
string[] cellValues = new string[this.Columns.Count];
for (int cellCount = 0; cellCount < this.Columns.Count; cellCount++)
{
if (row.Cells[cellCount].Value != null)
{
cellValues[cellCount] = row.Cells[cellCount].Value.ToString();
}
}
rowValues.Add(cellValues);
}
}
return rowValues;
}

#region Metadata overrides

//**********************************************************************
/// <summary>
/// Gets or sets a value indicating how column widths are determined.
/// </summary>
/// <remarks>
/// This simply provides new metadata for the
/// <see cref="System.Windows.Forms.DataGridView.AutoSizeColumnsMode"/>
/// property.
/// </remarks>
/// <value>One of the <c>DataGridViewAutoSizeColumnsMode</c> values.
/// The default is <c>DataGridViewAutoSizeColumnsMode.Fill</c>.</value>
/// <seealso cref="System.Windows.Forms.DataGridViewColumn.AutoSizeMode"/>
/// <seealso cref="System.Windows.Forms.DataGridView.AutoResizeColumn(int,DataGridViewAutoSizeColumnMode)"/>
/// <seealso cref="System.Windows.Forms.DataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode)"/>
[DefaultValue(DataGridViewAutoSizeColumnsMode.Fill)]
public new DataGridViewAutoSizeColumnsMode AutoSizeColumnsMode
{
get { return base.AutoSizeColumnsMode; }
set { base.AutoSizeColumnsMode = value; }
}

#endregion Metadata overrides

#region Sorting and IComparer implementation

// *********************************************************************
/// <summary>
/// Gets the column by which the System.Windows.Forms.DataGridView
/// contents are currently sorted.
/// </summary>
/// <value>The <see cref="DataGridViewColumn"/> by which the
/// <see cref="DataGridView"/> contents are currently sorted.</value>
public DataGridViewColumn CurrentSortedColumn
{
get
{
if (this.SortedColumn != null)
{
// Datagridview supports sorting.
return this.SortedColumn;
}
else
{
// Our own sorting?
return m_sortedColumn;
}
}
private set { this.m_sortedColumn = value; }
}

// *********************************************************************
/// <summary>
/// Gets a value indicating whether the items in the
/// <see cref="DataGridView"/> control are sorted in ascending or
/// descending order, or are not sorted.
/// </summary>
/// <value>One of the <see cref="SortOrder"/> values.</value>
public SortOrder CurrentSortOrder
{
get
{
if (CurrentSortedColumn != null)
{
return CurrentSortedColumn.HeaderCell.SortGlyphDirection;
}
else
{
return SortOrder.None;
}
}
}

// *********************************************************************
/// <summary>
/// Sorts the contents of the <see cref="DataGridView"/> control
/// in ascending or descending order based on the contents of the
/// specified column.
/// </summary>
/// <typeparam name="T">The type of the underlying data in the
/// <see cref="DataGridView"/>.</typeparam>
/// <param name="dataGridViewColumn">The column by which to sort the
/// contents of the <see cref="DataGridView"/>.</param>
/// <param name="sortableArray"> The array of underlying data.</param>
public void DoSort<T>(DataGridViewColumn dataGridViewColumn,
ref T[] sortableArray)
{
List<T> list = new List<T>(sortableArray);
this.DoSort<T>(dataGridViewColumn, ref list);
sortableArray = list.ToArray();
}

// *********************************************************************
/// <summary>
/// Sorts the contents of the <see cref="DataGridView"/> control
/// in ascending or descending order based on the contents of the
/// specified column.
/// </summary>
/// <typeparam name="T">The type of the underlying data in the
/// <see cref="DataGridView"/>.</typeparam>
/// <param name="dataGridViewColumn">The column by which to sort the
/// contents of the <see cref="DataGridView"/>.</param>
/// <param name="sortableList"> The list of underlying data.</param>
public void DoSort<T>(DataGridViewColumn dataGridViewColumn,
ref List<T> sortableList)
{
DataGridViewColumn oldColumn = this.CurrentSortedColumn;
ListSortDirection direction;

// If oldColumn is null, then the DataGridView is not currently sorted.
if (oldColumn != null)
{
// Sort the same column again, reversing the SortOrder.
if (oldColumn == dataGridViewColumn &&
this.CurrentSortOrder == SortOrder.Ascending)
{
direction = ListSortDirection.Descending;
}
else
{
// Sort a new column and remove the old SortGlyph.
direction = ListSortDirection.Ascending;
oldColumn.HeaderCell.SortGlyphDirection = SortOrder.None;
}
}
else
{
direction = ListSortDirection.Ascending;
}

CgSortHelper<T> sh =
new CgSortHelper<T>(ref sortableList);

sh.Sort(dataGridViewColumn.DataPropertyName, direction);
sortableList = sh.SortableList;
// Check if the datagridview has a datasource. If yes, bind the data
if (this.DataSource != null && sh.IsSortedCore) this.DataSource = sortableList;

dataGridViewColumn.HeaderCell.SortGlyphDirection =
direction == ListSortDirection.Ascending ?
SortOrder.Ascending : SortOrder.Descending;
CurrentSortedColumn = dataGridViewColumn;

} // end DoSort

// *********************************************************************
/// <summary>
/// Sorts the array of <see cref="DataGridViewRow"/> objects by it's
/// RowIndex in the specified <paramref name="sortOrder"/>.
/// </summary>
/// <param name="array">An array of DataGridViewRow.</param>
/// <param name="sortOrder"><see cref="SortOrder"/>.</param>
public void SortSelectedRowsByIndex(DataGridViewRow[] array,
SortOrder sortOrder)
{
Array.Sort(array, new CgSortComparer(sortOrder));
}

// *************************************************************************
/// <summary>
/// A class to implement sorting for the underlying data.
/// </summary>
private class CgSortComparer : IComparer
{
private SortOrder m_sortOrder;

// *********************************************************************
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="sortOrder"> Sort order for comparison.</param>
public CgSortComparer(SortOrder sortOrder)
{
this.m_sortOrder = sortOrder;
}

// *****************************************************************
/// <summary>
/// Compares object <paramref name="x"/> to object
/// <paramref name="y"/> and returns an indication of their relative
/// values.
/// </summary>
/// <param name="x"> First object.</param>
/// <param name="y"> Second object to compare with first object.</param>
/// <returns>A signed number indicating the relative values of
/// <paramref name="x"/> and <paramref name="y"/>.
/// < 0 : The value of <paramref name="x"/> is less than
/// <paramref name="y"/>.
/// = 0 : The value of <paramref name="x"/> is equal to the value of
/// <paramref name="y"/>.
/// > 0: The value of <paramref name="x"/> is greater than
/// <paramref name="y"/> or y is null.
/// </returns>
/// <exception cref="ArgumentException"> <paramref name="x"/> and
/// <paramref name="y"/> are not the same type.</exception>
public int Compare(object x, object y)
{
switch (this.m_sortOrder)
{
case SortOrder.Descending:
return ((DataGridViewRow) y).Index.CompareTo(
((DataGridViewRow) x).Index);
case SortOrder.Ascending:
default:
return ((DataGridViewRow) x).Index.CompareTo(
((DataGridViewRow) y).Index);
}
}
} // end class CgSortComparer

#endregion Sorting and IComparer implementation

} // end class CgDataGridView

} // end namespace Yours.Truly



Here is the sorthelper class.


// Copyright © Yours.Truly 2009. All rights reserved.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;

namespace Yours.Truly
{
//**************************************************************************
/// <summary>
/// A class to help sorting different kinds of object data.
/// </summary>
/// <remarks>
/// This class makes use of the CgPropertyComparer class to compare values
/// based on the type descriptor.
/// <para>$Id$</para>
/// <author>Your's truly</author>
/// </remarks>
public class CgSortHelper<T>
{
private bool m_isSorted;
private ListSortDirection m_direction = ListSortDirection.Ascending;
private List<T> m_sortableList;
[NonSerialized()]
private PropertyDescriptor m_sort = null;

//**********************************************************************
/// <summary>
/// Initializes a new instance of the CgSortHelper.
/// class.
/// </summary>
/// <param name="sortableList"> The list of underlying data.</param>
public CgSortHelper(ref List<T> sortableList)
{
m_sortableList = sortableList;
}

//**********************************************************************
/// <summary>
/// Initializes a new instance of the CgSortHelper.
/// class.
/// </summary>
/// <param name="sortableArray"> The array of underlying data.</param>
public CgSortHelper(ref T[] sortableArray)
{
m_sortableList = new List<T>(sortableArray);
}

#region Public Sorting API

// *********************************************************************
/// <summary>
/// Sorts the contents of the underlying data in ascending or descending
/// order.
/// </summary>
public void Sort()
{
ApplySortCore(m_sort, m_direction);
}

// *********************************************************************
/// <summary>
/// Sorts the contents of the underlying data in ascending or descending
/// order based on the contents of the specified <paramref name="property"/>.
/// </summary>
/// <param name="property"> The property based on which the sorting
/// needs to be performed.</param>
public void Sort(string property)
{
/* Get the PD */
m_sort = FindPropertyDescriptor(property);

/* Sort */
ApplySortCore(m_sort, m_direction);
}

// *********************************************************************
/// <summary>
/// Sorts the contents of the underlying data in ascending or descending
/// order based on the contents of the specified <paramref name="property"/>
/// and <paramref name="direction"/>.
/// </summary>
/// <param name="property"> The property based on which the sorting
/// needs to be performed.</param>
/// <param name="direction"> One of the <see cref="ListSortDirection"/>.
/// </param>
public void Sort(string property, ListSortDirection direction)
{
/* Get the sort property */
m_sort = FindPropertyDescriptor(property);
m_direction = direction;

/* Sort */
ApplySortCore(m_sort, m_direction);
}

#endregion

#region Sorting properties

// *********************************************************************
/// <summary>
/// Gets a value indicating whether the list supports sorting.
/// </summary>
/// <value>Always returns true.</value>
public bool SupportsSortingCore
{
get { return true; }
}

// *********************************************************************
/// <summary>
/// The underlying data to be sorted.
/// </summary>
/// <value> The list of objects of type T.</value>
public List<T> SortableList
{
get { return this.m_sortableList; }
}

// *********************************************************************
/// <summary>
/// Sorts the contents of the underlying data in ascending or descending
/// order based on the contents of the specified <paramref name="property"/>
/// and <paramref name="direction"/>.
/// </summary>
/// <param name="property"> The property based on which the sorting
/// needs to be performed.</param>
/// <param name="direction"> One of the <see cref="ListSortDirection"/>.
/// </param>
public void ApplySortCore(PropertyDescriptor property,
ListSortDirection direction)
{
List<T> items = this.SortableList as List<T>;

if ((null != items) && (null != property))
{
CgPropertyComparer<T> pc =
new CgPropertyComparer<T>(property, direction);
items.Sort(pc);

/* Set sorted */
m_isSorted = true;
}
else
{
/* Set sorted */
m_isSorted = false;
}
}

// *********************************************************************
/// <summary>
/// Gets a value indicating whether the list is sorted.
/// </summary>
/// <value>true if sorted; false otherwise.</value>
public bool IsSortedCore
{
get { return m_isSorted; }
}

// *********************************************************************
/// <summary>
/// Sets the <see cref="IsSortedCore"/> value to false without removing
/// sorting.
/// </summary>
public void RemoveSortCore()
{
m_isSorted = false;
}

#endregion

#region Private Sorting API

// *********************************************************************
/// <summary>
/// Gets the <see cref="PropertyDescriptor"/> with the given
/// <paramref name="property"/>.
/// </summary>
/// <param name="property"> The property based on which the sorting
/// needs to be performed.</param>
/// <returns><see cref="PropertyDescriptor"/>.</returns>
private PropertyDescriptor FindPropertyDescriptor(string property)
{
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(typeof(T));
PropertyDescriptor prop = null;

if (null != pdc)
{
prop = pdc.Find(property, true);
}
return prop;
}

#endregion

} // end CgSortHelper

//**************************************************************************
/// <summary>
/// A class to help sorting different kinds of object data.
/// </summary>
/// <remarks>
/// This class implements the IComparer interface and is based on
/// code implemented by Rockford Lhotka:
/// msdn.microsoft.com/library/default.asp?url=/library/en-us/dnadvnet
/// /html/vbnet01272004.asp" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnadvnet/html/vbnet01272004.asp">
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnadvnet/html/vbnet01272004.asp
/// <para>$Id$</para>
/// <author>your's truly</author>
/// </remarks>
internal class CgPropertyComparer<T> : IComparer<T>
{
private ListSortDirection m_direction = ListSortDirection.Ascending;
private PropertyDescriptor m_property;

//**********************************************************************
/// <summary>
/// Initializes a new instance of the CgPropertyComparer.
/// class.
/// </summary>
/// <param name="property"> The property based on which the sorting
/// needs to be performed.</param>
/// <param name="direction"> One of the <see cref="ListSortDirection"/>.
/// </param>
public CgPropertyComparer(PropertyDescriptor property,
ListSortDirection direction)
{
m_property = property;
m_direction = direction;
}

// *****************************************************************
/// <summary>
/// Compares object <paramref name="x"/> to object
/// <paramref name="y"/> and returns an indication of their relative
/// values.
/// </summary>
/// <param name="x"> First object.</param>
/// <param name="y"> Second object to compare with first object.</param>
/// <returns>A signed number indicating the relative values of
/// <paramref name="x"/> and <paramref name="y"/>.
/// < 0 : The value of <paramref name="x"/> is less than
/// <paramref name="y"/>.
/// = 0 : The value of <paramref name="x"/> is equal to the value of
/// <paramref name="y"/>.
/// > 0: The value of <paramref name="x"/> is greater than
/// <paramref name="y"/> or y is null.
/// </returns>
/// <exception cref="ArgumentException"> <paramref name="x"/> and
/// <paramref name="y"/> are not the same type.</exception>
public int Compare(T x, T y)
{
/* Get property values */
object xValue = GetPropertyValue(x, m_property.Name);
object yValue = GetPropertyValue(y, m_property.Name);

/* Determine sort order */
if (m_direction == ListSortDirection.Ascending)
{
return CompareAscending(xValue, yValue);
}
else
{
return CompareDescending(xValue, yValue);
}
}

// *********************************************************************
/// <summary>
/// Determines whether the specified x object of type T is equal to the
/// other object y of type T.
/// </summary>
/// <param name="x"> First object.</param>
/// <param name="y"> Second object to compare with first object.</param>
/// <returns> true if the x is equal to y, otherwise false.</returns>
public bool Equals(T x, T y)
{
return x.Equals(y);
}

// *********************************************************************
/// <summary>
/// Serves as a hash function for a particular type. System.Object.GetHashCode()
/// is suitable for use in hashing algorithms and data structures like a hash
/// table.
/// </summary>
/// <param name="obj">The object for which hashcode is required.</param>
/// <returns>A hash code for the current object of type T.</returns>
public int GetHashCode(T obj)
{
return obj.GetHashCode();
}

// *********************************************************************
/// <summary>
/// Compares two property values of any type
/// </summary>
/// <param name="x">First object.</param>
/// <param name="y">Second object.</param>
/// <returns>A signed number indicating the relative values of
/// <paramref name="x"/> and <paramref name="y"/> in ascending order.
/// < 0 : The value of <paramref name="x"/> is less than
/// <paramref name="y"/>.
/// = 0 : The value of <paramref name="x"/> is equal to the value of
/// <paramref name="y"/>.
/// > 0: The value of <paramref name="x"/> is greater than
/// <paramref name="y"/> or y is null.
/// </returns>
/// <exception cref="ArgumentException"> <paramref name="x"/> and
/// <paramref name="y"/> are not the same type.</exception>
private int CompareAscending(object x, object y)
{
int result;

/* If values implement IComparer */
if (x is IComparable)
{
result = ((IComparable) x).CompareTo(y);
}
/* If values don't implement IComparer but are equivalent */
else if (x.Equals(y))
{
result = 0;
}
/* Values don't implement IComparer and are not equivalent, so compare as string values */
else result = x.ToString().CompareTo(y.ToString());

/* Return result */
return result;
}

// *********************************************************************
/// <summary>
/// Compares two property values of any type
/// </summary>
/// <param name="x">First object.</param>
/// <param name="y">Second object.</param>
/// <returns>A signed number indicating the relative values of
/// <paramref name="x"/> and <paramref name="y"/> in descending order.
/// < 0 : The value of <paramref name="x"/> is less than
/// <paramref name="y"/>.
/// = 0 : The value of <paramref name="x"/> is equal to the value of
/// <paramref name="y"/>.
/// > 0: The value of <paramref name="x"/> is greater than
/// <paramref name="y"/> or y is null.
/// </returns>
/// <exception cref="ArgumentException"> <paramref name="x"/> and
/// <paramref name="y"/> are not the same type.</exception>
private int CompareDescending(object x, object y)
{
/* Return result adjusted for ascending or descending sort order ie
multiplied by 1 for ascending or -1 for descending */
return CompareAscending(x, y) * -1;
}

// *********************************************************************
/// <summary>
/// Gets the property value for the specified property and type.
/// </summary>
/// <param name="value">The type of object.</param>
/// <param name="property">The property for which the value is required.
/// </param>
/// <returns>An object containing the property value.</returns>
private object GetPropertyValue(T value, string property)
{
/* Get property */
PropertyInfo propertyInfo = value.GetType().GetProperty(property);

/* Return value */
return propertyInfo.GetValue(value, null);
}

} // end class CgPropertyComparer

} // end namespace Yours.Truly



Hope this helps someone.

Continue >>