ListView columns reordered event wanted!

P

platz

Hi,

I use a ListView that allows the user to reorder the columns.

After some research I found out that to get the new order of columns,
I had to grab into unmanaged code in the user32.dll. I succesfully
used the code provided here:
http://groups.google.dk/groups?q=//[email protected]&rnum=2

Now, my next problem is to determine when the user has reordered the
columns. Since no appropriate event has been implemented in .NET I
assume I have to use user32.dll again. Problem is: I do not know C++
and I am unfamiliar with the Windows API.

Next thing is, that I will need to do the same with columns rezised
and column sorted - how can I know, when those aspects have been
changed and how can I get information on to what they have been
changed?

Thanks in advance!

PPlatz
 
C

Claes Bergefall

AFAIK there is no event for when (automatic) sorting has
been done. You should turn of automatic sorting and sort
it manually (i.e. call the Sort method) when needed, e.g.
in response to the ColumnClick event or after you have
populated the list.

As for a notification about when a header has been resized
or moved, try this:

Public Const HDN_FIRST As Integer = 0 - 300
Public Const HDN_ENDTRACKA As Integer = HDN_FIRST - 7
Public Const HDN_ENDTRACKW As Integer = HDN_FIRST - 27
Public Const HDN_ENDDRAG As Integer = HDN_FIRST - 11
Public Const WM_NOTIFY As Integer = &H4E

<StructLayout(LayoutKind.Sequential)> Public Structure NMHDR
Public hwndFrom As IntPtr
Public idFrom As Integer
Public code As Integer
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_ENDTRACKA OrElse nm.Code = HDN_ENDTRACKW Then
' User has resized a column/header
' TODO: Add your code here
Exit Sub
ElseIf nm.code = HDN_ENDDRAG Then
' User has moved a column/header
' TODO: Add your code here
m.Result = IntPtr.Zero ' Return FALSE to allow the move
Exit Sub
End If
End If
MyBase.WndProc(m)
End Sub

/claes
 
P

Pernille Platz

Hi claes

Thanks a lot! I'll give it a try though I have to translate your code
sample to C#. I posted my question through google and the only thing I
was aware of, was that I was in a newsgroup named:
microsoft.public.dotnet.framework.windowsforms

Never mind. I'll let you know if it works out for me.
PPlatz
 
P

platz

Hi

I successfully translated the vb code provided by claes to c# (see
below).
The problem is, that the event is fired before the action has
finished. This means that I cannot get the new order of columns or the
new column widths when I am in the WndProc method. So, how do I get
there?

Platz

==================================
public const int HDN_FIRST = 0 - 300;
public const int HDN_ENDTRACKA = HDN_FIRST - 7;
public const int HDN_ENDTRACKW = HDN_FIRST - 27;
public const int HDN_ENDDRAG = HDN_FIRST - 11;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
public const int WM_NOTIFY = 0x4E;

[StructLayout(LayoutKind.Sequential)]
public struct NMHDR{
public IntPtr hwndFrom;
public int idFrom;
public int code;
}

protected override void WndProc(ref System.Windows.Forms.Message m
)
{
if (m.Msg == WM_NOTIFY){
NMHDR nm = (NMHDR)m.GetLParam(typeof(NMHDR));
if (nm.code == HDN_ENDTRACKA || nm.code == HDN_ENDTRACKW)
{
// User has resized a column/header
MessageBox.Show("column resized");
label1.Text = "Col1: " + listView1.Columns[0].Width;
label2.Text = "Col2: " + listView1.Columns[1].Width;
return;
}
else if(nm.code == HDN_ENDDRAG)
{
// User has moved a column/header
// TODO: Add your code here
m.Result = IntPtr.Zero; // Return FALSE to allow the move
MessageBox.Show("column moved");
SomeMethod();
return;
}
}
base.WndProc(ref m);
}
==================
 
C

Claes Bergefall

Use Control.BeginInvoke to invoke an async method from
inside the if-statement. Then in that async method you can
get the correct order/size. It would look like this in VB.NET:

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
....
If nm.code = HDN_ENDTRACKA ... Then
...
Me.BeginInvoke(New MethodInvoker(AddressOf
AsyncGetInfoColumnSize))
Exit Sub
ElseIf nm.code = HDN_ENDDRAG Then
m.Result = IntPtr.Zero
Me.BeginInvoke(New MethodInvoker(AddressOf AsyncGetColumnOrder))
Exit Sub
End If
....
End Sub

Private Sub AsyncGetColumnSize()
' Get the column size here
End Sub
Private Sub AsyncGetColumnOrder()
' Get the column order here
End Sub

which should be something like this in C#

....
MethodInvoker method = new MethodInvoker(AsyncGetColumnSize);
this.BeginInvoke(method);
return;
....
MethodInvoker method = new MethodInvoker(AsyncGetColumnOrder);
this.BeginInvoke(method);
return;
....

public void AsyncGetColumnSize()
{
// Get the column size here
}
public void AsyncGetColumnOrder()
{
// Get the column order here
}

/claes

platz said:
Hi

I successfully translated the vb code provided by claes to c# (see
below).
The problem is, that the event is fired before the action has
finished. This means that I cannot get the new order of columns or the
new column widths when I am in the WndProc method. So, how do I get
there?

Platz

