« RSS Feed to dasBlog Content Converter | Main | Ten Must-Have Tools Every Developer Shou... »

Apparently this is, like, the problem for the ages or something, but noone on this planet has come up with a way to automatically size the columns in a DataGrid.  Sure, there's all sorts of pyscho ways that involve measuring the length of strings in DataSets with the Graphics context, yada yada.  But, since I'm binding strongly-typed Object Collections to DataGrids in WinForm apps, that doesn't work for me (and it's a little over the top, IMHO).

So, I thought about it like this:

  • If you double click on the little splitter between columns they will autosize.
  • Therefore, the code to autosize has been written for me; no need to measure strings, etc.
  • How do I force a double click? No, wait, wrongheadedness, how do I call whatever THEY call when a double click happens?
  • So, I reflectored into DataGrid.OnMouseDown and saw their custom HitTest calls a private ColAutoResize.  Ah, bingo.

If you're going to 'sin' do it with style - do it with Reflection.

private void dgLogging_DataSourceChanged(object sender, System.EventArgs e)
        {
            try
            {
                Type t = dgLogging.GetType();
                MethodInfo m = t.GetMethod("ColAutoResize",BindingFlags.NonPublic);

                for (int i = dgLogging.FirstVisibleColumn; (i < dgLogging.VisibleColumnCount); i++)
                {
                    m.Invoke(dgLogging, new object[]{i});
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.Write("Failed Resizing Columns: " + ex.ToString());
            }
        }

(Thanks to Patrick Cauldwell for his help)

Tracked by:
"Autosize Columns in Datagrid" (.NET From India) [Trackback]
Rich "The Hubbins" Claussen [Trackback]
"Trackback for DataGrid resize example" (Development) [Trackback]


Thursday, June 17, 2004 2:33:32 PM (Pacific Standard Time, UTC-08:00)
Scott, now that is just awesome! Not so much the tip of how to resize the columns, but the idea of how you came up with a better way to do it. All about the way we think and come up with good/simple code & solutions.

Great idea - thanks for sharing.
Thursday, June 17, 2004 4:38:24 PM (Pacific Standard Time, UTC-08:00)
I thought you could set the Width property of a custom column style to -1 to achieve the same effect...

(2 minutes later) I just tried that and it doesn't work. Maybe I'm confusing this with the asp.net grid control...
Stéphane Lajoie
Thursday, June 17, 2004 5:30:10 PM (Pacific Standard Time, UTC-08:00)
Guess what you find if you look at ColAutoResize in Relector? Graphics.MeasureString. Most of the example tend to assume the DataSource is a DataTable, but tt works well with collections if you check for the IList or IListSource interface on the DataSource.
Thursday, June 17, 2004 7:47:23 PM (Pacific Standard Time, UTC-08:00)
Awesome, Scott! That is a nice little snippet, even with Scott Allen's discovery. :)
Thursday, June 17, 2004 8:41:38 PM (Pacific Standard Time, UTC-08:00)
Yes, of course in my reflectoring I saw that internally it was doing evil things, but my point was (I did a lousy job of explaining it) that ALL the code samples I saw on the net went ON and ON about the graphics stuff, many in DIFFERENT ways from the internal way.

My point is that - I knew the code was written already because the functionality existed if you double clicked. So, I just needed to figure out how to get access to it - regardless of whether they did the same stuff internally.

You are right though, that my object collections implemented IList!
Scott Hanselman
Friday, June 18, 2004 5:37:19 AM (Pacific Standard Time, UTC-08:00)
That was a great example of how a developer should think. Of course I also don't have a comforing feeling when I know I'm using some unintended, private functionality, but the process of getting to the solution was perfect.
I want to be like you when I grow up :)
Sergio Pereira
Friday, June 18, 2004 9:36:29 AM (Pacific Standard Time, UTC-08:00)
Agreed. And I also get your point that it's better to "illegally" use reflection than to go around measuring strings and duplicating work (possibly incorrectly) that's already done.

On a related note, I've always hated the ListView control in Windows. It should be much more intelligent than this when being resized. For example, when it grows beyond the last column in width, it just shows wasted blank space, even if some columns are too narrow for their content... I mean, we have web browsers doing a fine job laying out multiple levels of nested tables containing a mix of rich text and images, while desktop applications continue to use this relic from 15 years ago...

The new fancy layout stuff in VS 2005 might allow for a neat new list control. I guess I could try to make one, if I had a spare computer to install this "community preview" thing :).
Stephané
Wednesday, July 07, 2004 4:40:01 AM (Pacific Standard Time, UTC-08:00)
I'm trying to convert this to vb (and I've never done reflection before) and I'm having a problem. Is this a correct implementation of your code or am I going a little wrong here?

Public Class DataGridButBetter
Inherits DataGrid
Public Sub AutoResizeColumns()
Try
Dim t As Type = MyBase.GetType 'also tried me.GetType but t always ends up as DataGridButBetter
Dim m As System.Reflection.MethodInfo = t.GetMethod("ColAutoResize", System.Reflection.BindingFlags.NonPublic) 'and this ends up as nothing (null to you c# boys)
For i As Integer = Me.FirstVisibleColumn To Me.VisibleColumnCount - 1
m.Invoke(Me, New Object() {i})
Next
Catch ex As Exception
System.Diagnostics.Trace.Write("Failed Resizing Columns: " + ex.ToString())
End Try
End Sub
End Class

any ideas where I'm going wrong? thanks!
H McIntyre
Wednesday, July 07, 2004 9:55:11 AM (Pacific Standard Time, UTC-08:00)
Forgive me, but you are doing this to a WINFORM data grid, right?
Scott Hanselman
Wednesday, July 07, 2004 11:30:48 PM (Pacific Standard Time, UTC-08:00)
Windows.Forms.DataGrid is what I'm doing this to.
h McIntyre
Friday, July 09, 2004 1:33:43 PM (Pacific Standard Time, UTC-08:00)
Excellent demonstration on reflection for someone new to reflection! I seem to be having reflection security problems though. This is a WINFORM application and when I debug it t.GetMethod("ColAutoResize", System.Reflection.BindingFlags.NonPublic) returns null. Is there anything special that needs to be done before the CLR will allow visibilty to the private methods? Thanks!
Doug Lott
Thursday, August 05, 2004 8:04:37 AM (Pacific Standard Time, UTC-08:00)
Great Job. For the last couple questions about the GetMethod returning null, try this:

[VB.net]
Dim m As MethodInfo = t.GetMethod("ColAutoResize", BindingFlags.NonPublic Or BindingFlags.Instance)

