Autosize Columns in a ListView

Z

Zach

I have a listview, and currently there is the situation where a user
can double click between two column headers, and the system will
automatically resize the column header to the width of the longest text
in that column. I want this calculation to take into account the width
of the header as well. Currently, if there are 0 items in the list,
this reduces my column header to a width of 0, which is obviously
wrong. I tried writing a function to call into the Win32 API, but for
whatever reason it's not working. The code I used was this:


[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd,
[MarshalAs(UnmanagedType.U4)] int Msg, IntPtr wParam, IntPtr lParam);

private void AutosizeColumn(int ColumnIndex)
{
const int LVM_FIRST = 0x1000;
const int LVM_SETCOLUMNWIDTH = LVM_FIRST + 30;
IntPtr LVSCW_AUTOSIZE_USEHEADER = new IntPtr(-2);
IntPtr Index = new IntPtr(ColumnIndex);

Debug.Assert(listViewStatParse.Columns.Count > ColumnIndex,
"listViewStatParse.Columns.Count > ColumnIndex");

int Result = SendMessage(this.Handle, LVM_SETCOLUMNWIDTH, Index,
LVSCW_AUTOSIZE_USEHEADER);
}

But this causes all sorts of weird things to happen. Perhaps what I
really need is a way to manually get the font used by the column
header, and I can figure out the text metrics on my own, then manually
figure out the width of the text in the column header and the
individual items. Problem is I don't know how to get the font
information for the column headers. It doesn't seem to be related to
the font information in the individual list view items.

Any other suggestions appreciated.

Thanks
 
Z

Zach

Sorry, the code I posted below showed me calling

int Result = SendMessage(this.Handle, LVM_SETCOLUMNWIDTH, Index,
LVSCW_AUTOSIZE_USEHEADER);

but actually it's the slightly less incorrect version:

int Result = SendMessage(listViewStatParse.Handle, LVM_SETCOLUMNWIDTH,
Index, LVSCW_AUTOSIZE_USEHEADER);

:) Sorry about that.
 
C

Claes Bergefall

Not sure what weird behaviour you're seeing, but there is no need to use
P/Invoke for this. The following works just fine

private void AutosizeColumn(int ColumnIndex)
{
listViewStatParse.Columns[ColumnIndex].Width = -2;
}

/claes
 
Z

Zach

That works if I know when I want to autosize my column. But what about
when the user double clicks the vertical separator between two column
headers? In this case, there is no event that says "the column header
is about to be autosized". All you get is a sizing event and then a
sized event, that look like every other sizing and sized event, as far
as I know. If there are no items in the list view, then double
clicking the separator between two column headers shrinks the width of
the column to zero.

Claes said:
Not sure what weird behaviour you're seeing, but there is no need to use
P/Invoke for this. The following works just fine

private void AutosizeColumn(int ColumnIndex)
{
listViewStatParse.Columns[ColumnIndex].Width = -2;
}

/claes

Zach said:
I have a listview, and currently there is the situation where a user
can double click between two column headers, and the system will
automatically resize the column header to the width of the longest text
in that column. I want this calculation to take into account the width
of the header as well. Currently, if there are 0 items in the list,
this reduces my column header to a width of 0, which is obviously
wrong. I tried writing a function to call into the Win32 API, but for
whatever reason it's not working. The code I used was this:


[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd,
[MarshalAs(UnmanagedType.U4)] int Msg, IntPtr wParam, IntPtr lParam);

private void AutosizeColumn(int ColumnIndex)
{
const int LVM_FIRST = 0x1000;
const int LVM_SETCOLUMNWIDTH = LVM_FIRST + 30;
IntPtr LVSCW_AUTOSIZE_USEHEADER = new IntPtr(-2);
IntPtr Index = new IntPtr(ColumnIndex);

Debug.Assert(listViewStatParse.Columns.Count > ColumnIndex,
"listViewStatParse.Columns.Count > ColumnIndex");

int Result = SendMessage(this.Handle, LVM_SETCOLUMNWIDTH, Index,
LVSCW_AUTOSIZE_USEHEADER);
}

But this causes all sorts of weird things to happen. Perhaps what I
really need is a way to manually get the font used by the column
header, and I can figure out the text metrics on my own, then manually
figure out the width of the text in the column header and the
individual items. Problem is I don't know how to get the font
information for the column headers. It doesn't seem to be related to
the font information in the individual list view items.

Any other suggestions appreciated.

Thanks
 
C

Claes Bergefall

Yes, I noticed that too. My post was just regarding your AutosizeColumn
method.

