1342 lines
54 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
using DevComponents.DotNetBar.ScrollBar;
namespace DevComponents.DotNetBar.Controls
{
[ToolboxItem(true), Description("Page Slider Control with Touch Support")]
[Designer("DevComponents.DotNetBar.Design.PageSliderDesigner, DevComponents.DotNetBar.Design, Version=14.1.0.37, Culture=neutral, PublicKeyToken=90f470f34c89ccaf")]
[DefaultEvent("SelectedPageChanged"), DefaultProperty("SelectedPageIndex")]
[ToolboxBitmap(typeof(StepIndicator), "Controls.PageSlider.ico")]
public class PageSlider : Control
{
#region Constructor
private StepIndicator _StepIndicator;
private Touch.TouchHandler _TouchHandler = null;
private HScrollBarAdv _HorizontalScrollBar = null;
private VScrollBarAdv _VerticalScrollBar = null;
/// <summary>
/// Initializes a new instance of the PageSlider class.
/// </summary>
public PageSlider()
{
_StepIndicator = new StepIndicator();
_StepIndicator.Dock = DockStyle.Top;
this.Controls.Add(_StepIndicator);
if (BarFunctions.IsWindows7 && Touch.TouchHandler.IsTouchEnabled)
{
_TouchHandler = new DevComponents.DotNetBar.Touch.TouchHandler(this);
_TouchHandler.PanBegin += new EventHandler<DevComponents.DotNetBar.Touch.GestureEventArgs>(TouchHandlerPanBegin);
_TouchHandler.Pan += new EventHandler<DevComponents.DotNetBar.Touch.GestureEventArgs>(TouchHandlerPan);
_TouchHandler.PanEnd += new EventHandler<DevComponents.DotNetBar.Touch.GestureEventArgs>(TouchHandlerPanEnd);
}
_HorizontalScrollBar = new HScrollBarAdv();
_HorizontalScrollBar.Dock = DockStyle.Bottom;
_HorizontalScrollBar.Height = 12;
_HorizontalScrollBar.Scroll += new ScrollEventHandler(ScrollBarScroll);
_HorizontalScrollBar.Visible = false;
this.Controls.Add(_HorizontalScrollBar);
_VerticalScrollBar = new VScrollBarAdv();
_VerticalScrollBar.Dock = DockStyle.Right;
_VerticalScrollBar.Width = 12;
_VerticalScrollBar.Scroll += new ScrollEventHandler(ScrollBarScroll);
_VerticalScrollBar.Visible = false;
this.Controls.Add(_VerticalScrollBar);
}
protected override void Dispose(bool disposing)
{
StopScrollBarTimer();
_HorizontalScrollBar.Scroll -= new ScrollEventHandler(ScrollBarScroll);
_VerticalScrollBar.Scroll -= new ScrollEventHandler(ScrollBarScroll);
_HorizontalScrollBar.Dispose();
_VerticalScrollBar.Dispose();
_VerticalScrollBar = null;
_HorizontalScrollBar = null;
if (_TouchHandler != null)
{
_TouchHandler.PanBegin -= new EventHandler<DevComponents.DotNetBar.Touch.GestureEventArgs>(TouchHandlerPanBegin);
_TouchHandler.Pan -= new EventHandler<DevComponents.DotNetBar.Touch.GestureEventArgs>(TouchHandlerPan);
_TouchHandler.PanEnd -= new EventHandler<DevComponents.DotNetBar.Touch.GestureEventArgs>(TouchHandlerPanEnd);
_TouchHandler.Release();
}
base.Dispose(disposing);
}
#endregion
#region Events
/// <summary>
/// Occurs when selected page has changed.
/// </summary>
[Description("Occurs when selected page has changed.")]
public event EventHandler SelectedPageChanged;
/// <summary>
/// Raises SelectedPageChanged event.
/// </summary>
/// <param name="e">Provides event arguments.</param>
protected virtual void OnSelectedPageChanged(EventArgs e)
{
EventHandler handler = SelectedPageChanged;
if (handler != null)
handler(this, e);
}
#endregion
#region Implementation
protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
{
if (Dpi.RecordScalePerControl)
Dpi.SetScaling(factor);
//_StepIndicator.Height = Dpi.Height(_StepIndicator.Height);
base.ScaleControl(factor, specified);
}
protected override Size DefaultSize
{
get
{
return new Size(300, 200);
}
}
protected override void OnHandleCreated(EventArgs e)
{
if (_UpdateScrollBarVisibilityDelayed)
{
_UpdateScrollBarVisibilityDelayed = false;
UpdateScrollBarVisibility();
}
UpdateBoundsForAllPages(false);
base.OnHandleCreated(e);
}
protected override void OnHandleDestroyed(EventArgs e)
{
StopScrollBarTimer();
base.OnHandleDestroyed(e);
}
private bool _IndicatorVisible = true;
/// <summary>
/// Gets or sets whether current page indicator is visible. Default value is true.
/// </summary>
[DefaultValue(true), Category("Appearance"), Description("Indicates whether current page indicator is visible.")]
public bool IndicatorVisible
{
get { return _IndicatorVisible; }
set
{
if (value != _IndicatorVisible)
{
bool oldValue = _IndicatorVisible;
_IndicatorVisible = value;
OnIndicatorVisibleChanged(oldValue, value);
}
}
}
/// <summary>
/// Called when IndicatorVisible property has changed.
/// </summary>
/// <param name="oldValue">Old property value</param>
/// <param name="newValue">New property value</param>
protected virtual void OnIndicatorVisibleChanged(bool oldValue, bool newValue)
{
_StepIndicator.Visible = newValue;
}
protected override void OnControlAdded(ControlEventArgs e)
{
if (e.Control is PageSliderPage)
{
PageSliderPage page = (PageSliderPage)e.Control;
UpdatePageTable();
page.Bounds = GetPageBounds(page);
UpdateScrollBar();
}
base.OnControlAdded(e);
}
protected override void OnControlRemoved(ControlEventArgs e)
{
if (e.Control is PageSliderPage)
{
PageSliderPage page = (PageSliderPage)e.Control;
int pageIndex = _PageTable.IndexOf(page);
_PageTable.Remove(page);
UpdatePageTable();
if (_SelectedPage == page)
{
if (_PageTable.Count > 0)
this.SelectedPage = _PageTable[pageIndex > 0 ? pageIndex-- : 0];
else
this.SelectedPage = null;
}
UpdateScrollBar();
UpdateBoundsForAllPages(false, true);
}
base.OnControlRemoved(e);
}
protected override void OnLayout(LayoutEventArgs levent)
{
base.OnLayout(levent);
if (levent.AffectedProperty == "ChildIndex")
{
UpdatePageTable();
UpdateBoundsForAllPages(true, true);
}
}
private void UpdateBoundsForAllPages(bool updateControl)
{
UpdateBoundsForAllPages(updateControl, false);
}
private void UpdateBoundsForAllPages(bool updateControl, bool syncPageIndicator)
{
Rectangle selectedPageBounds = GetSelectedPageBounds();
int currentPage = -1;
for (int i = 0; i < _PageTable.Count; i++)
{
PageSliderPage page = _PageTable[i];
page.Bounds = GetPageBounds(page);
if (updateControl && page.Bounds.IntersectsWith(this.ClientRectangle))
this.Update();
if (syncPageIndicator && !_PageBoundsOffset.IsEmpty && page.Bounds.IntersectsWith(selectedPageBounds) && currentPage == -1)
currentPage = i + 1;
}
if (syncPageIndicator && _PageBoundsOffset.IsEmpty)
currentPage = this.SelectedPageIndex + 1;
if (syncPageIndicator && currentPage > 0)
_StepIndicator.CurrentStep = currentPage;
UpdateScrollBar();
}
private void UpdateScrollBar()
{
int selectedPageIndex = this.SelectedPageIndex;
Rectangle selectedPageBounds = GetSelectedPageBounds();
Rectangle firstPageBounds = Rectangle.Empty, lastPageBounds = Rectangle.Empty;
if (_PageTable.Count > 0)
{
firstPageBounds = _PageTable[0].Bounds;
lastPageBounds = _PageTable[_PageTable.Count - 1].Bounds;
}
if (_Orientation == eOrientation.Horizontal)
{
_HorizontalScrollBar.Minimum = 0;
_HorizontalScrollBar.Maximum = Math.Max(1, lastPageBounds.Right - firstPageBounds.X + (selectedPageIndex == -1 ? firstPageBounds.X : 0));
_HorizontalScrollBar.LargeChange = Math.Max(1, selectedPageBounds.Width);
_HorizontalScrollBar.SmallChange = 32;
if (!_IsScrolling && selectedPageIndex != -1)
_HorizontalScrollBar.Value = Math.Min(Math.Max(_HorizontalScrollBar.Minimum, Math.Abs(firstPageBounds.X)), _HorizontalScrollBar.Maximum);
}
else
{
_VerticalScrollBar.Minimum = 0;
_VerticalScrollBar.Maximum = Math.Max(1, lastPageBounds.Bottom - firstPageBounds.Y + (selectedPageIndex == -1 ? firstPageBounds.Y : 0));
_VerticalScrollBar.LargeChange = Math.Max(1, selectedPageBounds.Height);
_VerticalScrollBar.SmallChange = 32;
if (!_IsScrolling && selectedPageIndex != -1)
_VerticalScrollBar.Value = Math.Min(Math.Max(_VerticalScrollBar.Minimum, Math.Abs(firstPageBounds.Y)), _VerticalScrollBar.Maximum);
}
}
private bool _IsScrolling = false;
private int _InitialScrollValue = 0;
private void ScrollBarScroll(object sender, ScrollEventArgs e)
{
if (e.Type == ScrollEventType.EndScroll)
{
_IsScrolling = false;
Rectangle selectedPageBounds = GetSelectedPageBounds();
PageSliderPage newSelection = this.SelectedPage;
// Find right page to select since control can be scrolled a lot using scroll-bars
if (e.NewValue > _InitialScrollValue)
{
for (int i = _PageTable.Count - 1; i >= 0; i--)
{
PageSliderPage page = _PageTable[i];
if (page.Bounds.IntersectsWith(selectedPageBounds) && selectedPageBounds.Right - page.Left >= TriggerPageChangeOffset)
{
newSelection = page;
break;
}
}
}
else
{
for (int i = 0; i < _PageTable.Count; i++)
{
PageSliderPage page = _PageTable[i];
if (page.Bounds.IntersectsWith(selectedPageBounds) && page.Right - selectedPageBounds.X >= TriggerPageChangeOffset)
{
newSelection = page;
break;
}
}
}
if (this.SelectedPage != newSelection)
{
this.SelectedPage = newSelection;
if (_Orientation == eOrientation.Horizontal)
_HorizontalScrollBar.Value = Math.Abs(_PageTable[0].Left);
else
_VerticalScrollBar.Value = Math.Abs(_PageTable[0].Top);
}
else
PageBoundsOffset = Point.Empty;
}
else
{
if (!_IsScrolling)
{
if (_Orientation == eOrientation.Horizontal)
_InitialScrollValue = _HorizontalScrollBar.Value;
else
_InitialScrollValue = _VerticalScrollBar.Value;
_IsScrolling = true;
}
if (e.ScrollOrientation == ScrollOrientation.HorizontalScroll)
PageBoundsOffset = new Point(_InitialScrollValue - e.NewValue, 0);
else
PageBoundsOffset = new Point(0, _InitialScrollValue - e.NewValue);
}
}
protected override void OnResize(EventArgs e)
{
UpdateBoundsForAllPages(false);
base.OnResize(e);
}
private Rectangle GetSelectedPageBounds()
{
Rectangle selectedPageBounds = DeflateRectangle(this.ClientRectangle, _PagePadding);
if (_Orientation == eOrientation.Horizontal)
selectedPageBounds.Width -= _PageSpacing + _NextPageVisibleMargin;
else
selectedPageBounds.Height -= _PageSpacing + _NextPageVisibleMargin;
if (_IndicatorVisible)
{
if (_StepIndicator.Dock == DockStyle.Top)
{
selectedPageBounds.Y += _StepIndicator.Height;
selectedPageBounds.Height -= _StepIndicator.Height;
}
else if (_StepIndicator.Dock == DockStyle.Bottom)
{
selectedPageBounds.Height -= _StepIndicator.Height;
}
else if (_StepIndicator.Dock == DockStyle.Left)
{
selectedPageBounds.X += _StepIndicator.Width;
selectedPageBounds.Width -= _StepIndicator.Width;
}
else if (_StepIndicator.Dock == DockStyle.Right)
{
selectedPageBounds.Width -= _StepIndicator.Width;
}
}
if (_ScrollBarVisibility == eScrollBarVisibility.AlwaysVisible)
{
if (_Orientation == eOrientation.Horizontal)
selectedPageBounds.Height -= _HorizontalScrollBar.Height;
else
selectedPageBounds.Width -= _HorizontalScrollBar.Width;
}
return selectedPageBounds;
}
private Point PageBoundsOffset
{
get { return _PageBoundsOffset; }
set
{
if (_PageBoundsOffset != value)
{
_PageBoundsOffset = value;
UpdateBoundsForAllPages(true, true);
}
}
}
private Point _PageBoundsOffset = Point.Empty;
private System.Drawing.Rectangle GetPageBounds(PageSliderPage page)
{
int pageIndex = _PageTable.IndexOf(page);
int selectedPageIndex = this.SelectedPageIndex;
Rectangle bounds = Rectangle.Empty;
Rectangle selectedPageBounds = GetSelectedPageBounds();
if (pageIndex == selectedPageIndex)
{
bounds = selectedPageBounds;
}
else if (pageIndex > selectedPageIndex)
{
bounds = selectedPageBounds;
if (_Orientation == eOrientation.Horizontal)
bounds.X += (_PageSpacing + bounds.Width) * (pageIndex - selectedPageIndex);
else
bounds.Y += (_PageSpacing + bounds.Height) * (pageIndex - selectedPageIndex);
}
else
{
bounds = selectedPageBounds;
if (_Orientation == eOrientation.Horizontal)
bounds.X -= (_PageSpacing + bounds.Width) * (selectedPageIndex - pageIndex);
else
bounds.Y -= (_PageSpacing + bounds.Height) * (selectedPageIndex - pageIndex);
}
bounds.Offset(_PageBoundsOffset);
return bounds;
}
private Rectangle DeflateRectangle(Rectangle rect, Padding padding)
{
rect.X += padding.Left;
rect.Y += padding.Top;
rect.Width -= padding.Horizontal;
rect.Height -= padding.Vertical;
return rect;
}
private List<PageSliderPage> _PageTable = new List<PageSliderPage>();
private void UpdatePageTable()
{
_PageTable.Clear();
foreach (Control control in this.Controls)
{
PageSliderPage page = control as PageSliderPage;
if (page == null) continue;
bool appendPage = true;
for (int i = 0; i < _PageTable.Count; i++)
{
if (_PageTable[i].PageNumber > page.PageNumber)
{
_PageTable.Insert(i, page);
appendPage = false;
break;
}
}
if (appendPage) _PageTable.Add(page);
}
_StepIndicator.StepCount = _PageTable.Count;
_StepIndicator.CurrentStep = this.SelectedPageIndex + 1;
}
/// <summary>
/// Returns array of all pages in control.
/// </summary>
/// <returns>Array of pages.</returns>
public PageSliderPage[] GetAllPages()
{
return _PageTable.ToArray();
}
/// <summary>
/// Removes and disposes all pages within the control.
/// </summary>
public void RemoveAllPages()
{
foreach (PageSliderPage page in _PageTable)
{
page.Visible = false;
}
while (_PageTable.Count > 0)
{
PageSliderPage page = _PageTable[0];
this.Controls.Remove(page);
page.Dispose();
}
}
private PageSliderPage _SelectedPage;
/// <summary>
/// Indicates selected page.
/// </summary>
[DefaultValue(null), Category("Appearance"), Description("Indicates selected page.")]
public PageSliderPage SelectedPage
{
get { return _SelectedPage; }
set
{
if (value != _SelectedPage)
{
PageSliderPage oldValue = _SelectedPage;
_SelectedPage = value;
OnSelectedPageChanged(oldValue, value);
}
}
}
/// <summary>
/// Called when SelectedPage property has changed.
/// </summary>
/// <param name="oldValue">Old property value</param>
/// <param name="newValue">New property value</param>
protected virtual void OnSelectedPageChanged(PageSliderPage oldValue, PageSliderPage newValue)
{
//OnPropertyChanged(new PropertyChangedEventArgs("SelectedPage"));
if (_AnimationTime > 0)
AnimatePageTransition(oldValue, newValue);
else
UpdateBoundsForAllPages(false);
//if (newValue != null)
//{
// newValue.Bounds = GetPageBounds(newValue);
// newValue.BringToFront();
//}
UpdateScrollBar();
_StepIndicator.CurrentStep = this.SelectedPageIndex + 1;
OnSelectedPageChanged(EventArgs.Empty);
}
private void AnimatePageTransition(PageSliderPage oldValue, PageSliderPage newValue)
{
int oldIndex = -1;
if (oldValue != null)
oldIndex = _PageTable.IndexOf(oldValue);
int newIndex = -1;
if (newValue != null)
newIndex = _PageTable.IndexOf(newValue);
if (oldIndex == newIndex)
{
UpdateBoundsForAllPages(false);
_PageBoundsOffset = Point.Empty;
return;
}
int totalOffset = 0;
Rectangle selectedPageBounds = GetSelectedPageBounds();
if (_Orientation == eOrientation.Horizontal)
{
totalOffset = ((oldIndex - newIndex) * (selectedPageBounds.Width + _PageSpacing)) - _PageBoundsOffset.X;
}
else
{
totalOffset = ((oldIndex - newIndex) * (selectedPageBounds.Height + _PageSpacing)) - _PageBoundsOffset.Y;
}
_PageBoundsOffset = Point.Empty;
DateTime startingTime = DateTime.Now;
int speedFactor = 1;
int remainOffset = Math.Abs(totalOffset);
int offsetSign = Math.Sign(totalOffset);
int animationTimeDuration = _AnimationTime;
if (_Orientation == eOrientation.Horizontal && remainOffset < selectedPageBounds.Width * .8 ||
_Orientation == eOrientation.Vertical && remainOffset < selectedPageBounds.Height * .8)
{
if (_Orientation == eOrientation.Horizontal)
animationTimeDuration *= (remainOffset / selectedPageBounds.Width);
else
animationTimeDuration *= (remainOffset / selectedPageBounds.Height);
animationTimeDuration = Math.Max(animationTimeDuration, 50);
}
TimeSpan animationTime = new TimeSpan(0, 0, 0, 0, animationTimeDuration);
while (remainOffset > 0)
{
DateTime startPerMove = DateTime.Now;
foreach (PageSliderPage page in _PageTable)
{
Rectangle bounds = page.Bounds;
if (_Orientation == eOrientation.Horizontal)
bounds.Offset(speedFactor * offsetSign, 0);
else
bounds.Offset(0, speedFactor * offsetSign);
page.Bounds = bounds;
if (bounds.IntersectsWith(this.ClientRectangle))
this.Update();
}
remainOffset -= speedFactor;
TimeSpan elapsedPerMove = DateTime.Now - startPerMove;
TimeSpan elapsedTime = DateTime.Now - startingTime;
if ((animationTime - elapsedTime).TotalMilliseconds <= 0)
speedFactor = remainOffset;
else
{
if ((int)(animationTime - elapsedTime).TotalMilliseconds == 0)
speedFactor = 1;
else
speedFactor = remainOffset * (int)elapsedPerMove.TotalMilliseconds / Math.Max(1, (int)((animationTime - elapsedTime).TotalMilliseconds));
}
if (speedFactor > remainOffset) speedFactor = remainOffset;
}
}
private int _AnimationTime = 250;
/// <summary>
/// Indicates the animation time in milliseconds for operations that perform visual animation of transition. Set to zero to disable animation.
/// </summary>
[DefaultValue(250), Category("Behavior"), Description("Indicates the animation time in milliseconds for operations that perform visual animation of transition. Set to zero to disable animation.")]
public int AnimationTime
{
get { return _AnimationTime; }
set
{
if (value != _AnimationTime)
{
int oldValue = _AnimationTime;
_AnimationTime = value;
OnAnimationTimeChanged(oldValue, value);
}
}
}
/// <summary>
/// Called when AnimationTime property has changed.
/// </summary>
/// <param name="oldValue">Old property value</param>
/// <param name="newValue">New property value</param>
protected virtual void OnAnimationTimeChanged(int oldValue, int newValue)
{
//OnPropertyChanged(new PropertyChangedEventArgs("AnimationTime"));
}
/// <summary>
/// Gets or sets zero based selected page index. If there is no page selected returns -1.
/// </summary>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int SelectedPageIndex
{
get { return _SelectedPage != null ? _PageTable.IndexOf(_SelectedPage) : -1; }
set
{
if (value < 0 || value >= _PageTable.Count)
throw new ArgumentOutOfRangeException("SelectedPageIndex must be greater or equal than 0 and less or equal than number of pages added to control.");
this.SelectedPage = _PageTable[value];
}
}
private int _PageCount;
/// <summary>
/// Gets the total number of pages displayed by the control.
/// </summary>
[Browsable(false)]
public int PageCount
{
get
{
return _PageTable.Count;
}
}
private Padding _PagePadding = new Padding(4);
/// <summary>
/// Gets or sets the single page padding.
/// </summary>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Padding PagePadding
{
get { return _PagePadding; }
set
{
if (value != _PagePadding)
{
Padding oldValue = _PagePadding;
_PagePadding = value;
OnPagePaddingChanged(oldValue, value);
}
}
}
/// <summary>
/// Called when SelectedPagePadding property has changed.
/// </summary>
/// <param name="oldValue">Old property value</param>
/// <param name="newValue">New property value</param>
protected virtual void OnPagePaddingChanged(Padding oldValue, Padding newValue)
{
//OnPropertyChanged(new PropertyChangedEventArgs("SelectedPagePadding"));
UpdateBoundsForAllPages(false);
}
private const int DefaultPageSpacing = 48;
private int _PageSpacing = DefaultPageSpacing;
/// <summary>
/// Gets or sets the spacing in pixels between pages.
/// </summary>
[DefaultValue(DefaultPageSpacing), Category("Appearance"), Description("Indicates spacing in pixels between pages.")]
public int PageSpacing
{
get { return _PageSpacing; }
set
{
if (value != _PageSpacing)
{
int oldValue = _PageSpacing;
_PageSpacing = value;
OnPageSpacingChanged(oldValue, value);
}
}
}
/// <summary>
/// Called when PageSpacing property has changed.
/// </summary>
/// <param name="oldValue">Old property value</param>
/// <param name="newValue">New property value</param>
protected virtual void OnPageSpacingChanged(int oldValue, int newValue)
{
//OnPropertyChanged(new PropertyChangedEventArgs("PageSpacing"));
UpdateBoundsForAllPages(false);
}
private const int DefaultNextPageVisibleMargin = 50;
private int _NextPageVisibleMargin = DefaultNextPageVisibleMargin;
[DefaultValue(DefaultNextPageVisibleMargin)]
public int NextPageVisibleMargin
{
get { return _NextPageVisibleMargin; }
set
{
if (value != _NextPageVisibleMargin)
{
int oldValue = _NextPageVisibleMargin;
_NextPageVisibleMargin = value;
OnNextPageVisibleMarginChanged(oldValue, value);
}
}
}
/// <summary>
/// Called when NextPageVisibleMargin property has changed.
/// </summary>
/// <param name="oldValue">Old property value</param>
/// <param name="newValue">New property value</param>
protected virtual void OnNextPageVisibleMarginChanged(int oldValue, int newValue)
{
//OnPropertyChanged(new PropertyChangedEventArgs("NextPageVisibleMargin"));
UpdateBoundsForAllPages(false);
}
private eOrientation _Orientation = eOrientation.Horizontal;
/// <summary>
/// Gets or sets the page layout orientation. Default is horizontal.
/// </summary>
[DefaultValue(eOrientation.Horizontal), Category("Appearance"), Description("Indicates page layout orientation.")]
public eOrientation Orientation
{
get { return _Orientation; }
set
{
if (value != _Orientation)
{
eOrientation oldValue = _Orientation;
_Orientation = value;
OnOrientationChanged(oldValue, value);
}
}
}
/// <summary>
/// Called when Orientation property has changed.
/// </summary>
/// <param name="oldValue">Old property value</param>
/// <param name="newValue">New property value</param>
protected virtual void OnOrientationChanged(eOrientation oldValue, eOrientation newValue)
{
//OnPropertyChanged(new PropertyChangedEventArgs("Orientation"));
UpdateBoundsForAllPages(false);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == (int)WinApi.WindowsMessages.WM_APPCOMMAND)
{
int cmd = WinApi.GetAppCommandLParam(m.LParam);
if (cmd == (int)WinApi.AppCommands.APPCOMMAND_BROWSER_BACKWARD)
{
if (this.SelectedPageIndex > 0)
this.SelectedPageIndex--;
m.Result = new IntPtr(1);
}
else if (cmd == (int)WinApi.AppCommands.APPCOMMAND_BROWSER_FORWARD)
{
if (this.SelectedPageIndex < _PageTable.Count - 1)
this.SelectedPageIndex++;
m.Result = new IntPtr(1);
}
}
base.WndProc(ref m);
}
private bool _MouseDrag = false;
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && _TouchHandler == null && _PageTable.Count > 0 && _MouseDragEnabled)
{
_MouseDrag = true;
_TouchStartLocation = e.Location;
}
base.OnMouseDown(e);
}
private bool _PageMouseDragEnabled = true;
/// <summary>
/// Indicates whether page can be dragged using mouse to change currently selected page
/// </summary>
[DefaultValue(true), Category("Behavior"), Description("Indicates whether page can be dragged using mouse to change currently selected page")]
public bool PageMouseDragEnabled
{
get { return _PageMouseDragEnabled; }
set
{
_PageMouseDragEnabled = value;
}
}
private bool _MouseDragEnabled = true;
/// <summary>
/// Indicates whether selected page can be changed by using mouse drag on PageSlider client area which is not covered by pages.
/// </summary>
[DefaultValue(true), Category("Behavior"), Description("Indicates whether selected page can be changed by using mouse drag on PageSlider client area which is not covered by pages.")]
public bool MouseDragEnabled
{
get { return _MouseDragEnabled; }
set
{
_MouseDragEnabled = value;
}
}
internal void StartPageDrag()
{
if (_PageMouseDragEnabled && !_MouseDrag && Control.MouseButtons == MouseButtons.Left && _TouchHandler == null && _PageTable.Count > 0)
{
this.Capture = true;
_MouseDrag = true;
_TouchStartLocation = this.PointToClient(Control.MousePosition);
}
}
private int MaximumReversePageOffset
{
get
{
return Math.Min(32, this.Width / 6);
}
}
private int TriggerPageChangeOffset
{
get
{
return Math.Max(32, this.Width / 5);
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (_ScrollBarVisibility == eScrollBarVisibility.AutoVisible && !_HorizontalScrollBar.Visible && !_VerticalScrollBar.Visible)
StartScrollBarTimer();
if (_MouseDrag)
{
Rectangle selectedPageBounds = GetSelectedPageBounds();
if (_Orientation == eOrientation.Horizontal)
{
int offset = e.Location.X - _TouchStartLocation.X;
int offsetChange = offset - _PageBoundsOffset.X;
Rectangle firstPageBounds = GetPageBounds(_PageTable[0]);
if (_PageTable[0].Left + offsetChange > selectedPageBounds.X + MaximumReversePageOffset && SelectedPageIndex >= 0)
{
offset -= (_PageTable[0].Left + offsetChange) - (selectedPageBounds.X + MaximumReversePageOffset);
}
else if (_PageTable[_PageTable.Count - 1].Left + offsetChange < selectedPageBounds.X - MaximumReversePageOffset)
{
offset += (selectedPageBounds.X - MaximumReversePageOffset) - (_PageTable[_PageTable.Count - 1].Left + offsetChange);
}
PageBoundsOffset = new Point(offset, 0);
}
else
{
int offset = e.Location.Y - _TouchStartLocation.Y;
int offsetChange = offset - _PageBoundsOffset.Y;
Rectangle firstPageBounds = GetPageBounds(_PageTable[0]);
if (_PageTable[0].Top + offsetChange > selectedPageBounds.Y + MaximumReversePageOffset && SelectedPageIndex >= 0)
{
offset -= (_PageTable[0].Top + offsetChange) - (selectedPageBounds.Y + MaximumReversePageOffset);
}
else if (_PageTable[_PageTable.Count - 1].Top + offsetChange < selectedPageBounds.Y - MaximumReversePageOffset)
{
offset += (selectedPageBounds.Y - MaximumReversePageOffset) - (_PageTable[_PageTable.Count - 1].Top + offsetChange);
}
PageBoundsOffset = new Point(0, offset);
}
}
base.OnMouseMove(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (_MouseDrag)
{
_MouseDrag = false;
int totalMoveOffset = _Orientation == eOrientation.Horizontal ? _PageBoundsOffset.X : _PageBoundsOffset.Y;
if (Math.Abs(totalMoveOffset) >= TriggerPageChangeOffset)
{
Rectangle selectedPageBounds = GetSelectedPageBounds();
if (totalMoveOffset < 0)
{
if (this.SelectedPageIndex < _PageTable.Count - 1)
{
PageSliderPage newSelection = _PageTable[this.SelectedPageIndex + 1];
// Find right page to select since control can be scrolled a lot through inertia
for (int i = _PageTable.Count - 1; i >= 0; i--)
{
PageSliderPage page = _PageTable[i];
if (page.Bounds.IntersectsWith(selectedPageBounds) && selectedPageBounds.Right - page.Left >= TriggerPageChangeOffset)
{
newSelection = page;
break;
}
}
if (this.SelectedPage != newSelection)
this.SelectedPage = newSelection;
else
PageBoundsOffset = Point.Empty;
}
else
PageBoundsOffset = Point.Empty;
}
else
{
if (this.SelectedPageIndex > 0)
{
PageSliderPage newSelection = _PageTable[this.SelectedPageIndex - 1];
// Find right page to select since control can be scrolled a lot through inertia
for (int i = 0; i < _PageTable.Count; i++)
{
PageSliderPage page = _PageTable[i];
if (page.Bounds.IntersectsWith(selectedPageBounds) && page.Right - selectedPageBounds.X >= TriggerPageChangeOffset)
{
newSelection = page;
break;
}
}
if (this.SelectedPage != newSelection)
this.SelectedPage = newSelection;
else
PageBoundsOffset = Point.Empty;
}
else
PageBoundsOffset = Point.Empty;
}
}
else
PageBoundsOffset = Point.Empty;
}
base.OnMouseUp(e);
}
protected override void OnVisibleChanged(EventArgs e)
{
if (_ScrollBarVisibility == eScrollBarVisibility.AutoVisible && this.Visible)
{
StartScrollBarTimer();
}
base.OnVisibleChanged(e);
}
private eScrollBarVisibility _ScrollBarVisibility = eScrollBarVisibility.AutoVisible;
/// <summary>
/// Indicates scrollbar visibility.
/// </summary>
[DefaultValue(eScrollBarVisibility.AutoVisible), Category("Behavior"), Description("Indicates scrollbar visibility.")]
public eScrollBarVisibility ScrollBarVisibility
{
get { return _ScrollBarVisibility; }
set
{
if (value != _ScrollBarVisibility)
{
eScrollBarVisibility oldValue = _ScrollBarVisibility;
_ScrollBarVisibility = value;
OnScrollBarVisibilityChanged(oldValue, value);
}
}
}
/// <summary>
/// Called when ScrollBarVisibility property has changed.
/// </summary>
/// <param name="oldValue">Old property value</param>
/// <param name="newValue">New property value</param>
protected virtual void OnScrollBarVisibilityChanged(eScrollBarVisibility oldValue, eScrollBarVisibility newValue)
{
//OnPropertyChanged(new PropertyChangedEventArgs("ScrollBarVisibility"));
if (this.IsHandleCreated)
UpdateScrollBarVisibility();
else
_UpdateScrollBarVisibilityDelayed = true;
}
private bool _UpdateScrollBarVisibilityDelayed = false;
private void UpdateScrollBarVisibility()
{
if (_ScrollBarVisibility == eScrollBarVisibility.Hidden)
{
StopScrollBarTimer();
_HorizontalScrollBar.Visible = false;
_VerticalScrollBar.Visible = false;
UpdateBoundsForAllPages(false);
}
else if (_ScrollBarVisibility == eScrollBarVisibility.AlwaysVisible)
{
StopScrollBarTimer();
if (_Orientation == eOrientation.Horizontal)
{
if (!_HorizontalScrollBar.Visible)
{
_HorizontalScrollBar.Visible = true;
if (!this.IsHandleCreated && _HorizontalScrollBar.Width == 0)
_HorizontalScrollBar.Bounds = new Rectangle(this.ClientRectangle.X,
this.ClientRectangle.Bottom - _HorizontalScrollBar.Height, this.ClientRectangle.Width, _HorizontalScrollBar.Height);
UpdateBoundsForAllPages(false);
}
_VerticalScrollBar.Visible = false;
}
else
{
_HorizontalScrollBar.Visible = false;
if (!_VerticalScrollBar.Visible)
{
_VerticalScrollBar.Visible = true;
UpdateBoundsForAllPages(false);
}
}
}
else
{
Point p = this.PointToClient(Control.MousePosition);
if (_ScrollBarTimer == null && this.ClientRectangle.Contains(p))
{
StartScrollBarTimer();
}
}
}
private int _AutoScrollBarVisibleTime = 3000;
/// <summary>
/// Gets or sets the time in milliseconds that scrollbar is kept visible when there is no activity in control and mouse is over the control.
/// </summary>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int AutoScrollBarVisibleTime
{
get { return _AutoScrollBarVisibleTime; }
set
{
_AutoScrollBarVisibleTime = value;
}
}
internal void MouseEnterInternal()
{
if (_ScrollBarVisibility == eScrollBarVisibility.AutoVisible)
StartScrollBarTimer();
}
protected override void OnMouseEnter(EventArgs e)
{
MouseEnterInternal();
base.OnMouseEnter(e);
}
protected override void OnMouseLeave(EventArgs e)
{
MouseLeaveInternal();
base.OnMouseLeave(e);
}
internal void MouseLeaveInternal()
{
if (_ScrollBarVisibility == eScrollBarVisibility.AutoVisible)
StartScrollBarTimer();
}
private Timer _ScrollBarTimer = null;
private void StartScrollBarTimer()
{
if (_Orientation == eOrientation.Horizontal)
{
_HorizontalScrollBar.Visible = true;
_VerticalScrollBar.Visible = false;
}
else
{
_HorizontalScrollBar.Visible = false;
_VerticalScrollBar.Visible = true;
}
if (_ScrollBarTimer == null)
{
_ScrollBarTimer = new Timer();
_ScrollBarTimer.Tick += new EventHandler(ScrollBarTimerTick);
_ScrollBarTimer.Interval = _AutoScrollBarVisibleTime;
}
_ScrollBarTimer.Stop();
_ScrollBarTimer.Start();
}
private Point _LastMousePosition = Point.Empty;
void ScrollBarTimerTick(object sender, EventArgs e)
{
bool hideScrollBars = false;
Point p = this.PointToClient(Control.MousePosition);
if (this.ClientRectangle.Contains(p))
{
if (!_LastMousePosition.IsEmpty && _LastMousePosition == p && (!_HorizontalScrollBar.Bounds.Contains(p) && _HorizontalScrollBar.Visible || !_VerticalScrollBar.Bounds.Contains(p) && _VerticalScrollBar.Visible))
hideScrollBars = true;
}
else
{
if (!_LastMousePosition.IsEmpty)
hideScrollBars = true;
}
_LastMousePosition = p;
if (hideScrollBars)
{
StopScrollBarTimer();
_HorizontalScrollBar.Visible = false;
_VerticalScrollBar.Visible = false;
_LastMousePosition = Point.Empty;
}
}
private void StopScrollBarTimer()
{
Timer t = _ScrollBarTimer;
_ScrollBarTimer = null;
if (t != null)
{
t.Tick -= new EventHandler(ScrollBarTimerTick);
t.Stop();
t.Dispose();
}
}
#endregion
#region Touch Handling
private bool ProcessTouch(DevComponents.DotNetBar.Touch.GestureEventArgs e)
{
if (_TouchEnabled == eTouchHandling.No) return false;
if (_TouchEnabled == eTouchHandling.ClientContentOnly)
{
NativeFunctions.POINT p = new NativeFunctions.POINT(e.Location);
IntPtr handle = NativeFunctions.ChildWindowFromPoint(this.Handle, p);
if (handle == IntPtr.Zero)
return false;
Control c = Control.FromChildHandle(handle);
if (c == null) return false;
if (c is PageSliderPage)
{
handle = NativeFunctions.ChildWindowFromPoint(c.Handle, p);
if (handle == IntPtr.Zero)
return false;
c = Control.FromChildHandle(handle);
if (c == null || c is PageSliderPage || c is StepIndicator
|| c is Label || c is LabelX) return true;
}
if(c == this || c is StepIndicator)
return true;
return false;
}
return true;
}
private bool _TouchDrag = false;
private Point _TouchStartLocation = Point.Empty;
private void TouchHandlerPanBegin(object sender, DevComponents.DotNetBar.Touch.GestureEventArgs e)
{
if (!ProcessTouch(e)) return;
_TouchDrag = true;
_TouchStartLocation = e.Location;
e.Handled = true;
}
private void TouchHandlerPanEnd(object sender, DevComponents.DotNetBar.Touch.GestureEventArgs e)
{
if (_TouchDrag)
{
EndTouchPan();
e.Handled = true;
}
}
private void EndTouchPan()
{
_TouchDrag = false;
int totalMoveOffset = _Orientation == eOrientation.Horizontal ? _PageBoundsOffset.X : _PageBoundsOffset.Y;
if (Math.Abs(totalMoveOffset) >= TriggerPageChangeOffset)
{
Rectangle selectedPageBounds = GetSelectedPageBounds();
if (totalMoveOffset < 0)
{
if (this.SelectedPageIndex < _PageTable.Count - 1)
{
PageSliderPage newSelection = _PageTable[this.SelectedPageIndex + 1];
// Find right page to select since control can be scrolled a lot through inertia
for (int i = _PageTable.Count - 1; i >= 0; i--)
{
PageSliderPage page = _PageTable[i];
if (page.Bounds.IntersectsWith(selectedPageBounds) && selectedPageBounds.Right - page.Left >= TriggerPageChangeOffset)
{
newSelection = page;
break;
}
}
if (this.SelectedPage != newSelection)
this.SelectedPage = newSelection;
else
PageBoundsOffset = Point.Empty;
}
else
PageBoundsOffset = Point.Empty;
}
else
{
if (this.SelectedPageIndex > 0)
{
PageSliderPage newSelection = _PageTable[this.SelectedPageIndex - 1];
// Find right page to select since control can be scrolled a lot through inertia
for (int i = 0; i < _PageTable.Count; i++)
{
PageSliderPage page = _PageTable[i];
if (page.Bounds.IntersectsWith(selectedPageBounds) && page.Right - selectedPageBounds.X >= TriggerPageChangeOffset)
{
newSelection = page;
break;
}
}
if (this.SelectedPage != newSelection)
this.SelectedPage = newSelection;
else
PageBoundsOffset = Point.Empty;
}
else
PageBoundsOffset = Point.Empty;
}
}
else
PageBoundsOffset = Point.Empty;
}
private void TouchHandlerPan(object sender, DevComponents.DotNetBar.Touch.GestureEventArgs e)
{
if (_TouchDrag)
{
Rectangle selectedPageBounds = GetSelectedPageBounds();
if (_Orientation == eOrientation.Horizontal)
{
int offset = e.Location.X - _TouchStartLocation.X;
int offsetChange = offset - _PageBoundsOffset.X;
Rectangle firstPageBounds = GetPageBounds(_PageTable[0]);
bool overflow = false;
if (_PageTable[0].Left + offsetChange > selectedPageBounds.X + MaximumReversePageOffset && SelectedPageIndex >= 0)
{
offset -= (_PageTable[0].Left + offsetChange) - (selectedPageBounds.X + MaximumReversePageOffset);
overflow = true;
}
else if (_PageTable[_PageTable.Count - 1].Left + offsetChange < selectedPageBounds.X - MaximumReversePageOffset)
{
offset += (selectedPageBounds.X - MaximumReversePageOffset) - (_PageTable[_PageTable.Count - 1].Left + offsetChange);
overflow = true;
}
PageBoundsOffset = new Point(offset, 0);
if (overflow && e.IsInertia) EndTouchPan();
}
else
{
int offset = e.Location.Y - _TouchStartLocation.Y;
int offsetChange = offset - _PageBoundsOffset.Y;
Rectangle firstPageBounds = GetPageBounds(_PageTable[0]);
bool overflow = false;
if (_PageTable[0].Top + offsetChange > selectedPageBounds.Y + MaximumReversePageOffset && SelectedPageIndex >= 0)
{
offset -= (_PageTable[0].Top + offsetChange) - (selectedPageBounds.Y + MaximumReversePageOffset);
overflow = true;
}
else if (_PageTable[_PageTable.Count - 1].Top + offsetChange < selectedPageBounds.Y - MaximumReversePageOffset)
{
offset += (selectedPageBounds.Y - MaximumReversePageOffset) - (_PageTable[_PageTable.Count - 1].Top + offsetChange);
overflow = true;
}
PageBoundsOffset = new Point(0, offset);
if (overflow && e.IsInertia) EndTouchPan();
}
e.Handled = true;
}
}
private eTouchHandling _TouchEnabled = eTouchHandling.Yes;
/// <summary>
/// Indicates whether native touch support in control is enabled if available on target system.
/// </summary>
[DefaultValue(eTouchHandling.Yes), Category("Behavior"), Description("Indicates whether native touch support in control is enabled if available on target system.")]
public eTouchHandling TouchEnabled
{
get { return _TouchEnabled; }
set
{
if (value != _TouchEnabled)
{
eTouchHandling oldValue = _TouchEnabled;
_TouchEnabled = value;
OnTouchEnabledChanged(oldValue, value);
}
}
}
/// <summary>
/// Called when TouchEnabled property has changed.
/// </summary>
/// <param name="oldValue">Old property value</param>
/// <param name="newValue">New property value</param>
protected virtual void OnTouchEnabledChanged(eTouchHandling oldValue, eTouchHandling newValue)
{
//OnPropertyChanged(new PropertyChangedEventArgs("TouchEnabled"));
}
#endregion
}
/// <summary>
/// Specifies scrollbar visibility.
/// </summary>
public enum eScrollBarVisibility
{
/// <summary>
/// Scrollbars are not visible.
/// </summary>
Hidden,
/// <summary>
/// Scrollbars are visible only if mouse is inside of the control.
/// </summary>
AutoVisible,
/// <summary>
/// Scrollbars are always visible.
/// </summary>
AlwaysVisible
}
/// <summary>
/// Specifies level of touch enabled on the control.
/// </summary>
public enum eTouchHandling
{
/// <summary>
/// Touch is enabled control wide.
/// </summary>
Yes,
/// <summary>
/// Touch is disabled control wide.
/// </summary>
No,
/// <summary>
/// Touch is enabled but only for the direct control client content. If touch input occurs on any child control it is not processed.
/// </summary>
ClientContentOnly
}
}