==================================
public const int HDN_FIRST = 0 - 300;
public const int HDN_ENDTRACKA = HDN_FIRST - 7;
public const int HDN_ENDTRACKW = HDN_FIRST - 27;
public const int HDN_ENDDRAG = HDN_FIRST - 11;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
public const int WM_NOTIFY = 0x4E;

[StructLayout(LayoutKind.Sequential)]
public struct NMHDR{
public IntPtr hwndFrom;
public int idFrom;
public int code;
}

protected override void WndProc(ref System.Windows.Forms.Message m
)
{
if (m.Msg == WM_NOTIFY){
NMHDR nm = (NMHDR)m.GetLParam(typeof(NMHDR));
if (nm.code == HDN_ENDTRACKA || nm.code == HDN_ENDTRACKW)
{
// User has resized a column/header
MessageBox.Show("column resized");
label1.Text = "Col1: " + listView1.Columns[0].Width;
label2.Text = "Col2: " + listView1.Columns[1].Width;
return;
}
else if(nm.code == HDN_ENDDRAG)
{
// User has moved a column/header
// TODO: Add your code here
m.Result = IntPtr.Zero; // Return FALSE to allow the move
MessageBox.Show("column moved");
SomeMethod();
return;
}
}
base.WndProc(ref m);
}
==================


Pernille Platz <[email protected]> wrote in message
Hi claes

Thanks a lot! I'll give it a try though I have to translate your code
sample to C#. I posted my question through google and the only thing I
was aware of, was that I was in a newsgroup named:
microsoft.public.dotnet.framework.windowsforms

Never mind. I'll let you know if it works out for me.
PPlatz
 
P

Pernille Platz

It works fine! Thankyou!

Here is the C# code, if someone should be interested.

=========================

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;

namespace TestWin32Events
{
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.ListView listView1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

public const int HDN_FIRST = 0 - 300;
public const int HDN_ENDTRACKA = HDN_FIRST - 7;
public const int HDN_ENDTRACKW = HDN_FIRST - 27;
public const int HDN_ENDDRAG = HDN_FIRST - 11;
public const int WM_NOTIFY = 0x4E;

[StructLayout(LayoutKind.Sequential)]
public struct NMHDR
{
public IntPtr hwndFrom;
public int idFrom;
public int code;
}


public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

ColumnHeader header1 = new ColumnHeader();
header1.Text = "One";
header1.Width = 100;
header1.TextAlign = HorizontalAlignment.Left;

ColumnHeader header2 = new ColumnHeader();
header2.Text = "Two";
header2.Width = 100;
header2.TextAlign = HorizontalAlignment.Left;

this.listView1.Columns.Add(header1);
this.listView1.Columns.Add(header2);
this.listView1.AllowColumnReorder = true;
this.listView1.HeaderStyle = ColumnHeaderStyle.Clickable;
this.listView1.GridLines = true;
this.listView1.View = View.Details;
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (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.listView1 = new System.Windows.Forms.ListView();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// listView1
//
this.listView1.Location = new System.Drawing.Point(40, 32);
this.listView1.Name = "listView1";
this.listView1.Size = new System.Drawing.Size(200, 97);
this.listView1.TabIndex = 0;
//
// label1
//
this.label1.Location = new System.Drawing.Point(80, 152);
this.label1.Name = "label1";
this.label1.TabIndex = 1;
this.label1.Text = "label1";
//
// label2
//
this.label2.Location = new System.Drawing.Point(80, 184);
this.label2.Name = "label2";
this.label2.TabIndex = 2;
this.label2.Text = "label2";
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.listView1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}

protected override void WndProc(ref System.Windows.Forms.Message m)
{
if (m.Msg == WM_NOTIFY)
{
NMHDR nm = (NMHDR)m.GetLParam(typeof(NMHDR));
if (nm.code == HDN_ENDTRACKA || nm.code == HDN_ENDTRACKW)
{
// User has resized a column/header
MessageBox.Show("column resized");

MethodInvoker method = new MethodInvoker(DispayColumnWidth);
this.BeginInvoke(method);
return;
}
else if(nm.code == HDN_ENDDRAG)
{
m.Result = IntPtr.Zero; // Return FALSE to allow the move
MessageBox.Show("column moved");
MethodInvoker method = new MethodInvoker(DisplayColumnOrder);
this.BeginInvoke(method);
return;
}
}
base.WndProc(ref m);
}

[DllImport("user32.dll")]
static extern bool SendMessage(IntPtr hWnd, Int32 msg,
Int32 wParam, int[] lParam);

//returns an array of column index
private int[] GetDisplayColumnOrders(ListView listview)
{

//LVM_GETCOLUMNORDERARRAY
int x = 0x1000+59;
int len = listview.Columns.Count;
int [] inta = new int[len];
SendMessage(listview.Handle,x,len, inta);
return inta;
}


private void DisplayColumnOrder()
{
int[] displayOrder = GetDisplayColumnOrders(listView1);
string str = "";
foreach (int index in displayOrder )
{
ColumnHeader column = listView1.Columns[index];
str += column.Text;
}
MessageBox.Show("ColumnOrder: " + str);
}

private void DispayColumnWidth()
{
label1.Text = "Col1: " + listView1.Columns[0].Width;
label2.Text = "Col2: " + listView1.Columns[1].Width;
}
}
}
=========================
 
R

Richard Peterson

This works fine, unless the ListView is on a Panel. Then the WndProc
doesn't get the column resize messages. I assume the Panel is trapping
the messages, but I don't know what to do about it. Ideas, anyone?
 

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