Browse Source
git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@640 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61shortcuts
17 changed files with 1638 additions and 38 deletions
@ -0,0 +1,141 @@
@@ -0,0 +1,141 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt">2002-2005 AlphaSierraPapa</copyright>
|
||||
// <license see="prj:///doc/license.txt">GNU General Public License</license>
|
||||
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Gui.TreeGrid |
||||
{ |
||||
public class CollectionItemEventArgs<T> : EventArgs |
||||
{ |
||||
T item; |
||||
|
||||
public T Item { |
||||
get { |
||||
return item; |
||||
} |
||||
} |
||||
|
||||
public CollectionItemEventArgs(T item) |
||||
{ |
||||
this.item = item; |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// A collection that fires events when items are added or removed.
|
||||
/// </summary>
|
||||
public sealed class CollectionWithEvents<T> : IList<T> |
||||
{ |
||||
List<T> list = new List<T>(); |
||||
public event EventHandler<CollectionItemEventArgs<T>> Added; |
||||
public event EventHandler<CollectionItemEventArgs<T>> Removed; |
||||
|
||||
void OnAdded(T item) |
||||
{ |
||||
if (Added != null) |
||||
Added(this, new CollectionItemEventArgs<T>(item)); |
||||
} |
||||
void OnRemoved(T item) |
||||
{ |
||||
if (Removed != null) |
||||
Removed(this, new CollectionItemEventArgs<T>(item)); |
||||
} |
||||
|
||||
public T this[int index] { |
||||
get { |
||||
return list[index]; |
||||
} |
||||
set { |
||||
T oldValue = list[index]; |
||||
if (!object.Equals(oldValue, value)) { |
||||
list[index] = value; |
||||
OnRemoved(oldValue); |
||||
OnAdded(value); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public int Count { |
||||
[DebuggerStepThrough] |
||||
get { |
||||
return list.Count; |
||||
} |
||||
} |
||||
|
||||
public bool IsReadOnly { |
||||
get { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public int IndexOf(T item) |
||||
{ |
||||
return list.IndexOf(item); |
||||
} |
||||
|
||||
public void Insert(int index, T item) |
||||
{ |
||||
list.Insert(index, item); |
||||
OnAdded(item); |
||||
} |
||||
|
||||
public void RemoveAt(int index) |
||||
{ |
||||
T item = list[index]; |
||||
list.RemoveAt(index); |
||||
OnRemoved(item); |
||||
} |
||||
|
||||
public void Add(T item) |
||||
{ |
||||
list.Add(item); |
||||
OnAdded(item); |
||||
} |
||||
|
||||
public void Clear() |
||||
{ |
||||
List<T> oldList = list; |
||||
list = new List<T>(); |
||||
foreach (T item in oldList) { |
||||
OnRemoved(item); |
||||
} |
||||
} |
||||
|
||||
public bool Contains(T item) |
||||
{ |
||||
return list.Contains(item); |
||||
} |
||||
|
||||
public void CopyTo(T[] array, int arrayIndex) |
||||
{ |
||||
list.CopyTo(array, arrayIndex); |
||||
} |
||||
|
||||
public bool Remove(T item) |
||||
{ |
||||
if (list.Remove(item)) { |
||||
OnRemoved(item); |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
[DebuggerStepThrough] |
||||
public IEnumerator<T> GetEnumerator() |
||||
{ |
||||
return list.GetEnumerator(); |
||||
} |
||||
|
||||
[DebuggerStepThrough] |
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() |
||||
{ |
||||
return list.GetEnumerator(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,432 @@
@@ -0,0 +1,432 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt">2002-2005 AlphaSierraPapa</copyright>
|
||||
// <license see="prj:///doc/license.txt">GNU General Public License</license>
|
||||
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System; |
||||
using System.ComponentModel; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Drawing; |
||||
using System.Windows.Forms; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Gui.TreeGrid |
||||
{ |
||||
public class DynamicList : UserControl, VerticalScrollContainer.IScrollable |
||||
{ |
||||
public const int MaxColumnCount = 1000; |
||||
|
||||
public DynamicList() |
||||
: this(new CollectionWithEvents<DynamicListColumn>(), new CollectionWithEvents<DynamicListRow>()) |
||||
{ |
||||
} |
||||
|
||||
public DynamicList(CollectionWithEvents<DynamicListColumn> columns, CollectionWithEvents<DynamicListRow> rows) |
||||
{ |
||||
inUpdate = true; |
||||
if (columns == null) |
||||
throw new ArgumentNullException("columns"); |
||||
if (rows == null) |
||||
throw new ArgumentNullException("rows"); |
||||
this.columns = columns; |
||||
this.rows = rows; |
||||
// we have to register our events on the existing items
|
||||
foreach (DynamicListColumn column in columns) { |
||||
OnColumnAdded(null, new CollectionItemEventArgs<DynamicListColumn>(column)); |
||||
} |
||||
foreach (DynamicListRow row in rows) { |
||||
OnRowAdded(null, new CollectionItemEventArgs<DynamicListRow>(row)); |
||||
} |
||||
columns.Added += OnColumnAdded; |
||||
columns.Removed += OnColumnRemoved; |
||||
rows.Added += OnRowAdded; |
||||
rows.Removed += OnRowRemoved; |
||||
this.BackColor = DefaultBackColor; |
||||
this.SetStyle(ControlStyles.UserPaint, true); |
||||
this.SetStyle(ControlStyles.Selectable, true); |
||||
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); |
||||
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); |
||||
inUpdate = false; |
||||
RecalculateColumnWidths(); |
||||
} |
||||
|
||||
public new static readonly Color DefaultBackColor = SystemColors.ControlLightLight; |
||||
|
||||
protected override void Dispose(bool disposing) |
||||
{ |
||||
base.Dispose(disposing); |
||||
if (disposing) { |
||||
columns.Added -= OnColumnAdded; |
||||
columns.Removed -= OnColumnRemoved; |
||||
rows.Added -= OnRowAdded; |
||||
rows.Removed -= OnRowRemoved; |
||||
foreach (DynamicListColumn column in columns) { |
||||
OnColumnRemoved(null, new CollectionItemEventArgs<DynamicListColumn>(column)); |
||||
} |
||||
foreach (DynamicListRow row in rows) { |
||||
OnRowRemoved(null, new CollectionItemEventArgs<DynamicListRow>(row)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void OnColumnAdded(object sender, CollectionItemEventArgs<DynamicListColumn> e) |
||||
{ |
||||
e.Item.MinimumWidthChanged += ColumnMinimumWidthChanged; |
||||
e.Item.WidthChanged += ColumnWidthChanged; |
||||
RecalculateColumnWidths(); |
||||
} |
||||
|
||||
void OnColumnRemoved(object sender, CollectionItemEventArgs<DynamicListColumn> e) |
||||
{ |
||||
e.Item.MinimumWidthChanged -= ColumnMinimumWidthChanged; |
||||
e.Item.WidthChanged -= ColumnWidthChanged; |
||||
RecalculateColumnWidths(); |
||||
} |
||||
|
||||
void OnRowAdded(object sender, CollectionItemEventArgs<DynamicListRow> e) |
||||
{ |
||||
e.Item.HeightChanged += RowHeightChanged; |
||||
e.Item.ItemChanged += RowItemChanged; |
||||
} |
||||
|
||||
void OnRowRemoved(object sender, CollectionItemEventArgs<DynamicListRow> e) |
||||
{ |
||||
e.Item.HeightChanged -= RowHeightChanged; |
||||
e.Item.ItemChanged -= RowItemChanged; |
||||
} |
||||
|
||||
void ColumnMinimumWidthChanged(object sender, EventArgs e) |
||||
{ |
||||
RecalculateColumnWidths(); |
||||
} |
||||
|
||||
void RowHeightChanged(object sender, EventArgs e) |
||||
{ |
||||
Redraw(); |
||||
} |
||||
|
||||
void RowItemChanged(object sender, EventArgs e) |
||||
{ |
||||
Redraw(); |
||||
} |
||||
|
||||
bool inRecalculateColumnWidths; |
||||
bool inRecalculateNeedsRedraw; |
||||
|
||||
void RecalculateColumnWidths() |
||||
{ |
||||
if (inUpdate) return; |
||||
if (inRecalculateColumnWidths) return; |
||||
inRecalculateColumnWidths = true; |
||||
inRecalculateNeedsRedraw = false; |
||||
try { |
||||
int availableWidth = ClientSize.Width; |
||||
int minRequiredWidth = 0; |
||||
foreach (DynamicListColumn c in columns) { |
||||
if (c.AllowGrow) |
||||
minRequiredWidth += c.MinimumWidth; |
||||
else |
||||
availableWidth -= c.Width; |
||||
availableWidth -= 1; |
||||
} |
||||
// everyone gets c.MinimumWidth * availableWidth / minRequiredWidth
|
||||
foreach (DynamicListColumn c in columns) { |
||||
if (c.AllowGrow) |
||||
c.Width = Math.Max(2, c.MinimumWidth * availableWidth / minRequiredWidth); |
||||
} |
||||
} finally { |
||||
inRecalculateColumnWidths = false; |
||||
} |
||||
if (inRecalculateNeedsRedraw) { |
||||
Redraw(); |
||||
} |
||||
} |
||||
|
||||
void ColumnWidthChanged(object sender, EventArgs e) |
||||
{ |
||||
if (inRecalculateColumnWidths) { |
||||
inRecalculateNeedsRedraw = true; |
||||
return; |
||||
} |
||||
Redraw(); |
||||
} |
||||
|
||||
bool inUpdate; |
||||
|
||||
public void BeginUpdate() |
||||
{ |
||||
inUpdate = true; |
||||
} |
||||
|
||||
public void EndUpdate() |
||||
{ |
||||
inUpdate = false; |
||||
RecalculateColumnWidths(); |
||||
} |
||||
|
||||
void Redraw() |
||||
{ |
||||
if (inUpdate) return; |
||||
Invalidate(); |
||||
} |
||||
|
||||
List<Control> allowedControls = new List<Control>(); |
||||
List<Control> removedControls = new List<Control>(); |
||||
int scrollOffset = 0; |
||||
|
||||
public int ScrollOffset { |
||||
get { |
||||
return scrollOffset; |
||||
} |
||||
set { |
||||
if (scrollOffset != value) { |
||||
scrollOffset = value; |
||||
Redraw(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
int VerticalScrollContainer.IScrollable.ScrollOffsetY { |
||||
get { |
||||
return this.ScrollOffset; |
||||
} |
||||
set { |
||||
this.ScrollOffset = value; |
||||
} |
||||
} |
||||
|
||||
int VerticalScrollContainer.IScrollable.ScrollHeightY { |
||||
get { |
||||
return this.TotalRowHeight; |
||||
} |
||||
} |
||||
|
||||
protected override void OnPaint(PaintEventArgs e) |
||||
{ |
||||
//Debug.WriteLine("OnPaint");
|
||||
Graphics g = e.Graphics; |
||||
allowedControls.Clear(); |
||||
|
||||
int columnIndex = -1; |
||||
foreach (DynamicListColumn col in Columns) { |
||||
columnIndex += 1; |
||||
if (!col.AutoSize) |
||||
continue; |
||||
int minimumWidth = DynamicListColumn.DefaultWidth; |
||||
foreach (DynamicListRow row in Rows) { |
||||
DynamicListItem item = row[columnIndex]; |
||||
item.MeasureMinimumWidth(e.Graphics, ref minimumWidth); |
||||
} |
||||
col.MinimumWidth = minimumWidth; |
||||
} |
||||
|
||||
int controlIndex = 0; |
||||
int yPos = -scrollOffset; |
||||
int clientHeight = ClientSize.Height; |
||||
foreach (DynamicListRow row in Rows) { |
||||
if (yPos + row.Height > 0 && yPos < clientHeight) { |
||||
columnIndex = 0; |
||||
int xPos = 0; |
||||
foreach (DynamicListColumn col in Columns) { |
||||
Rectangle rect = new Rectangle(xPos, yPos, col.Width, row.Height); |
||||
DynamicListItem item = row[columnIndex]; |
||||
Control ctl = item.Control; |
||||
if (ctl != null) { |
||||
allowedControls.Add(ctl); |
||||
if (rect != ctl.Bounds) |
||||
ctl.Bounds = rect; |
||||
if (!this.Controls.Contains(ctl)) { |
||||
this.Controls.Add(ctl); |
||||
this.Controls.SetChildIndex(ctl, controlIndex); |
||||
} |
||||
controlIndex += 1; |
||||
} else { |
||||
item.PaintTo(e.Graphics, rect, col, item == itemAtMousePosition); |
||||
} |
||||
xPos += col.Width + 1; |
||||
columnIndex += 1; |
||||
} |
||||
} |
||||
yPos += row.Height + 1; |
||||
} |
||||
removedControls.Clear(); |
||||
foreach (Control ctl in Controls) { |
||||
if (!allowedControls.Contains(ctl)) |
||||
removedControls.Add(ctl); |
||||
} |
||||
foreach (Control ctl in removedControls) { |
||||
Debug.WriteLine("Removing control"); |
||||
Controls.Remove(ctl); |
||||
Debug.WriteLine("Control removed"); |
||||
} |
||||
allowedControls.Clear(); |
||||
removedControls.Clear(); |
||||
base.OnPaint(e); |
||||
} |
||||
|
||||
protected override void OnSizeChanged(EventArgs e) |
||||
{ |
||||
base.OnSizeChanged(e); |
||||
RecalculateColumnWidths(); |
||||
} |
||||
|
||||
public DynamicListRow GetRowFromPoint(int yPos) |
||||
{ |
||||
int y = -scrollOffset; |
||||
foreach (DynamicListRow row in Rows) { |
||||
if (yPos < y) |
||||
break; |
||||
if (yPos <= y + row.Height) |
||||
return row; |
||||
y += row.Height + 1; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets the upper left corner of the specified row.
|
||||
/// </summary>
|
||||
public Point GetPositionFromRow(DynamicListRow row) |
||||
{ |
||||
int y = -scrollOffset; |
||||
foreach (DynamicListRow r in Rows) { |
||||
if (r == row) |
||||
return new Point(0, y); |
||||
y += r.Height + 1; |
||||
} |
||||
throw new ArgumentException("The row in not in this list!"); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets the height of all rows.
|
||||
/// </summary>
|
||||
public int TotalRowHeight { |
||||
get { |
||||
int y = 0; |
||||
foreach (DynamicListRow r in Rows) { |
||||
y += r.Height + 1; |
||||
} |
||||
return y; |
||||
} |
||||
} |
||||
|
||||
public int GetColumnIndexFromPoint(int xPos) |
||||
{ |
||||
int columnIndex = 0; |
||||
int x = 0; |
||||
foreach (DynamicListColumn col in Columns) { |
||||
if (xPos < x) |
||||
break; |
||||
if (xPos <= x + col.Width) |
||||
return columnIndex; |
||||
x += col.Width + 1; |
||||
columnIndex += 1; |
||||
} |
||||
return -1; |
||||
} |
||||
|
||||
public DynamicListItem GetItemFromPoint(Point position) |
||||
{ |
||||
DynamicListRow row = GetRowFromPoint(position.Y); |
||||
if (row == null) |
||||
return null; |
||||
int columnIndex = GetColumnIndexFromPoint(position.X); |
||||
if (columnIndex < 0) |
||||
return null; |
||||
return row[columnIndex]; |
||||
} |
||||
|
||||
protected override void OnClick(EventArgs e) |
||||
{ |
||||
base.OnClick(e); |
||||
DynamicListItem item = GetItemFromPoint(PointToClient(Control.MousePosition)); |
||||
if (item != null) item.PerformClick(this); |
||||
} |
||||
|
||||
protected override void OnDoubleClick(EventArgs e) |
||||
{ |
||||
base.OnDoubleClick(e); |
||||
DynamicListItem item = GetItemFromPoint(PointToClient(Control.MousePosition)); |
||||
if (item != null) item.PerformDoubleClick(this); |
||||
} |
||||
|
||||
protected override void OnMouseHover(EventArgs e) |
||||
{ |
||||
base.OnMouseHover(e); |
||||
DynamicListItem item = GetItemFromPoint(PointToClient(Control.MousePosition)); |
||||
if (item != null) item.OnMouseHover(this); |
||||
} |
||||
|
||||
DynamicListItem itemAtMousePosition; |
||||
|
||||
protected override void OnMouseMove(MouseEventArgs e) |
||||
{ |
||||
base.OnMouseMove(e); |
||||
DynamicListItem item = GetItemFromPoint(e.Location); |
||||
if (itemAtMousePosition != item) { |
||||
if (itemAtMousePosition != null) { |
||||
OnLeaveItem(itemAtMousePosition); |
||||
} |
||||
ResetMouseEventArgs(); // raise hover again
|
||||
itemAtMousePosition = item; |
||||
if (item != null) { |
||||
if (item.Cursor != null) |
||||
this.Cursor = item.Cursor; |
||||
item.OnMouseEnter(this); |
||||
} |
||||
} |
||||
if (item != null) { |
||||
item.OnMouseMove(new DynamicListMouseEventArgs(this, e)); |
||||
} |
||||
} |
||||
|
||||
protected override void OnMouseDown(MouseEventArgs e) |
||||
{ |
||||
base.OnMouseDown(e); |
||||
DynamicListItem item = GetItemFromPoint(e.Location); |
||||
if (item != null) item.OnMouseDown(new DynamicListMouseEventArgs(this, e)); |
||||
} |
||||
|
||||
protected override void OnMouseUp(MouseEventArgs e) |
||||
{ |
||||
base.OnMouseUp(e); |
||||
DynamicListItem item = GetItemFromPoint(e.Location); |
||||
if (item != null) item.OnMouseUp(new DynamicListMouseEventArgs(this, e)); |
||||
} |
||||
|
||||
protected override void OnMouseLeave(EventArgs e) |
||||
{ |
||||
if (itemAtMousePosition != null) { |
||||
OnLeaveItem(itemAtMousePosition); |
||||
itemAtMousePosition = null; |
||||
} |
||||
base.OnMouseLeave(e); |
||||
} |
||||
|
||||
protected virtual void OnLeaveItem(DynamicListItem item) |
||||
{ |
||||
itemAtMousePosition.OnMouseLeave(this); |
||||
this.Cursor = Cursors.Default; |
||||
} |
||||
|
||||
readonly CollectionWithEvents<DynamicListColumn> columns; |
||||
readonly CollectionWithEvents<DynamicListRow> rows; |
||||
|
||||
public CollectionWithEvents<DynamicListColumn> Columns { |
||||
[DebuggerStepThrough] |
||||
get { |
||||
return columns; |
||||
} |
||||
} |
||||
|
||||
[Browsable(false)] |
||||
public CollectionWithEvents<DynamicListRow> Rows { |
||||
[DebuggerStepThrough] |
||||
get { |
||||
return rows; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,116 @@
@@ -0,0 +1,116 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt">2002-2005 AlphaSierraPapa</copyright>
|
||||
// <license see="prj:///doc/license.txt">GNU General Public License</license>
|
||||
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System; |
||||
using System.Drawing; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Gui.TreeGrid |
||||
{ |
||||
public class DynamicListColumn : ICloneable |
||||
{ |
||||
public const int DefaultWidth = 16; |
||||
|
||||
int width = DefaultWidth; |
||||
int minimumWidth = DefaultWidth; |
||||
bool allowGrow = true; |
||||
bool autoSize = false; |
||||
|
||||
Brush backgroundBrush = SystemBrushes.ControlLight; |
||||
public static readonly Color DefaultBackColor = SystemColors.ControlLight; |
||||
|
||||
public DynamicListColumn() |
||||
{ |
||||
} |
||||
|
||||
/// <summary>Copy constructor</summary>
|
||||
protected DynamicListColumn(DynamicListColumn col) |
||||
{ |
||||
width = col.width; |
||||
minimumWidth = col.minimumWidth; |
||||
allowGrow = col.allowGrow; |
||||
autoSize = col.autoSize; |
||||
backgroundBrush = col.backgroundBrush; |
||||
} |
||||
|
||||
public virtual DynamicListColumn Clone() |
||||
{ |
||||
return new DynamicListColumn(this); |
||||
} |
||||
|
||||
object ICloneable.Clone() |
||||
{ |
||||
return this.Clone(); |
||||
} |
||||
|
||||
#region Properties
|
||||
public int MinimumWidth { |
||||
get { |
||||
return minimumWidth; |
||||
} |
||||
set { |
||||
if (value < 2) |
||||
throw new ArgumentOutOfRangeException("value", value, "MinimumWidth must be at least 2"); |
||||
if (minimumWidth != value) { |
||||
minimumWidth = value; |
||||
if (MinimumWidthChanged != null) { |
||||
MinimumWidthChanged(this, EventArgs.Empty); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
public event EventHandler MinimumWidthChanged; |
||||
|
||||
public int Width { |
||||
get { |
||||
return width; |
||||
} |
||||
set { |
||||
if (value < 2) |
||||
throw new ArgumentOutOfRangeException("value", value, "Width must be at least 2"); |
||||
if (width != value) { |
||||
width = value; |
||||
if (WidthChanged != null) { |
||||
WidthChanged(this, EventArgs.Empty); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
public event EventHandler WidthChanged; |
||||
|
||||
public bool AllowGrow { |
||||
get { |
||||
return allowGrow; |
||||
} |
||||
set { |
||||
allowGrow = value; |
||||
} |
||||
} |
||||
|
||||
public bool AutoSize { |
||||
get { |
||||
return autoSize; |
||||
} |
||||
set { |
||||
autoSize = value; |
||||
} |
||||
} |
||||
|
||||
public Brush BackgroundBrush { |
||||
get { |
||||
return backgroundBrush; |
||||
} |
||||
set { |
||||
if (value == null) |
||||
throw new ArgumentNullException("value"); |
||||
backgroundBrush = value; |
||||
} |
||||
} |
||||
#endregion
|
||||
} |
||||
} |
@ -0,0 +1,345 @@
@@ -0,0 +1,345 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt">2002-2005 AlphaSierraPapa</copyright>
|
||||
// <license see="prj:///doc/license.txt">GNU General Public License</license>
|
||||
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System; |
||||
using System.Drawing; |
||||
using System.Diagnostics; |
||||
using System.Windows.Forms; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Gui.TreeGrid |
||||
{ |
||||
public sealed class DynamicListItem |
||||
{ |
||||
DynamicListRow row; |
||||
|
||||
internal DynamicListItem(DynamicListRow row) |
||||
{ |
||||
this.row = row; |
||||
} |
||||
|
||||
void OnItemChanged() |
||||
{ |
||||
row.RaiseItemChanged(this); |
||||
} |
||||
|
||||
Cursor cursor; |
||||
|
||||
public Cursor Cursor { |
||||
get { |
||||
return cursor; |
||||
} |
||||
set { |
||||
cursor = value; |
||||
} |
||||
} |
||||
|
||||
#region BackgroundBrush / Control
|
||||
Brush backgroundBrush; |
||||
|
||||
public Brush BackgroundBrush { |
||||
get { |
||||
return backgroundBrush; |
||||
} |
||||
set { |
||||
if (backgroundBrush != value) { |
||||
backgroundBrush = value; |
||||
OnItemChanged(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
Brush highlightBrush; |
||||
|
||||
public Brush HighlightBrush { |
||||
get { |
||||
return highlightBrush; |
||||
} |
||||
set { |
||||
if (highlightBrush != value) { |
||||
highlightBrush = value; |
||||
OnItemChanged(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
Control control; |
||||
|
||||
public Control Control { |
||||
get { |
||||
return control; |
||||
} |
||||
set { |
||||
if (control != value) { |
||||
control = value; |
||||
OnItemChanged(); |
||||
} |
||||
} |
||||
} |
||||
#endregion
|
||||
|
||||
#region MeasureWidth / Paint
|
||||
public event EventHandler<MeasureWidthEventArgs> MeasureWidth; |
||||
|
||||
internal void MeasureMinimumWidth(Graphics graphics, ref int minimumWidth) |
||||
{ |
||||
if (MeasureWidth != null) { |
||||
MeasureWidthEventArgs e = new MeasureWidthEventArgs(graphics); |
||||
MeasureWidth(this, e); |
||||
minimumWidth = Math.Max(minimumWidth, e.ItemWidth); |
||||
} |
||||
if (text.Length > 0) { |
||||
int width = (int)graphics.MeasureString(text, font, new PointF(0, 0), textFormat).Width; |
||||
minimumWidth = Math.Max(minimumWidth, width); |
||||
} |
||||
} |
||||
|
||||
public event PaintEventHandler Paint; |
||||
|
||||
internal void PaintTo(Graphics g, Rectangle rectangle, DynamicListColumn column, bool isMouseEntered) |
||||
{ |
||||
if (highlightBrush != null && isMouseEntered) |
||||
g.FillRectangle(highlightBrush, rectangle); |
||||
else |
||||
g.FillRectangle(backgroundBrush ?? column.BackgroundBrush, rectangle); |
||||
if (Paint != null) { |
||||
Paint(this, new PaintEventArgs(g, rectangle)); |
||||
} |
||||
if (text.Length > 0) { |
||||
g.DrawString(text, font, textBrush, rectangle, textFormat); |
||||
} |
||||
} |
||||
#endregion
|
||||
|
||||
#region Text drawing
|
||||
string text = string.Empty; |
||||
|
||||
public string Text { |
||||
get { |
||||
return text; |
||||
} |
||||
set { |
||||
if (value == null) |
||||
throw new ArgumentNullException("value", "Use string.Empty instead of null!"); |
||||
if (text != value) { |
||||
text = value; |
||||
OnItemChanged(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
Font font = Control.DefaultFont; |
||||
|
||||
public Font Font { |
||||
get { |
||||
return font; |
||||
} |
||||
set { |
||||
if (value == null) |
||||
throw new ArgumentNullException("value"); |
||||
if (font != value) { |
||||
font = value; |
||||
OnItemChanged(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
Brush textBrush = SystemBrushes.ControlText; |
||||
|
||||
public Brush TextBrush { |
||||
get { |
||||
return textBrush; |
||||
} |
||||
set { |
||||
if (value == null) |
||||
throw new ArgumentNullException("value"); |
||||
if (textBrush != value) { |
||||
textBrush = value; |
||||
OnItemChanged(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
StringFormat textFormat = StringFormat.GenericDefault; |
||||
|
||||
public StringFormat TextFormat { |
||||
get { |
||||
return textFormat; |
||||
} |
||||
set { |
||||
if (value == null) |
||||
throw new ArgumentNullException("value"); |
||||
if (textFormat != value) { |
||||
textFormat = value; |
||||
OnItemChanged(); |
||||
} |
||||
} |
||||
} |
||||
#endregion
|
||||
|
||||
#region Mouse Events
|
||||
public event EventHandler<DynamicListEventArgs> Click; |
||||
|
||||
public void PerformClick(DynamicList list) |
||||
{ |
||||
if (Click != null) |
||||
Click(this, new DynamicListEventArgs(list)); |
||||
} |
||||
|
||||
public event EventHandler<DynamicListEventArgs> DoubleClick; |
||||
|
||||
public void PerformDoubleClick(DynamicList list) |
||||
{ |
||||
if (DoubleClick != null) |
||||
DoubleClick(this, new DynamicListEventArgs(list)); |
||||
} |
||||
|
||||
public event EventHandler<DynamicListEventArgs> MouseHover; |
||||
|
||||
internal void OnMouseHover(DynamicList list) |
||||
{ |
||||
if (MouseHover != null) { |
||||
MouseHover(this, new DynamicListEventArgs(list)); |
||||
} |
||||
} |
||||
|
||||
public event EventHandler<DynamicListEventArgs> MouseLeave; |
||||
|
||||
internal void OnMouseLeave(DynamicList list) |
||||
{ |
||||
if (MouseLeave != null) { |
||||
MouseLeave(this, new DynamicListEventArgs(list)); |
||||
} |
||||
if (highlightBrush != null) OnItemChanged(); |
||||
} |
||||
|
||||
public event EventHandler<DynamicListEventArgs> MouseEnter; |
||||
|
||||
internal void OnMouseEnter(DynamicList list) |
||||
{ |
||||
if (MouseEnter != null) { |
||||
MouseEnter(this, new DynamicListEventArgs(list)); |
||||
} |
||||
if (highlightBrush != null) OnItemChanged(); |
||||
} |
||||
|
||||
public event EventHandler<DynamicListMouseEventArgs> MouseMove; |
||||
|
||||
internal void OnMouseMove(DynamicListMouseEventArgs e) |
||||
{ |
||||
if (MouseMove != null) { |
||||
MouseMove(this, e); |
||||
} |
||||
} |
||||
|
||||
public event EventHandler<DynamicListMouseEventArgs> MouseDown; |
||||
|
||||
internal void OnMouseDown(DynamicListMouseEventArgs e) |
||||
{ |
||||
if (MouseDown != null) { |
||||
MouseDown(this, e); |
||||
} |
||||
} |
||||
|
||||
public event EventHandler<DynamicListMouseEventArgs> MouseUp; |
||||
|
||||
internal void OnMouseUp(DynamicListMouseEventArgs e) |
||||
{ |
||||
if (MouseUp != null) { |
||||
MouseUp(this, e); |
||||
} |
||||
} |
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Display the control for this item. Automatically assign focus to the control
|
||||
/// and removes+disposes the control when it looses focus.
|
||||
/// </summary>
|
||||
public void AssignControlUntilFocusChange(Control control) |
||||
{ |
||||
MethodInvoker method = delegate { |
||||
if (!control.Focus()) { |
||||
control.Focus(); |
||||
} |
||||
control.LostFocus += delegate { |
||||
this.Control = null; |
||||
control.Dispose(); |
||||
}; |
||||
}; |
||||
|
||||
control.HandleCreated += delegate { |
||||
control.BeginInvoke(method); |
||||
}; |
||||
this.Control = control; |
||||
} |
||||
} |
||||
|
||||
public class DynamicListEventArgs : EventArgs |
||||
{ |
||||
DynamicList list; |
||||
|
||||
public DynamicList List { |
||||
[DebuggerStepThrough] |
||||
get { |
||||
return list; |
||||
} |
||||
} |
||||
|
||||
public DynamicListEventArgs(DynamicList list) |
||||
{ |
||||
if (list == null) throw new ArgumentNullException("list"); |
||||
this.list = list; |
||||
} |
||||
} |
||||
|
||||
public class DynamicListMouseEventArgs : MouseEventArgs |
||||
{ |
||||
DynamicList list; |
||||
|
||||
public DynamicList List { |
||||
[DebuggerStepThrough] |
||||
get { |
||||
return list; |
||||
} |
||||
} |
||||
|
||||
public DynamicListMouseEventArgs(DynamicList list, MouseEventArgs me) |
||||
: base(me.Button, me.Clicks, me.X, me.Y, me.Delta) |
||||
{ |
||||
if (list == null) throw new ArgumentNullException("list"); |
||||
this.list = list; |
||||
} |
||||
} |
||||
|
||||
public class MeasureWidthEventArgs : EventArgs |
||||
{ |
||||
Graphics graphics; |
||||
|
||||
public Graphics Graphics { |
||||
get { |
||||
return graphics; |
||||
} |
||||
} |
||||
|
||||
int itemWidth; |
||||
|
||||
public int ItemWidth { |
||||
get { |
||||
return itemWidth; |
||||
} |
||||
set { |
||||
itemWidth = value; |
||||
} |
||||
} |
||||
|
||||
public MeasureWidthEventArgs(Graphics graphics) |
||||
{ |
||||
if (graphics == null) |
||||
throw new ArgumentNullException("graphics"); |
||||
this.graphics = graphics; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,78 @@
@@ -0,0 +1,78 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt">2002-2005 AlphaSierraPapa</copyright>
|
||||
// <license see="prj:///doc/license.txt">GNU General Public License</license>
|
||||
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System; |
||||
using System.Diagnostics; |
||||
using System.Windows.Forms; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Gui.TreeGrid |
||||
{ |
||||
public class DynamicListRow |
||||
{ |
||||
int height = 16; |
||||
|
||||
public int Height { |
||||
get { |
||||
return height; |
||||
} |
||||
set { |
||||
if (value < 2) |
||||
throw new ArgumentOutOfRangeException("value", value, "value must be at least 2"); |
||||
if (height != value) { |
||||
height = value; |
||||
OnHeightChanged(EventArgs.Empty); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public event EventHandler HeightChanged; |
||||
|
||||
protected virtual void OnHeightChanged(EventArgs e) |
||||
{ |
||||
if (HeightChanged != null) { |
||||
HeightChanged(this, e); |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Fired when any item has changed.
|
||||
/// </summary>
|
||||
public event EventHandler ItemChanged; |
||||
|
||||
protected virtual void OnItemChanged(EventArgs e) |
||||
{ |
||||
if (ItemChanged != null) { |
||||
ItemChanged(this, e); |
||||
} |
||||
} |
||||
|
||||
internal void RaiseItemChanged(DynamicListItem item) |
||||
{ |
||||
OnItemChanged(EventArgs.Empty); |
||||
} |
||||
|
||||
DynamicListItem[] items = new DynamicListItem[10]; |
||||
|
||||
public DynamicListItem this[int columnIndex] { |
||||
[DebuggerStepThrough] |
||||
get { |
||||
if (columnIndex < 0) |
||||
throw new ArgumentOutOfRangeException("columnIndex", columnIndex, "columnIndex must be >= 0"); |
||||
if (columnIndex > DynamicList.MaxColumnCount) |
||||
throw new ArgumentOutOfRangeException("columnIndex", columnIndex, "columnIndex must be <= " + DynamicList.MaxColumnCount); |
||||
if (columnIndex >= items.Length) { |
||||
Array.Resize(ref items, columnIndex * 2 + 1); |
||||
} |
||||
DynamicListItem item = items[columnIndex]; |
||||
if (item == null) { |
||||
items[columnIndex] = item = new DynamicListItem(this); |
||||
} |
||||
return item; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,188 @@
@@ -0,0 +1,188 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt">2002-2005 AlphaSierraPapa</copyright>
|
||||
// <license see="prj:///doc/license.txt">GNU General Public License</license>
|
||||
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System; |
||||
using System.Drawing; |
||||
using System.Windows.Forms; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Gui.TreeGrid |
||||
{ |
||||
public class DynamicTreeRow : DynamicListRow |
||||
{ |
||||
CollectionWithEvents<DynamicListColumn> childColumns = new CollectionWithEvents<DynamicListColumn>(); |
||||
CollectionWithEvents<DynamicListRow> childRows = new CollectionWithEvents<DynamicListRow>(); |
||||
|
||||
DynamicListItem plus; |
||||
|
||||
public DynamicTreeRow() |
||||
{ |
||||
plus = this[0]; |
||||
plus.HighlightBrush = Brushes.AliceBlue; |
||||
plus.Cursor = Cursors.Hand; |
||||
plus.Click += OnPlusClick; |
||||
plus.Paint += OnPlusPaint; |
||||
plus.Text = "+"; |
||||
plus.TextFormat = new StringFormat(plus.TextFormat); |
||||
plus.TextFormat.Alignment = StringAlignment.Center; |
||||
plus.TextFormat.LineAlignment = StringAlignment.Center; |
||||
} |
||||
|
||||
protected virtual void OnPlusPaint(object sender, PaintEventArgs e) |
||||
{ |
||||
Rectangle r = e.ClipRectangle; |
||||
r.Inflate(-4, -4); |
||||
e.Graphics.DrawRectangle(SystemPens.ControlDarkDark, r); |
||||
} |
||||
|
||||
class ChildForm : Form |
||||
{ |
||||
bool isActive = true; |
||||
|
||||
protected override void OnActivated(EventArgs e) |
||||
{ |
||||
base.OnActivated(e); |
||||
isActive = true; |
||||
} |
||||
|
||||
protected override void OnDeactivate(EventArgs e) |
||||
{ |
||||
base.OnDeactivate(e); |
||||
isActive = false; |
||||
if (isOpeningChild) |
||||
return; |
||||
BeginInvoke(new MethodInvoker(CloseOnDeactivate)); |
||||
} |
||||
|
||||
void CloseOnDeactivate() |
||||
{ |
||||
ChildForm owner = Owner as ChildForm; |
||||
if (owner != null) { |
||||
if (owner.isActive) |
||||
Close(); |
||||
else |
||||
owner.CloseOnDeactivate(); |
||||
} else { |
||||
Close(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
ChildForm frm; |
||||
static bool isOpeningChild; |
||||
|
||||
protected virtual void OnPlusClick(object sender, DynamicListEventArgs e) |
||||
{ |
||||
if (frm != null) { |
||||
frm.Close(); |
||||
} |
||||
frm = new ChildForm(); |
||||
frm.Closed += delegate { |
||||
frm = null; |
||||
OnCollapsed(EventArgs.Empty); |
||||
}; |
||||
frm.FormBorderStyle = FormBorderStyle.SizableToolWindow; |
||||
Point p = e.List.PointToScreen(e.List.GetPositionFromRow(this)); |
||||
p.Offset(e.List.Columns[0].Width, Height); |
||||
frm.StartPosition = FormStartPosition.Manual; |
||||
frm.Location = p; |
||||
frm.ShowInTaskbar = false; |
||||
frm.Text = childWindowCaption; |
||||
frm.Owner = e.List.FindForm(); |
||||
OnExpanding(e); |
||||
|
||||
VerticalScrollContainer scrollContainer = new VerticalScrollContainer(); |
||||
scrollContainer.Dock = DockStyle.Fill; |
||||
|
||||
DynamicList childList = new DynamicList(childColumns, childRows); |
||||
childList.Dock = DockStyle.Fill; |
||||
childList.KeyDown += delegate(object sender2, KeyEventArgs e2) { |
||||
if (e2.KeyData == Keys.Escape) { |
||||
frm.Close(); |
||||
// workaround focus problem: sometimes the mainform gets focus after this
|
||||
e.List.FindForm().Focus(); |
||||
} |
||||
}; |
||||
scrollContainer.Controls.Add(childList); |
||||
|
||||
frm.DockPadding.All = 2; |
||||
frm.Controls.Add(scrollContainer); |
||||
|
||||
int screenHeight = Screen.FromPoint(p).WorkingArea.Bottom - p.Y; |
||||
screenHeight -= frm.Size.Height - frm.ClientSize.Height; |
||||
int formHeight = Math.Min(childList.TotalRowHeight + 4, screenHeight); |
||||
if (formHeight < 100) { |
||||
formHeight += 100; |
||||
frm.Top -= 100; |
||||
} |
||||
frm.ClientSize = new Size(e.List.Width, formHeight); |
||||
isOpeningChild = true; |
||||
frm.Show(); |
||||
isOpeningChild = false; |
||||
childList.Focus(); |
||||
OnExpanded(e); |
||||
} |
||||
|
||||
public event EventHandler Expanding; |
||||
public event EventHandler Expanded; |
||||
public event EventHandler Collapsed; |
||||
|
||||
protected virtual void OnExpanding(EventArgs e) |
||||
{ |
||||
if (Expanding != null) { |
||||
Expanding(this, e); |
||||
} |
||||
} |
||||
protected virtual void OnExpanded(EventArgs e) |
||||
{ |
||||
if (Expanded != null) { |
||||
Expanded(this, e); |
||||
} |
||||
} |
||||
protected virtual void OnCollapsed(EventArgs e) |
||||
{ |
||||
if (Collapsed != null) { |
||||
Collapsed(this, e); |
||||
} |
||||
} |
||||
|
||||
string childWindowCaption = "Child window"; |
||||
|
||||
public string ChildWindowCaption { |
||||
get { |
||||
return childWindowCaption; |
||||
} |
||||
set { |
||||
if (value == null) |
||||
throw new ArgumentNullException(); |
||||
childWindowCaption = value; |
||||
if (frm != null) frm.Text = value; |
||||
} |
||||
} |
||||
|
||||
public CollectionWithEvents<DynamicListColumn> ChildColumns { |
||||
get { |
||||
return childColumns; |
||||
} |
||||
set { |
||||
if (value == null) |
||||
throw new ArgumentNullException("value"); |
||||
childColumns = value; |
||||
} |
||||
} |
||||
|
||||
public CollectionWithEvents<DynamicListRow> ChildRows { |
||||
get { |
||||
return childRows; |
||||
} |
||||
set { |
||||
if (value == null) |
||||
throw new ArgumentNullException("value"); |
||||
childRows = value; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,120 @@
@@ -0,0 +1,120 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt">2002-2005 AlphaSierraPapa</copyright>
|
||||
// <license see="prj:///doc/license.txt">GNU General Public License</license>
|
||||
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System; |
||||
using System.ComponentModel; |
||||
using System.Drawing; |
||||
using System.Windows.Forms; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Gui.TreeGrid |
||||
{ |
||||
public class ScrollButtonControl : Control |
||||
{ |
||||
public ScrollButtonControl() |
||||
{ |
||||
this.BackColor = DynamicListColumn.DefaultBackColor; |
||||
this.TabStop = false; |
||||
this.SetStyle(ControlStyles.Selectable, false); |
||||
} |
||||
|
||||
protected override Size DefaultSize { |
||||
get { |
||||
return new Size(14, 14); |
||||
} |
||||
} |
||||
|
||||
ScrollButton arrow = ScrollButton.Down; |
||||
|
||||
public ScrollButton Arrow { |
||||
get { |
||||
return arrow; |
||||
} |
||||
set { |
||||
arrow = value; |
||||
Invalidate(); |
||||
} |
||||
} |
||||
|
||||
bool drawSeparatorLine = true; |
||||
|
||||
public bool DrawSeparatorLine { |
||||
get { |
||||
return drawSeparatorLine; |
||||
} |
||||
set { |
||||
drawSeparatorLine = value; |
||||
Invalidate(); |
||||
} |
||||
} |
||||
|
||||
Color highlightColor = SystemColors.Highlight; |
||||
|
||||
public Color HighlightColor { |
||||
get { |
||||
return highlightColor; |
||||
} |
||||
set { |
||||
highlightColor = value; |
||||
Invalidate(); |
||||
} |
||||
} |
||||
|
||||
protected override void OnPaint(PaintEventArgs e) |
||||
{ |
||||
base.OnPaint(e); |
||||
const int margin = 2; |
||||
int height = this.ClientSize.Height; |
||||
int size = height - 2 * margin; |
||||
int width = this.ClientSize.Width; |
||||
int left = (width - size) / 2; |
||||
int right = (width + size) / 2; |
||||
Point[] triangle; |
||||
switch (arrow) { |
||||
case ScrollButton.Down: |
||||
triangle = new Point[] { |
||||
new Point(left, margin), new Point(right, margin), new Point(width / 2, margin + size) |
||||
}; |
||||
if (drawSeparatorLine) |
||||
e.Graphics.DrawLine(SystemPens.GrayText, 0, 0, width, 0); |
||||
break; |
||||
case ScrollButton.Up: |
||||
triangle = new Point[] { |
||||
new Point(left, margin + size), new Point(right, margin + size), new Point(width / 2, margin) |
||||
}; |
||||
if (drawSeparatorLine) |
||||
e.Graphics.DrawLine(SystemPens.GrayText, 0, height - 1, width, height - 1); |
||||
break; |
||||
default: |
||||
return; |
||||
} |
||||
Color color; |
||||
if (Enabled) |
||||
color = cursorEntered ? HighlightColor : ForeColor; |
||||
else |
||||
color = SystemColors.GrayText; |
||||
using (Brush b = new SolidBrush(color)) { |
||||
e.Graphics.FillPolygon(b, triangle); |
||||
} |
||||
} |
||||
|
||||
bool cursorEntered = false; |
||||
|
||||
protected override void OnMouseEnter(EventArgs e) |
||||
{ |
||||
base.OnMouseEnter(e); |
||||
cursorEntered = true; |
||||
Invalidate(); |
||||
} |
||||
|
||||
protected override void OnMouseLeave(EventArgs e) |
||||
{ |
||||
base.OnMouseLeave(e); |
||||
cursorEntered = false; |
||||
Invalidate(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,172 @@
@@ -0,0 +1,172 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt">2002-2005 AlphaSierraPapa</copyright>
|
||||
// <license see="prj:///doc/license.txt">GNU General Public License</license>
|
||||
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System; |
||||
using System.ComponentModel; |
||||
using System.Drawing; |
||||
using System.Windows.Forms; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Gui.TreeGrid |
||||
{ |
||||
public class VerticalScrollContainer : Control |
||||
{ |
||||
public interface IScrollable |
||||
{ |
||||
int ScrollOffsetY { get; set; } |
||||
int ScrollHeightY { get; } |
||||
int Height { get; } |
||||
event MouseEventHandler MouseWheel; |
||||
} |
||||
|
||||
IScrollable scrollable; |
||||
bool showButtonsOnlyIfRequired = true; |
||||
ScrollButtonControl down = new ScrollButtonControl(); |
||||
ScrollButtonControl up = new ScrollButtonControl(); |
||||
Timer timer = new Timer(); |
||||
|
||||
int scrollSpeed = 5; |
||||
|
||||
public int ScrollSpeed { |
||||
get { |
||||
return scrollSpeed; |
||||
} |
||||
set { |
||||
scrollSpeed = value; |
||||
} |
||||
} |
||||
|
||||
public VerticalScrollContainer() |
||||
{ |
||||
up.Arrow = ScrollButton.Up; |
||||
down.Arrow = ScrollButton.Down; |
||||
up.Dock = DockStyle.Top; |
||||
down.Dock = DockStyle.Bottom; |
||||
this.TabStop = false; |
||||
this.SetStyle(ControlStyles.Selectable, false); |
||||
Controls.Add(up); |
||||
Controls.Add(down); |
||||
UpdateEnabled(); |
||||
|
||||
timer.Interval = 50; |
||||
timer.Tick += delegate { |
||||
ScrollBy((int)timer.Tag); |
||||
}; |
||||
up.MouseDown += delegate { |
||||
timer.Tag = -scrollSpeed; |
||||
ScrollBy(-scrollSpeed); |
||||
timer.Start(); |
||||
}; |
||||
down.MouseDown += delegate { |
||||
timer.Tag = scrollSpeed; |
||||
ScrollBy(scrollSpeed); |
||||
timer.Start(); |
||||
}; |
||||
up.MouseUp += StopTimer; |
||||
down.MouseUp += StopTimer; |
||||
} |
||||
|
||||
void StopTimer(object sender, MouseEventArgs e) |
||||
{ |
||||
timer.Stop(); |
||||
} |
||||
|
||||
protected override void Dispose(bool disposing) |
||||
{ |
||||
base.Dispose(disposing); |
||||
if (disposing) { |
||||
timer.Dispose(); |
||||
} |
||||
} |
||||
|
||||
void UpdateEnabled() |
||||
{ |
||||
if (scrollable == null) { |
||||
up.Visible = down.Visible = true; |
||||
up.Enabled = down.Enabled = false; |
||||
} else { |
||||
int scrollHeightY = scrollable.ScrollHeightY; |
||||
if (showButtonsOnlyIfRequired) { |
||||
if (scrollHeightY <= this.Height) { |
||||
scrollable.ScrollOffsetY = 0; |
||||
up.Visible = down.Visible = false; |
||||
return; |
||||
} |
||||
up.Visible = down.Visible = true; |
||||
} else { |
||||
up.Visible = down.Visible = true; |
||||
if (scrollable.ScrollHeightY <= scrollable.Height) { |
||||
scrollable.ScrollOffsetY = 0; |
||||
up.Enabled = down.Enabled = false; |
||||
return; |
||||
} |
||||
} |
||||
// set enabled
|
||||
up.Enabled = scrollable.ScrollOffsetY > 0; |
||||
down.Enabled = scrollable.ScrollOffsetY < scrollHeightY - scrollable.Height; |
||||
} |
||||
} |
||||
|
||||
void ScrollBy(int amount) |
||||
{ |
||||
scrollable.ScrollOffsetY = Math.Max(0, Math.Min(scrollable.ScrollOffsetY + amount, scrollable.ScrollHeightY - scrollable.Height)); |
||||
UpdateEnabled(); |
||||
} |
||||
|
||||
public bool ShowButtonsOnlyIfRequired { |
||||
get { |
||||
return showButtonsOnlyIfRequired; |
||||
} |
||||
set { |
||||
if (showButtonsOnlyIfRequired != value) { |
||||
showButtonsOnlyIfRequired = value; |
||||
UpdateEnabled(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void ScrollableWheel(object sender, MouseEventArgs e) |
||||
{ |
||||
ScrollBy(-e.Delta / 3); |
||||
} |
||||
|
||||
protected override void OnSizeChanged(EventArgs e) |
||||
{ |
||||
base.OnSizeChanged(e); |
||||
UpdateEnabled(); |
||||
} |
||||
|
||||
protected override void OnControlAdded(ControlEventArgs e) |
||||
{ |
||||
base.OnControlAdded(e); |
||||
if (scrollable == null && !DesignMode) { |
||||
scrollable = e.Control as IScrollable; |
||||
if (scrollable != null) { |
||||
scrollable.MouseWheel += ScrollableWheel; |
||||
Controls.SetChildIndex(e.Control, 0); |
||||
UpdateEnabled(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
protected override void OnControlRemoved(ControlEventArgs e) |
||||
{ |
||||
base.OnControlRemoved(e); |
||||
if (scrollable == e.Control) { |
||||
scrollable.MouseWheel -= ScrollableWheel; |
||||
scrollable = null; |
||||
UpdateEnabled(); |
||||
} |
||||
} |
||||
|
||||
protected override void OnHandleCreated(EventArgs e) |
||||
{ |
||||
base.OnHandleCreated(e); |
||||
// HACK: Windows.Forms bug workaround
|
||||
BeginInvoke(new MethodInvoker(PerformLayout)); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue