Datagrid colums auto-size

  • Thread starter Thread starter Tiggy
  • Start date Start date
T

Tiggy

I am surprised to see no property exists to make auto-size column in a
datagrid. I only can change the "preferred size" but what to do if I want
columns with different sizes ? My datagrid is bound to a table in a dataset,
I guess I will have to apply different style to each column but how ? And
will my databinding be as easy as before ?

Thanks in advance for your cooperation,
tiggy
 
Here is a class you can use to inherit a standard DataGrid and then use some
new properties and methods. (Including Autosize and WidenRight Column).

Sample code included at bottom. Along with another class to change colors.
==============================================
Option Explicit On
Option Strict On

Imports System.Drawing
Imports System.Windows.Forms

Namespace abc.xyz
Public Class MyDataGrid
Inherits DataGrid

#Region "Public Properties"
Public ReadOnly Property VerticalScrollBarVisible() As Boolean
Get
'The VertScrollBar property of a DataGrid is Protected. The only way
to access it is thru a derived class!
Return Me.VertScrollBar.Visible
End Get
End Property
#End Region

#Region "Public Methods"
Public Sub WidenRightColumn(ByRef DataGrid As MyDataGrid, ByVal dt As
DataTable, ByVal TableStyle As String)
'computes the width of the running grid and only widens the right
column if it is less than the width.
Dim numCols As Integer
numCols = dt.Columns.Count

Dim scrollBarWidth As Integer
If DataGrid.VerticalScrollBarVisible = True Then
scrollBarWidth = SystemInformation.VerticalScrollBarWidth
Else
scrollBarWidth = 0
End If

'the fudge -4 is for the grid borders
Dim targetWidth As Integer
targetWidth = DataGrid.ClientSize.Width - scrollBarWidth - 4

Dim runningWidthUsed As Integer
runningWidthUsed = DataGrid.TableStyles(TableStyle).RowHeaderWidth
Dim i As Integer
i = 0

Do While (i < (numCols - 1)) And (runningWidthUsed < targetWidth)
runningWidthUsed +=
DataGrid.TableStyles(TableStyle).GridColumnStyles(i).Width
i += 1
Loop

If (runningWidthUsed < targetWidth) Then
DataGrid.TableStyles(TableStyle).GridColumnStyles((numCols -
1)).Width = (targetWidth - runningWidthUsed)
End If

End Sub

'Comments on Method: AutoFitColumns
'Parameters
' DataGrid=DataGrid that is to be formatted.
' DataTable = DataGrid.DataSource.DataSet.Tables(0)
' NumberOfRowsToScan=Number of data records to be scanned in order to
compute the columns widths (Recommend 20).
' MaxPixelWidth=Maximum allowable pixel width of a COLUMN. (JF - Use a
large number like 300.)

'The MaxPixelWidth parameter specifies the MAXIMUM value of the width
component of the returned SizeF structure
'(SizeF.Width). If the width parameter is less than the actual width of
the string, the returned SizeF.Width
'component is truncated to a value representing the maximum number of
characters that will fit within the specified width.

'Takes a DataGrid that should be already assigned a populated dataset,
'and formats the DataGrid so that the column widths reflect the widths
of the data.
'This is accomplished by dynamically creating a single
DataGridTableStyle and multiple DataGridTextBoxColumn(s).
'For each data column, the width of the column is set to the highest
data width within the column.
'In order to reduce computation time where large datasets exist,
'a parameter exists to define how many rows are scanned.
'I would suggest that only the first 20 rows are scanned.

Public Sub AutoFitColumns(ByRef DataGrid As MyDataGrid, ByVal dt As
DataTable, ByVal TableStyle As DataGridTableStyle, _
ByVal NumberOfRowsToScan As Integer, ByVal
MaxColumnWidth As Integer)


Dim HideColumns() As String = {""}
AutoFitColumns(DataGrid, dt, TableStyle, NumberOfRowsToScan,
MaxColumnWidth, HideColumns)
End Sub

Public Sub AutoFitColumns(ByRef DataGrid As MyDataGrid, ByVal dt As
DataTable, ByVal TableStyle As DataGridTableStyle, _
ByVal NumberOfRowsToScan As Integer, ByVal
MaxColumnWidth As Integer, ByVal HideColumns() As String)
'Create graphics object for measuring widths.
Dim Graphics As Graphics = DataGrid.CreateGraphics()
Try
'Can only scan rows if they exist.
NumberOfRowsToScan = System.Math.Min(NumberOfRowsToScan,
dt.Rows.Count)

