Click here to Skip to main content
15,884,099 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I am trying to customize default windows scrollbar in combobox like below.

C#
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);
    }

    // MyNativeWindow class to create a window given a class name.
    [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
    internal class NativeListWindow : NativeWindow
    {
        // Constant values were found in the "windows.h" header file.
        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;

        }

        // Listen to when the handle changes to keep the variable in sync
        [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)
        {
            // Listen for messages that are sent to the button window. Some messages are sent 
            // to the parent window instead of the button's window. 

            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;//0x00A1
        public const int WM_NCACTIVATE = 0x0086;
        public const int WM_NCMOUSELEAVE = 0x02A2;
        public const int WM_NCLBUTTONUP = 0x00A2;////0xA2
        public const int WM_NCHITTEST = 0x0084;////0xA2
        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
    {
        /// <summary>
        /// Hides the window and activates another window.
        /// </summary>
        Hide = 0,
        /// <summary>
        /// Activates and displays a window. If the window is minimized or 
        /// maximized, the system restores it to its original size and position.
        /// An application should specify this flag when displaying the window 
        /// for the first time.
        /// </summary>
        Normal = 1,
        /// <summary>
        /// Activates the window and displays it as a minimized window.
        /// </summary>
        ShowMinimized = 2,
        /// <summary>
        /// Maximizes the specified window.
        /// </summary>
        Maximize = 3, // is this the right value?
        /// <summary>
        /// Activates the window and displays it as a maximized window.
        /// </summary>       
        ShowMaximized = 3,
        /// <summary>
        /// Displays a window in its most recent size and position. This value 
        /// is similar to <see cref="Win32.ShowWindowCommand.Normal"/>, except 
        /// the window is not activated.
        /// </summary>
        ShowNoActivate = 4,
        /// <summary>
        /// Activates the window and displays it in its current size and position. 
        /// </summary>
        Show = 5,
        /// <summary>
        /// Minimizes the specified window and activates the next top-level 
        /// window in the Z order.
        /// </summary>
        Minimize = 6,
        /// <summary>
        /// Displays the window as a minimized window. This value is similar to
        /// <see cref="Win32.ShowWindowCommand.ShowMinimized"/>, except the 
        /// window is not activated.
        /// </summary>
        ShowMinNoActive = 7,
        /// <summary>
        /// Displays the window in its current size and position. This value is 
        /// similar to <see cref="Win32.ShowWindowCommand.Show"/>, except the 
        /// window is not activated.
        /// </summary>
        ShowNA = 8,
        /// <summary>
        /// Activates and displays the window. If the window is minimized or 
        /// maximized, the system restores it to its original size and position. 
        /// An application should specify this flag when restoring a minimized window.
        /// </summary>
        Restore = 9,
        /// <summary>
        /// Sets the show state based on the SW_* value specified in the 
        /// STARTUPINFO structure passed to the CreateProcess function by the 
        /// program that started the application.
        /// </summary>
        ShowDefault = 10,
        /// <summary>
        ///  Windows 2000/XP: Minimizes a window, even if the thread 
        /// that owns the window is not responding. This flag should only be 
        /// used when minimizing windows from a different thread.
        /// </summary>
        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;
    }
    /// <summary>
    /// Window handles (HWND) used for hWndInsertAfter
    /// </summary>
    public static class HWND
    {
        public static IntPtr
        NoTopMost = new IntPtr(-2),
        TopMost = new IntPtr(-1),
        Top = new IntPtr(0),
        Bottom = new IntPtr(1);
    }

    /// <summary>
    /// SetWindowPos Flags
    /// </summary>
    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;
    }
    /// <summary>
    ///     Special window handles
    /// </summary>
    public enum SpecialWindowHandles
    {
        // ReSharper disable InconsistentNaming
        /// <summary>
        ///     Places the window at the bottom of the Z order. If the hWnd parameter identifies a topmost window, the window loses its topmost status and is placed at the bottom of all other windows.
        /// </summary>
        HWND_TOP = 0,
        /// <summary>
        ///     Places the window above all non-topmost windows (that is, behind all topmost windows). This flag has no effect if the window is already a non-topmost window.
        /// </summary>
        HWND_BOTTOM = 1,
        /// <summary>
        ///     Places the window at the top of the Z order.
        /// </summary>
        HWND_TOPMOST = -1,
        /// <summary>
        ///     Places the window above all non-topmost windows. The window maintains its topmost position even when it is deactivated.
        /// </summary>
        HWND_NOTOPMOST = -2
        // ReSharper restore InconsistentNaming
    }

    [Flags]
    public enum SetWindowPosFlags : uint
    {
        // ReSharper disable InconsistentNaming

        /// <summary>
        ///     If the calling thread and the thread that owns the window are attached to different input queues, the system posts the request to the thread that owns the window. This prevents the calling thread from blocking its execution while other threads process the request.
        /// </summary>
        SWP_ASYNCWINDOWPOS = 0x4000,

        /// <summary>
        ///     Prevents generation of the WM_SYNCPAINT message.
        /// </summary>
        SWP_DEFERERASE = 0x2000,

        /// <summary>
        ///     Draws a frame (defined in the window's class description) around the window.
        /// </summary>
        SWP_DRAWFRAME = 0x0020,

        /// <summary>
        ///     Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE is sent only when the window's size is being changed.
        /// </summary>
        SWP_FRAMECHANGED = 0x0020,

        /// <summary>
        ///     Hides the window.
        /// </summary>
        SWP_HIDEWINDOW = 0x0080,

        /// <summary>
        ///     Does not activate the window. If this flag is not set, the window is activated and moved to the top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter parameter).
        /// </summary>
        SWP_NOACTIVATE = 0x0010,

        /// <summary>
        ///     Discards the entire contents of the client area. If this flag is not specified, the valid contents of the client area are saved and copied back into the client area after the window is sized or repositioned.
        /// </summary>
        SWP_NOCOPYBITS = 0x0100,

        /// <summary>
        ///     Retains the current position (ignores X and Y parameters).
        /// </summary>
        SWP_NOMOVE = 0x0002,

        /// <summary>
        ///     Does not change the owner window's position in the Z order.
        /// </summary>
        SWP_NOOWNERZORDER = 0x0200,

        /// <summary>
        ///     Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent window uncovered as a result of the window being moved. When this flag is set, the application must explicitly invalidate or redraw any parts of the window and parent window that need redrawing.
        /// </summary>
        SWP_NOREDRAW = 0x0008,

        /// <summary>
        ///     Same as the SWP_NOOWNERZORDER flag.
        /// </summary>
        SWP_NOREPOSITION = 0x0200,

        /// <summary>
        ///     Prevents the window from receiving the WM_WINDOWPOSCHANGING message.
        /// </summary>
        SWP_NOSENDCHANGING = 0x0400,

        /// <summary>
        ///     Retains the current size (ignores the cx and cy parameters).
        /// </summary>
        SWP_NOSIZE = 0x0001,

        /// <summary>
        ///     Retains the current Z order (ignores the hWndInsertAfter parameter).
        /// </summary>
        SWP_NOZORDER = 0x0004,

        /// <summary>
        ///     Displays the window.
        /// </summary>
        SWP_SHOWWINDOW = 0x0040,

        // ReSharper restore InconsistentNaming
    }

    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.
Posted
Updated 1-Mar-13 20:58pm
v2
Comments
Thomas Duwe 26-Feb-13 3:45am    
If someone would like to look into your code and test it, this someone needs all the referenced classes and structs etc....
Kaizen202 2-Mar-13 2:59am    
I have added all referenced classes. Please look into this code and help.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900