As for a way to handle when someone double-clicks a divider in the header it
can be accomplished by some WndProc stuff. The header acutally sends a
HDN_DIVIDERDBLCLICK notification to its parent (i.e. it sends a WM_NOTIFY
message to the ListView with the code HDN_DIVIDERDBLCLICK). You can handle
that and resize it. So inherit the ListView and override WndProc to handle
this notification. Here's some VB code that does this (sorry, you'll have to
translate it yourself)

Private Const WM_NOTIFY As Integer = &H4E
Private Const HDN_FIRST As Integer = 0 - 300
Private Const HDN_DIVIDERDBLCLICKA As Integer = HDN_FIRST - 5
Private Const HDN_DIVIDERDBLCLICKW As Integer = HDN_FIRST - 25

<StructLayout(LayoutKind.Sequential)>
Private Structure NMHDR
Public hwndFrom As IntPtr
Public idFrom As Integer
Public code As Integer
End Structure

<StructLayout(LayoutKind.Sequential)>
Private Structure NMHEADER
Public hdr As Win32.Native.NMHDR
Public iItem As Integer
Public iButton As Integer
Public pitem As IntPtr
End Structure

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_NOTIFY Then
Dim nm As NMHDR = CType(m.GetLParam(GetType(NMHDR)), NMHDR)
If nm.code = HDN_DIVIDERDBLCLICKA OrElse nm.code =
HDN_DIVIDERDBLCLICKW Then
Dim nmheader As NMHEADER = CType(m.GetLParam(GetType(NMHEADER)),
NMHEADER)
Me.Columns(nmheader.iItem).Width = -2
Exit Sub
End If
End If
MyBase.WndProc(m)
End Sub


/claes


Zach said:
That works if I know when I want to autosize my column. But what about
when the user double clicks the vertical separator between two column
headers? In this case, there is no event that says "the column header
is about to be autosized". All you get is a sizing event and then a
sized event, that look like every other sizing and sized event, as far
as I know. If there are no items in the list view, then double
clicking the separator between two column headers shrinks the width of
the column to zero.

Claes said:
Not sure what weird behaviour you're seeing, but there is no need to use
P/Invoke for this. The following works just fine

private void AutosizeColumn(int ColumnIndex)
{
listViewStatParse.Columns[ColumnIndex].Width = -2;
}

/claes

Zach said:
I have a listview, and currently there is the situation where a user
can double click between two column headers, and the system will
automatically resize the column header to the width of the longest text
in that column. I want this calculation to take into account the width
of the header as well. Currently, if there are 0 items in the list,
this reduces my column header to a width of 0, which is obviously
wrong. I tried writing a function to call into the Win32 API, but for
whatever reason it's not working. The code I used was this:


[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd,
[MarshalAs(UnmanagedType.U4)] int Msg, IntPtr wParam, IntPtr lParam);

private void AutosizeColumn(int ColumnIndex)
{
const int LVM_FIRST = 0x1000;
const int LVM_SETCOLUMNWIDTH = LVM_FIRST + 30;
IntPtr LVSCW_AUTOSIZE_USEHEADER = new IntPtr(-2);
IntPtr Index = new IntPtr(ColumnIndex);

Debug.Assert(listViewStatParse.Columns.Count > ColumnIndex,
"listViewStatParse.Columns.Count > ColumnIndex");

int Result = SendMessage(this.Handle, LVM_SETCOLUMNWIDTH, Index,
LVSCW_AUTOSIZE_USEHEADER);
}

But this causes all sorts of weird things to happen. Perhaps what I
really need is a way to manually get the font used by the column
header, and I can figure out the text metrics on my own, then manually
figure out the width of the text in the column header and the
individual items. Problem is I don't know how to get the font
information for the column headers. It doesn't seem to be related to
the font information in the individual list view items.

Any other suggestions appreciated.

Thanks
 
Z

Zach

This works beautifully, thanks :) Sorry I didn't describe my exact
intentions correctly the first time, this is what I was really after.

Thanks again
Zach

Claes said:
Yes, I noticed that too. My post was just regarding your AutosizeColumn
method.

As for a way to handle when someone double-clicks a divider in the header it
can be accomplished by some WndProc stuff. The header acutally sends a
HDN_DIVIDERDBLCLICK notification to its parent (i.e. it sends a WM_NOTIFY
message to the ListView with the code HDN_DIVIDERDBLCLICK). You can handle
that and resize it. So inherit the ListView and override WndProc to handle
this notification. Here's some VB code that does this (sorry, you'll have to
translate it yourself)

