I am trying to customize default windows scrollbar in combobox like below.
public partial class ComboEx : ComboBox
{
internal ScrollbarEx vScrollBar;
NativeListWindow listControl;
public ComboEx()
{
InitializeComponent();
DropDownHeight = 100;
vScrollBar = new ScrollbarEx();
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
COMBOBOXINFO combInfo = new COMBOBOXINFO();
combInfo.cbSize = Marshal.SizeOf(combInfo);
Win32.GetComboBoxInfo( this.Handle, ref combInfo );
listControl = new NativeListWindow(this, combInfo.hwndList);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == (Win32.WM_REFLECT + Win32.WM_COMMAND))
{
if (Win32.HIWORD( (int)m.WParam ) == Win32.CBN_DROPDOWN)
{
COMBOBOXINFO combInfo = new COMBOBOXINFO();
combInfo.cbSize = Marshal.SizeOf(combInfo);
Win32.GetComboBoxInfo( this.Handle, ref combInfo );
vScrollBar.Location = new Point( this.Width-23, 1 );
vScrollBar.Size = new Size( 23, DropDownHeight );
vScrollBar.Visible = true;
Win32.SetParent(vScrollBar.Handle, combInfo.hwndList);
Win32.ShowWindow(vScrollBar.Handle, ShowWindowCommands.Show);
Win32.SetWindowPos(vScrollBar.Handle,HWND.TopMost, 155, 1, 23, 105, SetWindowPosFlags.SWP_SHOWWINDOW);
}
}
base.WndProc(ref m);
}
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
internal class NativeListWindow : NativeWindow
{
private const int WS_CHILD = 0x40000000,
WS_VISIBLE = 0x10000000,
WM_ACTIVATEAPP = 0x001C;
private int windowHandle;
private ComboEx parent;
public NativeListWindow(ComboEx owner,IntPtr handle)
{
AssignHandle(handle);
parent = owner;
}
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void OnHandleChange()
{
windowHandle = (int)this.Handle;
}
private void AdjustClientRect(ref RECT rect)
{
rect.right -= 23;
}
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message message)
{
switch (message.Msg)
{
case Win32.NCCALCSIZE:
{
if (message.WParam != IntPtr.Zero)
{
NCCALCSIZE_PARAMS rcsize = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(message.LParam, typeof(NCCALCSIZE_PARAMS));
AdjustClientRect(ref rcsize.rect0);
Marshal.StructureToPtr(rcsize, message.LParam, false);
}
else
{
RECT rcsize = (RECT)Marshal.PtrToStructure(message.LParam, typeof(RECT));
AdjustClientRect(ref rcsize);
Marshal.StructureToPtr(rcsize, message.LParam, false);
}
message.Result = new IntPtr(1);
return;
break;
}
case Win32.WM_NCMOUSEMOVE:
{
base.WndProc(ref message);
Win32.SendMessage(parent.vScrollBar.Handle, (uint)message.Msg, message.WParam, message.LParam);
break;
break;
}
case Win32.WM_NCLBUTTONDOWN:
{
base.WndProc(ref message);
Win32.SendMessage(parent.vScrollBar.Handle, (uint)message.Msg, message.WParam, message.LParam);
break;
}
case Win32.WM_NCACTIVATE:
{
base.WndProc(ref message);
Win32.SendMessage(parent.vScrollBar.Handle, (uint)message.Msg, message.WParam, message.LParam);
break;
}
case Win32.WM_NCMOUSELEAVE:
{
base.WndProc(ref message);
Win32.SendMessage(parent.vScrollBar.Handle, (uint)message.Msg, message.WParam, message.LParam);
break;
}
case Win32.WM_NCLBUTTONUP:
{
base.WndProc(ref message);
Win32.SendMessage(parent.vScrollBar.Handle, (uint)message.Msg, message.WParam, message.LParam);
break;
}
case Win32.WM_NCHITTEST:
{
base.WndProc(ref message);
Win32.SendMessage(parent.vScrollBar.Handle, (uint)message.Msg, message.WParam, message.LParam);
break;
}
case Win32.WM_MOUSEMOVE:
{
base.WndProc(ref message);
if ((int)message.LParam > 0)
{
int x = Win32.LOWORD((int)message.LParam);
int y = Win32.HIWORD((int)message.LParam);
RECT rect = new RECT(); ;
Win32.GetWindowRect(new HandleRef(parent.vScrollBar, parent.vScrollBar.Handle),out rect);
Rectangle rc = new Rectangle(parent.vScrollBar.Location.X, parent.vScrollBar.Location.Y,
(rect.right - rect.left), (rect.bottom - rect.top));
if (rc.Contains(new Point(x, y)))
{
Win32.SetFocus(parent.vScrollBar.Handle);
Win32.SetWindowPos(parent.vScrollBar.Handle, HWND.TopMost, 155, 1, 23, 105, SetWindowPosFlags.SWP_SHOWWINDOW);
Win32.SendMessage(parent.vScrollBar.Handle, (uint)message.Msg, message.WParam, message.LParam);
}
}
break;
}
}
base.WndProc(ref message);
}
}
}
class Win32
{
public const int WM_REFLECT = 0x2000;
public const int WM_COMMAND = 0x0111;
public const int CBN_DROPDOWN = 7;
public const int NCCALCSIZE = 0x0083;
public const int WM_NCPAINT = 0x0085;
public const int WM_NCMOUSEMOVE = 0x00A0;
public const int WM_NCLBUTTONDOWN = 0x00A1;
public const int WM_NCACTIVATE = 0x0086;
public const int WM_NCMOUSELEAVE = 0x02A2;
public const int WM_NCLBUTTONUP = 0x00A2; public const int WM_NCHITTEST = 0x0084; public const int WM_MOUSEMOVE = 0x0200;
public const int WM_VSCROLL = 0x115;
public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
public static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
public static readonly IntPtr HWND_TOP = new IntPtr(0);
public static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
[DllImport("user32.dll")]
public static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, Int32 wParam, Int32 lParam);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(HandleRef hwnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern IntPtr SetFocus(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetScrollInfo(IntPtr hwnd, int fnBar, ref SCROLLINFO lpsi);
public static int HIWORD(int n)
{
return (n >> 16) & 0xffff;
}
public static int LOWORD(int n)
{
return (n & 0xffff);
}
}
public enum ShowWindowCommands : int
{
Hide = 0,
Normal = 1,
ShowMinimized = 2,
Maximize = 3,
ShowMaximized = 3,
ShowNoActivate = 4,
Show = 5,
Minimize = 6,
ShowMinNoActive = 7,
ShowNA = 8,
Restore = 9,
ShowDefault = 10,
ForceMinimize = 11
}
[StructLayout(LayoutKind.Sequential)]
public struct COMBOBOXINFO
{
public Int32 cbSize;
public RECT rcItem;
public RECT rcButton;
public ComboBoxButtonState buttonState;
public IntPtr hwndCombo;
public IntPtr hwndEdit;
public IntPtr hwndList;
}
public enum ComboBoxButtonState
{
STATE_SYSTEM_NONE = 0,
STATE_SYSTEM_INVISIBLE = 0x00008000,
STATE_SYSTEM_PRESSED = 0x00000008
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left, top, right, bottom;
}
public static class HWND
{
public static IntPtr
NoTopMost = new IntPtr(-2),
TopMost = new IntPtr(-1),
Top = new IntPtr(0),
Bottom = new IntPtr(1);
}
public static class SWP
{
public static readonly int
NOSIZE = 0x0001,
NOMOVE = 0x0002,
NOZORDER = 0x0004,
NOREDRAW = 0x0008,
NOACTIVATE = 0x0010,
DRAWFRAME = 0x0020,
FRAMECHANGED = 0x0020,
SHOWWINDOW = 0x0040,
HIDEWINDOW = 0x0080,
NOCOPYBITS = 0x0100,
NOOWNERZORDER = 0x0200,
NOREPOSITION = 0x0200,
NOSENDCHANGING = 0x0400,
DEFERERASE = 0x2000,
ASYNCWINDOWPOS = 0x4000;
}
public enum SpecialWindowHandles
{
HWND_TOP = 0,
HWND_BOTTOM = 1,
HWND_TOPMOST = -1,
HWND_NOTOPMOST = -2
}
[Flags]
public enum SetWindowPosFlags : uint
{
SWP_ASYNCWINDOWPOS = 0x4000,
SWP_DEFERERASE = 0x2000,
SWP_DRAWFRAME = 0x0020,
SWP_FRAMECHANGED = 0x0020,
SWP_HIDEWINDOW = 0x0080,
SWP_NOACTIVATE = 0x0010,
SWP_NOCOPYBITS = 0x0100,
SWP_NOMOVE = 0x0002,
SWP_NOOWNERZORDER = 0x0200,
SWP_NOREDRAW = 0x0008,
SWP_NOREPOSITION = 0x0200,
SWP_NOSENDCHANGING = 0x0400,
SWP_NOSIZE = 0x0001,
SWP_NOZORDER = 0x0004,
SWP_SHOWWINDOW = 0x0040,
}
public struct NCCALCSIZE_PARAMS
{
public RECT rect0;
public RECT rect1;
public RECT rect2;
public IntPtr lppos;
}
[Serializable, StructLayout(LayoutKind.Sequential)]
struct SCROLLINFO
{
public int cbSize;
public uint fMask;
public int nMin;
public int nMax;
public int nPage;
public int nPos;
public int nTrackPos;
}
public enum SBOrientation : int
{
SB_HORZ = 0x0,
SB_VERT = 0x1,
SB_CTL = 0x2,
SB_BOTH = 0x3
}
public enum ScrollInfoMask : uint
{
SIF_RANGE = 0x1,
SIF_PAGE = 0x2,
SIF_POS = 0x4,
SIF_DISABLENOSCROLL = 0x8,
SIF_TRACKPOS = 0x10,
SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS),
}
public partial class ScrollbarEx : VScrollBar
{
public ScrollbarEx()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
this.Focus();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
}
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
}
protected override void OnLostFocus(EventArgs e)
{
base.OnLostFocus(e);
}
}
In the above code, I did the following
1. created a NativeWindow to catch the messages of combobox listcontrol by assigning combInfo.hwndList handle.
2. Placed my custom scrollbar(ScrollBarEx) in the non-client area of combobox listcontrol.
But my custom scrollbar(ScrollBarEx) doesn't receives any messages or focus. It looks like it is dead. Please look into this code and share some idea to make the scrollbar live.
[EDIT]
All classes added.