using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Runtime.InteropServices;

class UltraNavScrollHelper:Component
{
    private Control _hScrollBar;
    private Control _vScrollBar;
    private Control _controlSite;
    private NativeScrollBar _nsb;
    private NativeWndProcListner _npl;
    private bool _created;

    public UltraNavScrollHelper(IContainer container) {
        container.Add(this);
    }

    public UltraNavScrollHelper(Control control, Control horizontalScrollBar, Control verticalScrollBar)
    {
        this.Control = control;
        this.HorizontalScrollBar = horizontalScrollBar;
        this.VerticalScrollBar = verticalScrollBar;
    }

    protected override void Dispose(bool disposing)
    {
        if (_controlSite != null)
        {
            _controlSite.HandleCreated -= new EventHandler(_controlSite_HandleCreated);
            _controlSite.HandleDestroyed -= new EventHandler(_controlSite_HandleDestroyed);
            _controlSite = null;
            DestroyHandle();
        }

        base.Dispose(disposing);
    }

    public Control Control
    {
        get { return _controlSite; }
        set
        {
            if (value != null && value != _controlSite)
            {
                _controlSite = value;
                _controlSite.HandleCreated += new EventHandler(_controlSite_HandleCreated);
                _controlSite.HandleDestroyed += new EventHandler(_controlSite_HandleDestroyed);
            }
        }
    }

    public Control HorizontalScrollBar
    {
        get { return _hScrollBar; }
        set
        {
            _hScrollBar = value;
            CreateHandle();
        }
    }

    public Control VerticalScrollBar
    {
        get { return _vScrollBar; }
        set
        {
            _vScrollBar = value;
            CreateHandle();
        }
    }

    private void CreateHandle()
    {
        if (_created) return;

        if ((this.Site == null || this.DesignMode == false) && (this._vScrollBar != null || this._hScrollBar != null))
        {
            this._nsb = new NativeScrollBar(_controlSite);
            this._npl = new NativeWndProcListner(this, _controlSite);
            _created = true;
        }
    }
    private void DestroyHandle()
    {
        if (this._nsb != null)
        {
            this._nsb.DestroyHandle();
            this._npl.DestroyHandle();
            _created = false;
        }
    }

    private void _controlSite_HandleCreated(object sender, EventArgs e)
    {
        CreateHandle();
    }

    private void _controlSite_HandleDestroyed(object sender, EventArgs e)
    {
        DestroyHandle();
    }

    internal class NativeWndProcListner : NativeWindow
    {
        private Control _toListen;
        private UltraNavScrollHelper _owner;

        private const int WM_HSCROLL = 0x0114,
                          WM_VSCROLL = 0x0115;

        private class ControlHelper : Control
        {
            internal static void ReflectMessageHelper(IntPtr hWnd, ref Message m)
            {
                Control.ReflectMessage(hWnd, ref m);
            }
        }

        public NativeWndProcListner(UltraNavScrollHelper owner, Control toListenTo)
        {
            _toListen = toListenTo;
            _owner = owner;
            if (_toListen.IsHandleCreated)
            {
                this.AssignHandle(_toListen.Handle);
            }
        }

        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_VSCROLL:
                    if (_owner.VerticalScrollBar != null  &&
                        m.LParam != _owner.VerticalScrollBar.Handle
                        && _owner.VerticalScrollBar.Visible)
                    {
                        ControlHelper.ReflectMessageHelper(_owner.VerticalScrollBar.Handle, ref m);
                    }
                    break;
                case WM_HSCROLL:
                    if (_owner.HorizontalScrollBar != null &&
                        m.LParam != _owner.HorizontalScrollBar.Handle
                        && _owner.HorizontalScrollBar.Visible)
                    {
                        ControlHelper.ReflectMessageHelper(_owner.HorizontalScrollBar.Handle, ref m);
                    }
                    break;
            }

            base.WndProc(ref m);
        }
    }
    internal class NativeScrollBar : NativeWindow
    {
        public NativeScrollBar(Control parent)
        {

            CreateParams cp = new CreateParams();

            // Fill in the CreateParams details.
            cp.Caption = String.Empty;
            cp.ClassName = "SCROLLBAR";

            // Set the position of the scrollbar so that it is offscreen
            cp.X = -1000;
            cp.Y = -1000;
            cp.Height = 100;
            cp.Width = 100;

            // Create as a child of the specified parent
            cp.Parent = parent.Handle;
            cp.Style = WS_VISIBLE | SBS_VERT | WS_CHILD;

            IntPtr modHandle = NativeScrollBar.GetModuleHandle(null);

            IntPtr handleCreated = IntPtr.Zero;
            int lastWin32Error = 0;
            try
            {
                // Create the actual scrollbar window
                handleCreated = NativeScrollBar.CreateWindowEx(cp.ExStyle, cp.ClassName,
                                                                  cp.Caption, cp.Style, cp.X, cp.Y, cp.Width, cp.Height, new HandleRef(cp, cp.Parent), new HandleRef(null, IntPtr.Zero),
                                                                  new HandleRef(null, modHandle), cp.Param);
                lastWin32Error = Marshal.GetLastWin32Error();
            }
            catch (NullReferenceException e)
            {
                throw new OutOfMemoryException("Could not create Native Window");
            }
            if (handleCreated == IntPtr.Zero)
            {
                throw new Win32Exception(lastWin32Error, "System error creating Native Window");
            }

            // Now assign the NativeWindow handle to the native scroll bar
            this.AssignHandle(handleCreated);

        }

        // Constant values were found in the "windows.h" header file.
        private const int WS_CHILD = 0x40000000,
                          WS_VISIBLE = 0x10000000,
                          SBS_HORZ = 0x0000,
                          SBS_VERT = 0x0001;
        [DllImport("USER32.DLL", EntryPoint = "CreateWindowEx", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CreateWindowEx(int dwExStyle, string lpszClassName,
                                                   string lpszWindowName, int style, int x, int y, int width, int height,
                                                   HandleRef hWndParent, HandleRef hMenu, HandleRef hInst, [MarshalAs(UnmanagedType.AsAny)] object pvParam);
        [DllImport("KERNEL32.DLL", CharSet = CharSet.Auto)]
        private static extern IntPtr GetModuleHandle(string modName);


    }
 
}