Private Const WM_NOTIFY As Integer = &H4E
Private Const HDN_FIRST As Integer = 0 - 300
Private Const HDN_DIVIDERDBLCLICKA As Integer = HDN_FIRST - 5
Private Const HDN_DIVIDERDBLCLICKW As Integer = HDN_FIRST - 25

<StructLayout(LayoutKind.Sequential)>
Private Structure NMHDR
Public hwndFrom As IntPtr
Public idFrom As Integer
Public code As Integer
End Structure

<StructLayout(LayoutKind.Sequential)>
Private Structure NMHEADER
Public hdr As Win32.Native.NMHDR
Public iItem As Integer
Public iButton As Integer
Public pitem As IntPtr
End Structure

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_NOTIFY Then
Dim nm As NMHDR = CType(m.GetLParam(GetType(NMHDR)), NMHDR)
If nm.code = HDN_DIVIDERDBLCLICKA OrElse nm.code =
HDN_DIVIDERDBLCLICKW Then
Dim nmheader As NMHEADER = CType(m.GetLParam(GetType(NMHEADER)),
NMHEADER)
Me.Columns(nmheader.iItem).Width = -2
Exit Sub
End If
End If
MyBase.WndProc(m)
End Sub


/claes


Zach said:
That works if I know when I want to autosize my column. But what about
when the user double clicks the vertical separator between two column
headers? In this case, there is no event that says "the column header
is about to be autosized". All you get is a sizing event and then a
sized event, that look like every other sizing and sized event, as far
as I know. If there are no items in the list view, then double
clicking the separator between two column headers shrinks the width of
the column to zero.

Claes said:
Not sure what weird behaviour you're seeing, but there is no need to use
P/Invoke for this. The following works just fine

private void AutosizeColumn(int ColumnIndex)
{
listViewStatParse.Columns[ColumnIndex].Width = -2;
}

/claes

I have a listview, and currently there is the situation where a user
can double click between two column headers, and the system will
automatically resize the column header to the width of the longest text
in that column. I want this calculation to take into account the width
of the header as well. Currently, if there are 0 items in the list,
this reduces my column header to a width of 0, which is obviously
wrong. I tried writing a function to call into the Win32 API, but for
whatever reason it's not working. The code I used was this:


[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd,
[MarshalAs(UnmanagedType.U4)] int Msg, IntPtr wParam, IntPtr lParam);

private void AutosizeColumn(int ColumnIndex)
{
const int LVM_FIRST = 0x1000;
const int LVM_SETCOLUMNWIDTH = LVM_FIRST + 30;
IntPtr LVSCW_AUTOSIZE_USEHEADER = new IntPtr(-2);
IntPtr Index = new IntPtr(ColumnIndex);

Debug.Assert(listViewStatParse.Columns.Count > ColumnIndex,
"listViewStatParse.Columns.Count > ColumnIndex");

int Result = SendMessage(this.Handle, LVM_SETCOLUMNWIDTH, Index,
LVSCW_AUTOSIZE_USEHEADER);
}

But this causes all sorts of weird things to happen. Perhaps what I
really need is a way to manually get the font used by the column
header, and I can figure out the text metrics on my own, then manually
figure out the width of the text in the column header and the
individual items. Problem is I don't know how to get the font
information for the column headers. It doesn't seem to be related to
the font information in the individual list view items.

Any other suggestions appreciated.

Thanks
 
S

Satish

Hi,
Please help me.
I am getting an exception when I am trying to resize the columns of a listview using SendMessage.

I have a requirement that I have to read the values from a listview in a dialog. I am got the handle for the Dialog and List View. I want to resize the columns of the ListView so that the messages are not truncated. I used SendMessage for this with the signature:
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
and the statement I used is
IntPtr ptrMsgResult = SendMessage( m_ptrListViewHandle, LVM_SETCOLUMNWIDTH, 2, 1000 );
I am getting exception sometimes when I execute this statement. Tried different signatures like HandleRef for the hWnd, IntPtr for the lParam. But of no use.

Could anybody suggest me how can I achieve my goal without an exception?
Also I see that there is a reference to the "ListViewStatParse" in the above Conversations. Can anybody tell me how to get that ListViewStatParse so that I can easily resize the columns of my listview? All I have is a handle to that ListView only.

I need it very urgent.
You can also mail me on (e-mail address removed)
Thank You Very much.

Thanks & Regards,
Satish

From http://www.developmentnow.com/g/29_2006_6_0_0_774968/Autosize-Columns-in-a-ListView.ht

Posted via DevelopmentNow.com Group
http://www.developmentnow.com
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top