Custom Control with SetWindowLong() P/Invoke calls

G

Guest

I'm trying to create a custom listbox control that can fire events before the
selected item changes in the list box and let subscribers prevent the changes.
I'm using SetWindowLong() to set a new message pump function to the listbox
using the GWL_WNDPROC constant and a delegate to my C# message pump function
as parameters.
When I put all code in a System.Windows.Forms.Form it works all right, but
when I create a separate custom control add place the SetWindowLong-call in
the constructor of the custom control I no longer receive any listbox-related
events.
(See code attached below)
Can anyone help me with this?

/Martin

namespace Custom
{
public delegate void PreSelectedIndexChangedHandler( object sender, ref
PreSelectedIndexChangedEventArgs e );

public class Listbox : System.Windows.Forms.ListBox
{
#region Win32 constants
#endregion

#region Win32 unmanaged library imports
#endregion

private static WindowsCallbackFunction MyWindowsCallbackFunction;
private static System.IntPtr OldWinProc;

private delegate UInt32 WindowsCallbackFunction( System.IntPtr Wnd,
UInt32 Msg, ushort WParam, UInt32 LParam );
public event PreSelectedIndexChangedHandler PreSelectedIndexChanged;

public Listbox(): base()
{
MyWindowsCallbackFunction = new WindowsCallbackFunction( WndProc );

OldWinProc = GetWindowLong( base.Handle, GWL_WNDPROC );
SetWindowLong( base.Handle, GWL_WNDPROC, MyWindowsCallbackFunction );
}

public UInt32 WndProc( System.IntPtr Wnd, UInt32 Msg, ushort WParam,
UInt32 LParam )
{
// Do stuff here
switch ( Msg )
{
case WM_LBUTTONDOWN:
uint xPos = LParam & 0xFFFF;
uint yPos = LParam >> 16;
int NewIndex = -1;
int p = 0;
// Find which list item thas was clicked (if any)
for (int i=this.TopIndex; i<this.Items.Count; i++)
{
if ( yPos >= p && yPos < p+this.GetItemHeight(i) )
{
NewIndex = i;
break;
}
else
{
p += this.GetItemHeight(i);
}
}
if ( NewIndex >= 0 && PreSelectedIndexChanged != null )
{
// Create event and ask all subscribers whether they accept the
change
// of the selected index
PreSelectedIndexChangedEventArgs Args = new
PreSelectedIndexChangedEventArgs( this.SelectedIndex, NewIndex );
PreSelectedIndexChanged( this, ref Args );
if ( Args.NewIndex != NewIndex )
{
// At least one subscriber to the PreSelectedIndexChanged event
// has requested that the selected index change should be
abandoned
return uint.MaxValue;
}
}
break;
default:
break;
}
return CallWindowProc( OldWinProc, Wnd, Msg, WParam, LParam );
}
}
 
A

Andrew Smith \(Infragistics\)

I suspect that the handle is getting recreated (e.g. when some properties
are set). Why don't you just override the WndProc method as opposed to
subclassing the window using pinvoke?
 
G

Guest

I guess I'm too used to the Win32 way of doing it and when I discovered that
WndProc didn't return a value I thought there was no way for my code to
report that the message had been handled.
I tried again in .Net with just a simple return without the call to the base
class WndProc and it seems to work all right. It was just to simple for me to
understand :-(

Thank you for your help!

/Martin
 

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