[C#]
MethodInfo m = t.GetMethod("ColAutoResize", BindingFlags.NonPublic | BindingFlags.Instance)

Someone else can probably explain why this works better than I.

Cheers!
John Reber
Thursday, September 02, 2004 12:41:05 PM (Pacific Standard Time, UTC-08:00)
h McIntyre - aren't you really doing it to a 'DataGridButBetter' extension of the DataGrid?? I try the same thing in my extended DataGrid and get null as well, even with that last OR addition. I think it is because we aren't getting the correct type back for GetType - we are getting our DataGrids, not the WinForms DataGrid...

Any ideas???
Andrew Kennett
Monday, September 20, 2004 8:44:55 AM (Pacific Standard Time, UTC-08:00)
I found, for whatever reason, that multiple queries to the VisibleColumnCount were not always the same...And it was stopping after 5 columns, regardless of how many were in the DataSet; however, the columns were there, and visible, just not being displayed.

[vb.net that's working great]
Private Sub sqlGrid_DataSourceChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles sqlGrid.DataSourceChanged
Try
Dim t As Type = sqlGrid.GetType
Dim m As Reflection.MethodInfo = t.GetMethod("ColAutoResize", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)

Dim i As Integer = sqlGrid.FirstVisibleColumn
Dim j As Integer = sqlGrid.VisibleColumnCount
Do While i < j
m.Invoke(sqlGrid, New Object() {i})
i = i + 1
Loop
Catch ex As Exception
Trace.Write("Failed to resize column: " + ex.ToString + vbCrLf)
End Try
End Sub
opello
Friday, September 24, 2004 3:37:01 PM (Pacific Standard Time, UTC-08:00)
Reflection works on a specific Type so what you want to do in the subclassed DataGrid scenario is grab the MethodInfo of ColAutoResize(int) off DataGrid and *not* the subclass. You also need to make sure you're specifying BindingFlags.Instance as well as the parameter signature. You then perform Invoke() on your subclassed instance. Here's what it might look like in a sub-classed SuperDataGrid:

public void AutoResizeColumns() {

MethodInfo m = typeof(DataGrid).GetMethod("ColAutoResize", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(Int32) }, null);

for (int i = this.FirstVisibleColumn; (i < this.VisibleColumnCount); i++) {
m.Invoke(this, new object[] {i});
}
}
Monday, November 08, 2004 9:14:16 PM (Pacific Standard Time, UTC-08:00)
I found the following to be required:

using System.Reflection;



protected void dataGrid1_DataSourceChanged(object sender, System.EventArgs e)

{

try

{

Type t = dataGrid1.GetType();

MethodInfo m = t.GetMethod("ColAutoResize",

BindingFlags.Instance | BindingFlags.NonPublic);



for (int i = dataGrid1.FirstVisibleColumn;

i < dataGrid1.VisibleColumnCount; i++)

m.Invoke(dataGrid1, new object[]{i});

}

catch (Exception ex)

{

System.Diagnostics.Trace.Write("Failed Resizing Columns: " + ex.ToString());

}

}



”You must specify Instance or Static along with Public or NonPublic or no members will be returned”; quote from http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemreflectionbindingflagsclasstopic.asp
Peter Beckwith
Saturday, March 19, 2005 10:35:07 AM (Pacific Standard Time, UTC-08:00)
Great, really great job!

I have been searching for an answer for this for a while now, and I came across solutions of pages and pages of code.

This is simple and elegant.

Congratulations.

If it can be useful I modified the code like this, so I can use it from wherever in my project.

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
Public Sub columns_autoresize(ByRef sqlGrid As DataGrid)

Try
Dim t As Type = sqlGrid.GetType
Dim m As Reflection.MethodInfo = t.GetMethod("ColAutoResize", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)

Dim i As Integer = sqlGrid.FirstVisibleColumn
Dim j As Integer = sqlGrid.VisibleColumnCount
Do While i < j
m.Invoke(sqlGrid, New Object() {i})
i = i + 1
Loop
Catch ex As Exception
Trace.Write("Failed to resize column: " + ex.ToString + vbCrLf)
End Try
End Sub
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
Lukygrl
Thursday, March 31, 2005 8:54:39 AM (Pacific Standard Time, UTC-08:00)
Very usefull code! Great work.
Thanks to all.
Carlos.
Monday, October 17, 2005 10:24:43 AM (Pacific Standard Time, UTC-08:00)
I was using the following code (on Paint Event) to AutoSize Columns in my datagrid:


Code:

Type t = dgTC.DataSource.GetType();
MethodInfo m = t.GetMethod("ColAutoResize", BindingFlags.Instance | BindingFlags.NonPublic);
for (int i = dgTC.FirstVisibleColumn; (i< dgTC.VisibleColumnCount); i++)
{
m.Invoke(dgTC, new object[]{i});
}

But after adding custom tablestyle and column style I am getting following exception:

Code:

System.Reflection.TargetInvocationException: Exception has been thrown
by the target of an invocation.

---> System.InvalidOperationException: The '' DataGridColumnStyle cannot
be used because it is not associated with a Property or Column in the DataSource.

At: System.Windows.Forms.DataGridColumnStyle.CheckValidDataSource(CurrencyManager value)

At: System.Windows.Forms.DataGridColumnStyle.GetColumnValueAtRow
(CurrencyManager source, Int32 rowNum)
at System.Windows.Forms.DataGrid.ColAutoResize(Int32 col)
--- End of inner exception stack trace ---

at System.Reflection.RuntimeMethodInfo.InternalInvoke(Object obj,
BindingFlags invokeAttr, Binder binder, Object[] parameters,
CultureInfo culture, Boolean isBinderDefault, Assembly caller, Boolean
verifyAccess)

at System.Reflection.RuntimeMethodInfo.InternalInvoke(Object obj,
BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture,
Boolean verifyAccess)

at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags
invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
at TotalControl.frmDataEntry.AutoSizeDataGrid() in c:\documents and settings\
MyComp\my documents\visual studio
projects\totalcontrol\frmdataentry.cs:line 699


Can someone help me with this? Thanks a lot in advance.
Rob
Comments are closed.

Contact

Sponsors

Hosting By

Hot Topics

Tags

Calendar

<November 2009>
SunMonTueWedThuFriSat
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345

Archives

November, 2009 (5)
October, 2009 (19)
September, 2009 (11)
August, 2009 (12)
July, 2009 (21)
June, 2009 (26)
May, 2009 (16)
April, 2009 (13)
March, 2009 (17)
February, 2009 (17)
January, 2009 (18)
December, 2008 (32)
November, 2008 (17)
October, 2008 (22)
September, 2008 (16)
August, 2008 (14)
July, 2008 (25)
June, 2008 (19)
May, 2008 (17)
April, 2008 (17)
March, 2008 (26)
February, 2008 (21)
January, 2008 (28)
December, 2007 (19)
November, 2007 (17)
October, 2007 (31)
September, 2007 (39)
August, 2007 (37)
July, 2007 (43)
June, 2007 (37)
May, 2007 (32)
April, 2007 (38)
March, 2007 (29)
February, 2007 (46)
January, 2007 (31)
December, 2006 (27)
November, 2006 (31)
October, 2006 (32)
September, 2006 (39)
August, 2006 (34)
July, 2006 (40)
June, 2006 (18)
May, 2006 (31)
April, 2006 (34)
March, 2006 (30)
February, 2006 (38)
January, 2006 (44)
December, 2005 (19)
November, 2005 (34)
October, 2005 (24)
September, 2005 (37)
August, 2005 (20)
July, 2005 (24)
June, 2005 (33)
May, 2005 (16)
April, 2005 (22)
March, 2005 (34)
February, 2005 (15)
January, 2005 (37)
December, 2004 (28)
November, 2004 (30)
October, 2004 (34)
September, 2004 (22)
August, 2004 (34)
July, 2004 (18)
June, 2004 (64)
May, 2004 (49)
April, 2004 (21)
March, 2004 (29)
February, 2004 (29)
January, 2004 (36)
December, 2003 (25)
November, 2003 (24)
October, 2003 (59)
September, 2003 (42)
August, 2003 (24)
July, 2003 (44)
June, 2003 (29)
May, 2003 (21)
April, 2003 (30)
March, 2003 (27)
February, 2003 (47)
January, 2003 (50)
December, 2002 (31)
November, 2002 (38)
October, 2002 (44)
September, 2002 (15)
May, 2002 (2)
April, 2002 (4)

Google Ads