using System; using System.Collections.Generic; using System.ComponentModel; using System.Text; using System.Windows.Forms; using DevComponents.DotNetBar.ScrollBar; using System.Drawing; using System.Runtime.InteropServices; namespace DevComponents.DotNetBar.Controls { internal class ScrollbarSkinner : IDisposable { #region Constructor private const int ScrollPositionUpdateDelay = 400; private HScrollBarAdv _HScrollBar = null; private VScrollBarAdv _VScrollBar = null; private Control _Thumb = null; private Control _Parent = null; public ScrollbarSkinner(Control parent) { if(parent == null) throw new ArgumentNullException("Parent cannot be null"); if (!(parent is IScrollBarOverrideSupport)) throw new ArgumentException("parent must implement IScrollBarOverrideSupport interface."); _Parent = parent; _HScrollBar = new HScrollBarAdv(); _HScrollBar.Visible = false; _HScrollBar.Appearance = _ScrollBarAppearance; _HScrollBar.Scroll += HScrollBarScroll; _VScrollBar = new VScrollBarAdv(); _VScrollBar.Visible = false; _VScrollBar.Appearance = _ScrollBarAppearance; _VScrollBar.Scroll += VScrollBarScroll; _Thumb = new Control(); _Thumb.Visible = false; _Thumb.BackColor = SystemColors.Window; if (_Parent.Parent != null) { AttachScrollbars(); } WireParent(); if(_Parent.IsHandleCreated) UpdateScrollBars(); } #endregion #region Implementation private void WireParent() { _Parent.ParentChanged += ParentParentChanged; _Parent.HandleCreated += ParentHandleCreated; _Parent.Resize += ParentResize; IScrollBarOverrideSupport sbo = (IScrollBarOverrideSupport)_Parent; sbo.NonClientSizeChanged += NonClientSizeChanged; sbo.ScrollBarValueChanged += ControlScrollBarValueChanged; } void ParentResize(object sender, EventArgs e) { UpdateScrollBars(); } void ParentHandleCreated(object sender, EventArgs e) { UpdateScrollBars(); } private void NonClientSizeChanged(object sender, EventArgs e) { UpdateScrollBars(); } private void ControlScrollBarValueChanged(object sender, ScrollValueChangedEventArgs e) { if ((e.ScrollChange & eScrollBarScrollChange.Vertical) == eScrollBarScrollChange.Vertical) { UpdateVerticalScrollBarValues(); } if ((e.ScrollChange & eScrollBarScrollChange.MouseWheel) == eScrollBarScrollChange.MouseWheel) { if (_VScrollBar.Visible) { UpdateVerticalScrollBarValues(); InvokeDelayed(new MethodInvoker(delegate { UpdateVerticalScrollBarValues(); }), ScrollPositionUpdateDelay); } else if (_HScrollBar.Visible) { UpdateHorizontalScrollBarValues(); InvokeDelayed(new MethodInvoker(delegate { UpdateHorizontalScrollBarValues(); }), ScrollPositionUpdateDelay); } } if ((e.ScrollChange & eScrollBarScrollChange.Horizontal) == eScrollBarScrollChange.Horizontal) { UpdateHorizontalScrollBarValues(); } UpdateScrollBars(); } private void UnwireParent() { _Parent.ParentChanged -= ParentParentChanged; _Parent.HandleCreated -= ParentHandleCreated; _Parent.Resize -= ParentResize; IScrollBarOverrideSupport sbo = (IScrollBarOverrideSupport)_Parent; sbo.NonClientSizeChanged -= NonClientSizeChanged; sbo.ScrollBarValueChanged -= ControlScrollBarValueChanged; } void ParentParentChanged(object sender, EventArgs e) { AttachScrollbars(); UpdateScrollBars(); } private bool _UpdatingScrollbars = false; protected virtual void UpdateScrollBarsDelayed() { InvokeDelayed(new MethodInvoker(delegate { UpdateScrollBars(); }), ScrollPositionUpdateDelay); } protected virtual void UpdateScrollBars() { if (_Parent == null || _Parent.Parent == null || !_Parent.IsHandleCreated || _UpdatingScrollbars) return; _UpdatingScrollbars = true; try { Control scrollOverrideControl = _Parent; if (scrollOverrideControl == null || ((IScrollBarOverrideSupport)scrollOverrideControl).DesignMode) { if (_HScrollBar.Visible) _HScrollBar.Visible = false; if (_VScrollBar.Visible) _VScrollBar.Visible = false; return; } WinApi.SCROLLBARINFO psbi = new WinApi.SCROLLBARINFO(); psbi.cbSize = Marshal.SizeOf(psbi); WinApi.GetScrollBarInfo(scrollOverrideControl.Handle, (uint)WinApi.eObjectId.OBJID_VSCROLL, ref psbi); if (psbi.rgstate[0] != (int)WinApi.eStateFlags.STATE_SYSTEM_INVISIBLE) { Rectangle vsBounds = psbi.rcScrollBar.ToRectangle(); vsBounds.Location = scrollOverrideControl.Parent.PointToClient(vsBounds.Location); Rectangle scrollCountrolBounds = scrollOverrideControl.Bounds; if (!scrollCountrolBounds.Contains(vsBounds)) { // We need to guess bounds for best performance and appearance if (vsBounds.Right > scrollCountrolBounds.Right) { vsBounds.X = scrollCountrolBounds.Right - vsBounds.Width; } else if (vsBounds.X < scrollCountrolBounds.X) { vsBounds.X = 0; } } if (_VScrollBar.Bounds != vsBounds) { _VScrollBar.Bounds = vsBounds; UpdateVerticalScrollBarValues(); } if (!_VScrollBar.Visible) { _VScrollBar.Visible = true; _VScrollBar.BringToFront(); _VScrollBar.Refresh(); InvokeDelayed(new MethodInvoker(delegate { UpdateVerticalScrollBarValues(); }), ScrollPositionUpdateDelay); } else { _VScrollBar.BringToFront(); } if (psbi.rgstate[0] == (int)WinApi.eStateFlags.STATE_SYSTEM_UNAVAILABLE) _VScrollBar.Enabled = false; else if (!_VScrollBar.Enabled) _VScrollBar.Enabled = true; //Console.WriteLine("VscrollBar Bounds detection {0}", vsBounds); } else if (_VScrollBar.Visible) _VScrollBar.Visible = false; psbi = new WinApi.SCROLLBARINFO(); psbi.cbSize = Marshal.SizeOf(psbi); WinApi.GetScrollBarInfo(scrollOverrideControl.Handle, (uint)WinApi.eObjectId.OBJID_HSCROLL, ref psbi); if (psbi.rgstate[0] != (int)WinApi.eStateFlags.STATE_SYSTEM_INVISIBLE) { Rectangle hsBounds = psbi.rcScrollBar.ToRectangle(); hsBounds.Location = scrollOverrideControl.Parent.PointToClient(hsBounds.Location); Rectangle scrollCountrolBounds = scrollOverrideControl.Bounds; if (!scrollCountrolBounds.Contains(hsBounds)) { // We need to guess bounds for best performance and appearance if (hsBounds.Bottom > scrollCountrolBounds.Bottom) { hsBounds.Y = scrollCountrolBounds.Bottom - hsBounds.Height; } } if (_VScrollBar.Visible && hsBounds.Width == scrollCountrolBounds.Width) hsBounds.Width -= _VScrollBar.Width; if (_HScrollBar.Bounds != hsBounds) { _HScrollBar.Bounds = hsBounds; UpdateHorizontalScrollBarValues(); } if (!_HScrollBar.Visible) { _HScrollBar.Visible = true; _HScrollBar.BringToFront(); _HScrollBar.Refresh(); InvokeDelayed(new MethodInvoker(delegate { UpdateHorizontalScrollBarValues(); }), ScrollPositionUpdateDelay); } else { _HScrollBar.BringToFront(); } if (psbi.rgstate[0] == (int)WinApi.eStateFlags.STATE_SYSTEM_UNAVAILABLE) _HScrollBar.Enabled = false; else if (!_HScrollBar.Enabled) _HScrollBar.Enabled = true; } else if (_HScrollBar.Visible) _HScrollBar.Visible = false; if (_HScrollBar.Visible && _VScrollBar.Visible) { _Thumb.Bounds = new Rectangle(_VScrollBar.Left, _VScrollBar.Bounds.Bottom, _VScrollBar.Width, _HScrollBar.Height); _Thumb.Visible = true; _Thumb.BringToFront(); } else { _Thumb.Visible = false; } } finally { _UpdatingScrollbars = false; } } private void AttachScrollbars() { DetachScrollbars(); Control host = _Parent.Parent; if (host != null) { if (host is TableLayoutPanel) { while (host is TableLayoutPanel) { host = host.Parent; } } if (host != null) { host.Controls.Add(_VScrollBar); host.Controls.Add(_HScrollBar); host.Controls.Add(_Thumb); } } } private void DetachScrollbars() { if (_VScrollBar.Parent != null) _VScrollBar.Parent.Controls.Remove(_VScrollBar); if (_HScrollBar.Parent != null) _HScrollBar.Parent.Controls.Remove(_HScrollBar); if (_Thumb.Parent != null) _Thumb.Parent.Controls.Remove(_Thumb); _VScrollBar.Visible = false; _HScrollBar.Visible = false; _Thumb.Visible = false; } private eScrollBarAppearance _ScrollBarAppearance = eScrollBarAppearance.Default; /// /// Gets or sets the scroll-bar visual style. /// [DefaultValue(eScrollBarAppearance.Default), Category("Appearance"), Description("Gets or sets the scroll-bar visual style.")] public eScrollBarAppearance ScrollBarAppearance { get { return _ScrollBarAppearance; } set { _ScrollBarAppearance = value; OnScrollBarAppearanceChanged(); } } private void OnScrollBarAppearanceChanged() { if (_VScrollBar != null) _VScrollBar.Appearance = _ScrollBarAppearance; if (_HScrollBar != null) _HScrollBar.Appearance = _ScrollBarAppearance; } private bool _InternalVScrollPositionUpdated = false; private void VScrollBarScroll(object sender, ScrollEventArgs e) { if (e.NewValue == e.OldValue && e.Type != ScrollEventType.EndScroll) return; _InternalVScrollPositionUpdated = true; //Console.WriteLine("{0} Setting Sys_VScrollBar value {1}, {2}", DateTime.Now, e.NewValue, e.Type); if (e.Type == ScrollEventType.ThumbTrack) // We need to send this becouse ScrollableControl internally will get the current scroll position using GetScrollInfo instead from window message LParam { WinApi.SCROLLINFO si = new WinApi.SCROLLINFO(); si.nTrackPos = e.NewValue; si.nPos = e.NewValue; si.fMask = WinApi.ScrollInfoMask.SIF_POS; si.cbSize = Marshal.SizeOf(si); WinApi.SetScrollInfo(_Parent.Handle, WinApi.SBOrientation.SB_VERT, ref si, false); } WinApi.SendMessage(_Parent.Handle, (int)WinApi.WindowsMessages.WM_VSCROLL, WinApi.CreateLParam(MapVScrollType(e.Type), e.NewValue), IntPtr.Zero); _InternalVScrollPositionUpdated = false; if (e.Type == ScrollEventType.EndScroll) UpdateVerticalScrollBarValues(); } private bool _InternalHScrollPositionUpdated = false; private void HScrollBarScroll(object sender, ScrollEventArgs e) { if (e.NewValue == e.OldValue && e.Type != ScrollEventType.EndScroll) return; _InternalHScrollPositionUpdated = true; //Console.WriteLine("{0} Setting Sys_HScrollBar value {1}, {2}", DateTime.Now, e.NewValue, e.Type); if (e.Type == ScrollEventType.ThumbTrack) // We need to send this becouse ScrollableControl internally will get the current scroll position using GetScrollInfo instead from window message LParam { WinApi.SCROLLINFO si = new WinApi.SCROLLINFO(); si.nTrackPos = e.NewValue; si.nPos = e.NewValue; si.fMask = WinApi.ScrollInfoMask.SIF_POS; si.cbSize = Marshal.SizeOf(si); WinApi.SetScrollInfo(_Parent.Handle, WinApi.SBOrientation.SB_HORZ, ref si, false); } WinApi.SendMessage(_Parent.Handle, (int)WinApi.WindowsMessages.WM_HSCROLL, WinApi.CreateLParam(MapHScrollType(e.Type), e.NewValue), IntPtr.Zero); _InternalHScrollPositionUpdated = false; if (e.Type == ScrollEventType.EndScroll) UpdateHorizontalScrollBarValues(); } private static int MapVScrollType(ScrollEventType type) { if (type == ScrollEventType.EndScroll) return (int)WinApi.ScrollBarCommands.SB_ENDSCROLL; else if (type == ScrollEventType.First) return (int)WinApi.ScrollBarCommands.SB_TOP; else if (type == ScrollEventType.LargeDecrement) return (int)WinApi.ScrollBarCommands.SB_PAGEUP; else if (type == ScrollEventType.LargeIncrement) return (int)WinApi.ScrollBarCommands.SB_PAGEDOWN; else if (type == ScrollEventType.Last) return (int)WinApi.ScrollBarCommands.SB_BOTTOM; else if (type == ScrollEventType.SmallDecrement) return (int)WinApi.ScrollBarCommands.SB_LINEUP; else if (type == ScrollEventType.SmallIncrement) return (int)WinApi.ScrollBarCommands.SB_LINEDOWN; else if (type == ScrollEventType.ThumbPosition) return (int)WinApi.ScrollBarCommands.SB_THUMBPOSITION; else if (type == ScrollEventType.ThumbTrack) return (int)WinApi.ScrollBarCommands.SB_THUMBTRACK; return 0; } private static int MapHScrollType(ScrollEventType type) { if (type == ScrollEventType.EndScroll) return (int)WinApi.ScrollBarCommands.SB_ENDSCROLL; else if (type == ScrollEventType.First) return (int)WinApi.ScrollBarCommands.SB_LEFT; else if (type == ScrollEventType.LargeDecrement) return (int)WinApi.ScrollBarCommands.SB_PAGELEFT; else if (type == ScrollEventType.LargeIncrement) return (int)WinApi.ScrollBarCommands.SB_PAGERIGHT; else if (type == ScrollEventType.Last) return (int)WinApi.ScrollBarCommands.SB_RIGHT; else if (type == ScrollEventType.SmallDecrement) return (int)WinApi.ScrollBarCommands.SB_LINELEFT; else if (type == ScrollEventType.SmallIncrement) return (int)WinApi.ScrollBarCommands.SB_LINERIGHT; else if (type == ScrollEventType.ThumbPosition) return (int)WinApi.ScrollBarCommands.SB_THUMBPOSITION; else if (type == ScrollEventType.ThumbTrack) return (int)WinApi.ScrollBarCommands.SB_THUMBTRACK; return 0; } private void UpdateVerticalScrollBarValues() { if (_InternalVScrollPositionUpdated) return; WinApi.SCROLLINFO scrollInfo = new WinApi.SCROLLINFO(); scrollInfo.cbSize = Marshal.SizeOf(scrollInfo); scrollInfo.fMask = WinApi.ScrollInfoMask.SIF_POS | WinApi.ScrollInfoMask.SIF_RANGE | WinApi.ScrollInfoMask.SIF_PAGE; if (WinApi.GetScrollInfo(_Parent.Handle, WinApi.SBOrientation.SB_VERT, ref scrollInfo)) { //Console.WriteLine("{0} scrollInfo={1}", DateTime.Now, scrollInfo.ToString()); if (_VScrollBar.Minimum != scrollInfo.nMin) _VScrollBar.Minimum = scrollInfo.nMin; if (_VScrollBar.Maximum != scrollInfo.nMax) _VScrollBar.Maximum = scrollInfo.nMax; if (_VScrollBar.Value != scrollInfo.nPos) _VScrollBar.Value = scrollInfo.nPos; if (_VScrollBar.LargeChange != scrollInfo.nPage) _VScrollBar.LargeChange = scrollInfo.nPage; } } private void UpdateHorizontalScrollBarValues() { if (_InternalHScrollPositionUpdated) return; WinApi.SCROLLINFO scrollInfo = new WinApi.SCROLLINFO(); scrollInfo.cbSize = Marshal.SizeOf(scrollInfo); scrollInfo.fMask = WinApi.ScrollInfoMask.SIF_POS | WinApi.ScrollInfoMask.SIF_RANGE | WinApi.ScrollInfoMask.SIF_PAGE | WinApi.ScrollInfoMask.SIF_TRACKPOS; if (WinApi.GetScrollInfo(_Parent.Handle, WinApi.SBOrientation.SB_HORZ, ref scrollInfo)) { //Console.WriteLine("{0} TRACKPOS={1}", DateTime.Now, scrollInfo); if (_HScrollBar.Minimum != scrollInfo.nMin) _HScrollBar.Minimum = scrollInfo.nMin; if (_HScrollBar.Maximum != scrollInfo.nMax) _HScrollBar.Maximum = scrollInfo.nMax; if (_HScrollBar.Value != scrollInfo.nPos) _HScrollBar.Value = scrollInfo.nPos; if (_HScrollBar.LargeChange != scrollInfo.nPage) _HScrollBar.LargeChange = scrollInfo.nPage; } } public VScrollBarAdv VScrollBar { get { return _VScrollBar; } } public HScrollBarAdv HScrollBar { get { return _HScrollBar; } } #endregion #region Invoke Delayed protected void InvokeDelayed(MethodInvoker method) { InvokeDelayed(method, 10); } protected void InvokeDelayed(MethodInvoker method, int delayInterval) { if (delayInterval <= 0) { method.Invoke(); return; } Timer delayedInvokeTimer = new Timer(); delayedInvokeTimer = new Timer(); delayedInvokeTimer.Tag = method; delayedInvokeTimer.Interval = delayInterval; delayedInvokeTimer.Tick += new EventHandler(DelayedInvokeTimerTick); delayedInvokeTimer.Start(); } void DelayedInvokeTimerTick(object sender, EventArgs e) { Timer timer = (Timer)sender; MethodInvoker method = (MethodInvoker)timer.Tag; timer.Stop(); timer.Dispose(); if (!this.IsDisposed) method.Invoke(); } #endregion private bool _IsDisposed = false; public bool IsDisposed { get { return _IsDisposed; } } public void Dispose() { UnwireParent(); if(_VScrollBar.Parent == null) _VScrollBar.Dispose(); if (_HScrollBar.Parent == null) _HScrollBar.Dispose(); if (_Thumb.Parent == null) _Thumb.Dispose(); _IsDisposed = true; } } }