'Now loop through the columns and set the widths.
Dim Column As DataColumn
Dim ColumnStyle As DataGridColumnStyle
Dim Width As Integer
For Each Column In dt.Columns
ColumnStyle = TableStyle.GridColumnStyles(Column.ColumnName)

If Not ColumnStyle Is Nothing Then
If HideColumns.IndexOf(HideColumns, Column.ColumnName) >= 0 Then
'hide all columns in the HideColumns array
ColumnStyle.Width = 0
Else
'Set width to header text width.
Width = CInt(Graphics.MeasureString(ColumnStyle.HeaderText,
DataGrid.Font, MaxColumnWidth).Width)
'Change width, if data width is wider than header text width.
'Check the width of the data in the first X rows.
Dim iRow As Integer
Dim dr As DataRow
For iRow = 0 To NumberOfRowsToScan - 1
dr = dt.Rows(iRow)
If Not IsDBNull(dr(Column.ColumnName)) Then
Width = CInt(System.Math.Max(Width,
Graphics.MeasureString(CStr(dr(Column.ColumnName)), DataGrid.Font,
MaxColumnWidth).Width))
End If
Next
'ColumnStyle.Width = Width + 4
'increase width from 4 to 20 to allow for the Sort arrow when
a column header is clicked on.
ColumnStyle.Width = Width + 20
End If
End If
Next
Finally
Graphics.Dispose()
End Try
End Sub

Public Sub MakeColumnsReadOnly(ByRef DataGrid As MyDataGrid, ByVal
TableName As String, ByVal ReadOnlyColumns() As String)
Dim ColName As String
For Each ColName In ReadOnlyColumns
DataGrid.TableStyles(TableName).GridColumnStyles(ColName).ReadOnly =
True
Next ColName
End Sub

#End Region

End Class
End Namespace


==============================================
Sample code to use the class in a Windows Form with a MyDataGrid on it:

'define a list of columns to make ReadOnly
Dim ReadOnlyColumns() As String = {"field1", "field2"}

'define a list of columns to Hide
Dim HideColumns() As String = {"field3", "field4"}

tableStyle.MappingName = strTableName
tableStyle.AlternatingBackColor = Bisque

'see below for another class to change the color of READ ONLY fields.
Dim MyColoredColumn As DataGridColoredTextBoxColumn
Dim i As Integer
Do While i < ds.Tables(strTableName).Columns.Count
MyColoredColumn = New DataGridColoredTextBoxColumn
MyColoredColumn.HeaderText =
ds.Tables(strTableName).Columns(i).ColumnName
MyColoredColumn.MappingName =
ds.Tables(strTableName).Columns(i).ColumnName
tableStyle.GridColumnStyles.Add(MyColoredColumn)
End If
i += 1
Loop

With DataGrid1
.TableStyles.Clear()
.TableStyles.Add(tableStyle)
.DataSource = ds
.DataMember = "dtName"
.AllowNavigation = False
.MakeColumnsReadOnly(DataGrid1, strTableName, ReadOnlyColumns)
.AutoFitColumns(DataGrid1, ds.Tables(strTableName), tableStyle, 20,
300, HideColumns)
.WidenRightColumn(DataGrid1, ds.Tables(strTableName), strTableName)
End With


==========================================
To make the read only columns a different color you need another class:

Option Explicit On
Option Strict On

Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Windows.Forms

Namespace xyz.abc
Public Class DataGridColoredTextBoxColumn
Inherits DataGridTextBoxColumn

#Region "Constructor"
Public Sub New()
'do nothing
End Sub
#End Region

#Region "Public Methods"
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 criteria on the cell value
' Here, we color any cell that is Read Only.
Try
If Me.ReadOnly = True Then
'could be as simple as
'backBrush = New SolidBrush(Color.Aqua)
'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
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 Region

End Class

End Namespace
 
Thanks you so much. I just included your sample of code, that is working
perfectly :)))))
That was exactely what I needed, that helped me a lot....
....and thanks again
Tiggy
 
Thanks for letting me know it worked.

Glad you could figure out what that code was doing and where it belonged!
 
Back
Top