using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Security.Permissions;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using System.Collections;
using System.Drawing.Design;
using Aga.Controls.Tree.NodeControls;
namespace Aga.Controls.Tree
{
public partial class TreeViewAdv : Control
{
#region Inner Classes
private struct NodeControlInfo
{
public static readonly NodeControlInfo Empty = new NodeControlInfo();
private NodeControl _control;
public NodeControl Control
{
get { return _control; }
}
private Rectangle _bounds;
public Rectangle Bounds
{
get { return _bounds; }
}
public NodeControlInfo(NodeControl control, Rectangle bounds)
{
_control = control;
_bounds = bounds;
}
}
private class ExpandedNode
{
private object _tag;
public object Tag
{
get { return _tag; }
set { _tag = value; }
}
private Collection<ExpandedNode> _children = new Collection<ExpandedNode>();
public Collection<ExpandedNode> Children
{
get { return _children; }
set { _children = value; }
}
}
#endregion
private const int TopMargin = 0;
private const int LeftMargin = 7;
private const int ItemDragSensivity = 4;
private readonly int _columnHeaderHeight;
private const int DividerWidth = 9;
private int _offsetX;
private int _firstVisibleRow;
private ReadOnlyCollection<TreeNodeAdv> _readonlySelection;
private Pen _linePen;
private Pen _markPen;
private bool _dragMode;
private bool _suspendUpdate;
private bool _structureUpdating;
private bool _needFullUpdate;
private bool _fireSelectionEvent;
private NodePlusMinus _plusMinus;
private Control _currentEditor;
private EditableControl _currentEditorOwner;
private ToolTip _toolTip;
#region Internal Properties
private int ColumnHeaderHeight
{
get
{
if (UseColumns)
return _columnHeaderHeight;
else
return 0;
}
}
/// <summary>
/// returns all nodes, which parent is expanded
/// </summary>
private IEnumerable<TreeNodeAdv> ExpandedNodes
{
get
{
if (_root.Nodes.Count > 0)
{
TreeNodeAdv node = _root.Nodes[0];
while (node != null)
{
yield return node;
if (node.IsExpanded && node.Nodes.Count > 0)
node = node.Nodes[0];
else if (node.NextNode != null)
node = node.NextNode;
else
node = node.BottomNode;
}
}
}
}
private bool _suspendSelectionEvent;
internal bool SuspendSelectionEvent
{
get { return _suspendSelectionEvent; }
set
{
_suspendSelectionEvent = value;
if (!_suspendSelectionEvent && _fireSelectionEvent)
OnSelectionChanged();
}
}
private List<TreeNodeAdv> _rowMap;
internal List<TreeNodeAdv> RowMap
{
get { return _rowMap; }
}
private TreeNodeAdv _selectionStart;
internal TreeNodeAdv SelectionStart
{
get { return _selectionStart; }
set { _selectionStart = value; }
}
private InputState _input;
internal InputState Input
{
get { return _input; }
set
{
_input = value;
}
}
private bool _itemDragMode;
internal bool ItemDragMode
{
get { return _itemDragMode; }
set { _itemDragMode = value; }
}
private Point _itemDragStart;
internal Point ItemDragStart
{
get { return _itemDragStart; }
set { _itemDragStart = value; }
}
/// <summary>
/// Number of rows fits to the screen
/// </summary>
internal int PageRowCount
{
get
{
return Math.Max((DisplayRectangle.Height - ColumnHeaderHeight) / RowHeight, 0);
}
}
/// <summary>
/// Number of all visible nodes (which parent is expanded)
/// </summary>
internal int RowCount
{
get
{
return _rowMap.Count;
}
}
private int _contentWidth = 0;
private int ContentWidth
{
get
{
return _contentWidth;
}
}
internal int FirstVisibleRow
{
get { return _firstVisibleRow; }
set
{
HideEditor();
_firstVisibleRow = value;
UpdateView();
}
}
private int OffsetX
{
get { return _offsetX; }
set
{
HideEditor();
_offsetX = value;
UpdateView();
}
}
public override Rectangle DisplayRectangle
{
get
{
Rectangle r = ClientRectangle;
//r.Y += ColumnHeaderHeight;
//r.Height -= ColumnHeaderHeight;
int w = _vScrollBar.Visible ? _vScrollBar.Width : 0;
int h = _hScrollBar.Visible ? _hScrollBar.Height : 0;
return new Rectangle(r.X, r.Y, r.Width - w, r.Height - h);
}
}
private List<TreeNodeAdv> _selection;
internal List<TreeNodeAdv> Selection
{
get { return _selection; }
}
#endregion
#region Public Properties
#region DesignTime
private bool _fullRowSelect;
[DefaultValue(false), Category("Behavior")]
public bool FullRowSelect
{
get { return _fullRowSelect; }
set
{
_fullRowSelect = value;
UpdateView();
}
}
private bool _useColumns;
[DefaultValue(false), Category("Behavior")]
public bool UseColumns
{
get { return _useColumns; }
set
{
_useColumns = value;
FullUpdate();
}
}
private bool _showLines = true;
[DefaultValue(true), Category("Behavior")]
public bool ShowLines
{
get { return _showLines; }
set
{
_showLines = value;
UpdateView();
}
}
private bool _showNodeToolTips = false;
[DefaultValue(false), Category("Behavior")]
public bool ShowNodeToolTips
{
get { return _showNodeToolTips; }
set { _showNodeToolTips = value; }
}
private bool _keepNodesExpanded;
[DefaultValue(false), Category("Behavior")]
public bool KeepNodesExpanded
{
get { return _keepNodesExpanded; }
set { _keepNodesExpanded = value; }
}
private ITreeModel _model;
[Category("Data")]
public ITreeModel Model
{
get { return _model; }
set
{
if (_model != value)
{
if (_model != null)
UnbindModelEvents();
_model = value;
CreateNodes();
FullUpdate();
if (_model != null)
BindModelEvents();
}
}
}
private BorderStyle _borderStyle;
[DefaultValue(BorderStyle.Fixed3D), Category("Appearance")]
public BorderStyle BorderStyle
{
get
{
return this._borderStyle;
}
set
{
if (_borderStyle != value)
{
_borderStyle = value;
base.UpdateStyles();
}
}
}
private int _rowHeight = 16;
[DefaultValue(16), Category("Appearance")]
public int RowHeight
{
get
{
return _rowHeight;
}
set
{
if (value <= 0)
throw new ArgumentOutOfRangeException();
_rowHeight = value;
FullUpdate();
}
}
private TreeSelectionMode _selectionMode = TreeSelectionMode.Single;
[DefaultValue(TreeSelectionMode.Single), Category("Behavior")]
public TreeSelectionMode SelectionMode
{
get { return _selectionMode; }
set { _selectionMode = value; }
}
private bool _hideSelection;
[DefaultValue(false), Category("Behavior")]
public bool HideSelection
{
get { return _hideSelection; }
set
{
_hideSelection = value;
UpdateView();
}
}
private float _topEdgeSensivity = 0.3f;
[DefaultValue(0.3f), Category("Behavior")]
public float TopEdgeSensivity
{
get { return _topEdgeSensivity; }
set
{
if (value < 0 || value > 1)
throw new ArgumentOutOfRangeException();
_topEdgeSensivity = value;
}
}
private float _bottomEdgeSensivity = 0.3f;
[DefaultValue(0.3f), Category("Behavior")]
public float BottomEdgeSensivity
{
get { return _bottomEdgeSensivity; }
set
{
if (value < 0 || value > 1)
throw new ArgumentOutOfRangeException("value should be from 0 to 1");
_bottomEdgeSensivity = value;
}
}
private bool _loadOnDemand;
[DefaultValue(false), Category("Behavior")]
public bool LoadOnDemand
{
get { return _loadOnDemand; }
set { _loadOnDemand = value; }
}
private int _indent = 19;
[DefaultValue(19), Category("Behavior")]
public int Indent
{
get { return _indent; }
set
{
_indent = value;
UpdateView();
}
}
private Color _lineColor = SystemColors.ControlDark;
[Category("Behavior")]
public Color LineColor
{
get { return _lineColor; }
set
{
_lineColor = value;
CreateLinePen();
UpdateView();
}
}
private Color _dragDropMarkColor = Color.Black;
[Category("Behavior")]
public Color DragDropMarkColor
{
get { return _dragDropMarkColor; }
set
{
_dragDropMarkColor = value;
CreateMarkPen();
}
}
private float _dragDropMarkWidth = 3.0f;
[DefaultValue(3.0f), Category("Behavior")]
public float DragDropMarkWidth
{
get { return _dragDropMarkWidth; }
set
{
_dragDropMarkWidth = value;
CreateMarkPen();
}
}
private TreeColumnCollection _columns;
[Category("Behavior"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Collection<TreeColumn> Columns
{
get { return _columns; }
}
private NodeControlsCollection _controls;
[Category("Behavior"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Editor(typeof(NodeControlCollectionEditor), typeof(UITypeEditor))]
public Collection<NodeControl> NodeControls
{
get
{
return _controls;
}
}
#endregion
#region RunTime
[Browsable(false)]
public IEnumerable<TreeNodeAdv> AllNodes
{
get
{
if (_root.Nodes.Count > 0)
{
TreeNodeAdv node = _root.Nodes[0];
while (node != null)
{
yield return node;
if (node.Nodes.Count > 0)
node = node.Nodes[0];
else if (node.NextNode != null)
node = node.NextNode;
else
node = node.BottomNode;
}
}
}
}
private DropPosition _dropPosition;
[Browsable(false)]
public DropPosition DropPosition
{
get { return _dropPosition; }
set { _dropPosition = value; }
}
private TreeNodeAdv _root;
[Browsable(false)]
public TreeNodeAdv Root
{
get { return _root; }
}
[Browsable(false)]
public ReadOnlyCollection<TreeNodeAdv> SelectedNodes
{
get
{
return _readonlySelection;
}
}
[Browsable(false)]
public TreeNodeAdv SelectedNode
{
get
{
if (Selection.Count > 0)
{
if (CurrentNode != null && CurrentNode.IsSelected)
return CurrentNode;
else
return Selection[0];
}
else
return null;
}
set
{
if (SelectedNode == value)
return;
BeginUpdate();
try
{
if (value == null)
{
ClearSelection();
}
else
{
if (!IsMyNode(value))
throw new ArgumentException();
ClearSelection();
value.IsSelected = true;
CurrentNode = value;
EnsureVisible(value);
}
}
finally
{
EndUpdate();
}
}
}
private TreeNodeAdv _currentNode;
[Browsable(false)]
public TreeNodeAdv CurrentNode
{
get { return _currentNode; }
internal set { _currentNode = value; }
}
#endregion
#endregion
#region Public Events
[Category("Action")]
public event ItemDragEventHandler ItemDrag;
private void OnItemDrag(MouseButtons buttons, object item)
{
if (ItemDrag != null)
ItemDrag(this, new ItemDragEventArgs(buttons, item));
}
[Category("Behavior")]
public event EventHandler<TreeNodeAdvMouseEventArgs> NodeMouseDoubleClick;
private void OnNodeMouseDoubleClick(TreeNodeAdvMouseEventArgs args)
{
if (NodeMouseDoubleClick != null)
NodeMouseDoubleClick(this, args);
}
[Category("Behavior")]
public event EventHandler<TreeColumnEventArgs> ColumnWidthChanged;
internal void OnColumnWidthChanged(TreeColumn column)
{
if (ColumnWidthChanged != null)
ColumnWidthChanged(this, new TreeColumnEventArgs(column));
}
[Category("Behavior")]
public event EventHandler SelectionChanged;
internal void OnSelectionChanged()
{
if (SuspendSelectionEvent)
_fireSelectionEvent = true;
else
{
_fireSelectionEvent = false;
if (SelectionChanged != null)
SelectionChanged(this, EventArgs.Empty);
}
}
[Category("Behavior")]
public event EventHandler<TreeViewAdvEventArgs> Collapsing;
internal void OnCollapsing(TreeNodeAdv node)
{
if (Collapsing != null)
Collapsing(this, new TreeViewAdvEventArgs(node));
}
[Category("Behavior")]
public event EventHandler<TreeViewAdvEventArgs> Collapsed;
internal void OnCollapsed(TreeNodeAdv node)
{
if (Collapsed != null)
Collapsed(this, new TreeViewAdvEventArgs(node));
}
[Category("Behavior")]
public event EventHandler<TreeViewAdvEventArgs> Expanding;
internal void OnExpanding(TreeNodeAdv node)
{
if (Expanding != null)
Expanding(this, new TreeViewAdvEventArgs(node));
}
[Category("Behavior")]
public event EventHandler<TreeViewAdvEventArgs> Expanded;
internal void OnExpanded(TreeNodeAdv node)
{
if (Expanded != null)
Expanded(this, new TreeViewAdvEventArgs(node));
}
#endregion
public TreeViewAdv()
{
InitializeComponent();
SetStyle(ControlStyles.AllPaintingInWmPaint
| ControlStyles.UserPaint
| ControlStyles.OptimizedDoubleBuffer
| ControlStyles.ResizeRedraw
| ControlStyles.Selectable
, true);
if (Application.RenderWithVisualStyles)
_columnHeaderHeight = 20;
else
_columnHeaderHeight = 17;
BorderStyle = BorderStyle.Fixed3D;
_hScrollBar.Height = SystemInformation.HorizontalScrollBarHeight;
_vScrollBar.Width = SystemInformation.VerticalScrollBarWidth;
_rowMap = new List<TreeNodeAdv>();
_selection = new List<TreeNodeAdv>();
_readonlySelection = new ReadOnlyCollection<TreeNodeAdv>(_selection);
_columns = new TreeColumnCollection(this);
_toolTip = new ToolTip();
Input = new NormalInputState(this);
CreateNodes();
CreatePens();
ArrangeControls();
_plusMinus = new NodePlusMinus();
_controls = new NodeControlsCollection(this);
}
#region Public Methods
public TreePath GetPath(TreeNodeAdv node)
{
if (node == _root)
return TreePath.Empty;
else
{
Stack<object> stack = new Stack<object>();
while (node != _root)
{
stack.Push(node.Tag);
node = node.Parent;
}
return new TreePath(stack.ToArray());
}
}
public TreeNodeAdv GetNodeAt(Point point)
{
if (point.X < 0 || point.Y < 0)
return null;
point = ToAbsoluteLocation(point);
int row = point.Y / RowHeight;
if (row < RowCount && row >= 0)
{
NodeControlInfo info = GetNodeControlInfoAt(_rowMap[row], point);
if (info.Control != null)
return _rowMap[row];
}
return null;
}
public void BeginUpdate()
{
_suspendUpdate = true;
}
public void EndUpdate()
{
_suspendUpdate = false;
if (_needFullUpdate)
FullUpdate();
else
UpdateView();
}
public void ExpandAll()
{
BeginUpdate();
SetIsExpanded(_root, true);
EndUpdate();
}
public void CollapseAll()
{
BeginUpdate();
SetIsExpanded(_root, false);
EndUpdate();
}
/// <summary>
/// Expand all parent nodes, andd scroll to the specified node
/// </summary>
public void EnsureVisible(TreeNodeAdv node)
{
if (node == null)
throw new ArgumentNullException("node");
if (!IsMyNode(node))
throw new ArgumentException();
TreeNodeAdv parent = node.Parent;
while (parent != _root)
{
parent.IsExpanded = true;
parent = parent.Parent;
}
ScrollTo(node);
}
/// <summary>
/// Make node visible, scroll if needed. All parent nodes of the specified node must be expanded
/// </summary>
/// <param name="node"></param>
public void ScrollTo(TreeNodeAdv node)
{
if (node == null)
throw new ArgumentNullException("node");
if (!IsMyNode(node))
throw new ArgumentException();
if (node.Row < 0)
CreateRowMap();
int row = 0;
if (node.Row < FirstVisibleRow)
row = node.Row;
else if (node.Row >= FirstVisibleRow + (PageRowCount - 1))
row = node.Row - (PageRowCount - 1);
if (row >= _vScrollBar.Minimum && row <= _vScrollBar.Maximum)
_vScrollBar.Value = row;
}
#endregion
private Point ToAbsoluteLocation(Point point)
{
return new Point(point.X + _offsetX, point.Y + (FirstVisibleRow * RowHeight) - ColumnHeaderHeight);
}
private Point ToViewLocation(Point point)
{
return new Point(point.X - _offsetX, point.Y - (FirstVisibleRow * RowHeight) + ColumnHeaderHeight);
}
protected override void OnSizeChanged(EventArgs e)
{
ArrangeControls();
SafeUpdateScrollBars();
base.OnSizeChanged(e);
}
private void ArrangeControls()
{
int hBarSize = _hScrollBar.Height;
int vBarSize = _vScrollBar.Width;
Rectangle clientRect = ClientRectangle;
_hScrollBar.SetBounds(clientRect.X, clientRect.Bottom - hBarSize,
clientRect.Width - vBarSize, hBarSize);
_vScrollBar.SetBounds(clientRect.Right - vBarSize, clientRect.Y,
vBarSize, clientRect.Height - hBarSize);
}
private void SafeUpdateScrollBars()
{
if (InvokeRequired)
Invoke(new MethodInvoker(UpdateScrollBars));
else
UpdateScrollBars();
}
private void UpdateScrollBars()
{
UpdateVScrollBar();
UpdateHScrollBar();
UpdateVScrollBar();
UpdateHScrollBar();
_hScrollBar.Width = DisplayRectangle.Width;
_vScrollBar.Height = DisplayRectangle.Height;
}
private void UpdateHScrollBar()
{
_hScrollBar.Maximum = ContentWidth;
_hScrollBar.LargeChange = Math.Max(DisplayRectangle.Width, 0);
_hScrollBar.SmallChange = 5;
_hScrollBar.Visible = _hScrollBar.LargeChange < _hScrollBar.Maximum;
_hScrollBar.Value = Math.Min(_hScrollBar.Value, _hScrollBar.Maximum - _hScrollBar.LargeChange + 1);
}
private void UpdateVScrollBar()
{
_vScrollBar.Maximum = Math.Max(RowCount - 1, 0);
_vScrollBar.LargeChange = PageRowCount;
_vScrollBar.Visible = _vScrollBar.LargeChange <= _vScrollBar.Maximum;
_vScrollBar.Value = Math.Min(_vScrollBar.Value, _vScrollBar.Maximum - _vScrollBar.LargeChange + 1);
}
private void CreatePens()
{
CreateLinePen();
CreateMarkPen();
}
private void CreateMarkPen()
{
GraphicsPath path = new GraphicsPath();
path.AddLines(new Point[] { new Point(0, 0), new Point(1, 1), new Point(-1, 1), new Point(0, 0) });
CustomLineCap cap = new CustomLineCap(null, path);
cap.WidthScale = 1.0f;
_markPen = new Pen(_dragDropMarkColor, _dragDropMarkWidth);
_markPen.CustomStartCap = cap;
_markPen.CustomEndCap = cap;
}
private void CreateLinePen()
{
_linePen = new Pen(_lineColor);
_linePen.DashStyle = DashStyle.Dot;
}
protected override CreateParams CreateParams
{
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
get
{
CreateParams res = base.CreateParams;
switch (BorderStyle)
{
case BorderStyle.FixedSingle:
res.Style |= 0x800000;
break;
case BorderStyle.Fixed3D:
res.ExStyle |= 0x200;
break;
}
return res;
}
}
protected override void OnGotFocus(EventArgs e)
{
DisposeEditor();
UpdateView();
ChangeInput();
base.OnGotFocus(e);
}
protected override void OnLeave(EventArgs e)
{
DisposeEditor();
UpdateView();
base.OnLeave(e);
}
#region Keys
protected override bool IsInputKey(Keys keyData)
{
if (((keyData & Keys.Up) == Keys.Up)
|| ((keyData & Keys.Down) == Keys.Down)
|| ((keyData & Keys.Left) == Keys.Left)
|| ((keyData & Keys.Right) == Keys.Right))
return true;
else
return base.IsInputKey(keyData);
}
internal void ChangeInput()
{
if ((ModifierKeys & Keys.Shift) == Keys.Shift)
{
if (!(Input is InputWithShift))
Input = new InputWithShift(this);
}
else if ((ModifierKeys & Keys.Control) == Keys.Control)
{
if (!(Input is InputWithControl))
Input = new InputWithControl(this);
}
else
{
if (!(Input.GetType() == typeof(NormalInputState)))
Input = new NormalInputState(this);
}
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (!e.Handled)
{
if (e.KeyCode == Keys.ShiftKey || e.KeyCode == Keys.ControlKey)
ChangeInput();
Input.KeyDown(e);
if (!e.Handled)
{
foreach (NodeControlInfo item in GetNodeControls(CurrentNode))
{
item.Control.KeyDown(e);
if (e.Handled)
return;
}
}
}
}
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
if (!e.Handled)
{
if (e.KeyCode == Keys.ShiftKey || e.KeyCode == Keys.ControlKey)
ChangeInput();
if (!e.Handled)
{
foreach (NodeControlInfo item in GetNodeControls(CurrentNode))
{
item.Control.KeyUp(e);
if (e.Handled)
return;
}
}
}
}
#endregion
#region Mouse
private TreeNodeAdvMouseEventArgs CreateMouseArgs(MouseEventArgs e)
{
TreeNodeAdvMouseEventArgs args = new TreeNodeAdvMouseEventArgs(e);
args.ViewLocation = e.Location;
args.AbsoluteLocation = ToAbsoluteLocation(e.Location);
args.ModifierKeys = ModifierKeys;
args.Node = GetNodeAt(e.Location);
NodeControlInfo info = GetNodeControlInfoAt(args.Node, args.AbsoluteLocation);
args.ControlBounds = info.Bounds;
args.Control = info.Control;
return args;
}
protected override void OnMouseWheel(MouseEventArgs e)
{
base.OnMouseWheel(e);
if (SystemInformation.MouseWheelScrollLines > 0)
{
int lines = e.Delta / 120 * SystemInformation.MouseWheelScrollLines;
int newValue = _vScrollBar.Value - lines;
_vScrollBar.Value = Math.Max(_vScrollBar.Minimum,
Math.Min(_vScrollBar.Maximum - _vScrollBar.LargeChange + 1, newValue));
}
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (!Focused)
Focus();
if (e.Button == MouseButtons.Left)
{
TreeColumn c = GetColumnDividerAt(e.Location);
if (c != null)
{
Input = new ResizeColumnState(this, c, e.Location);
return;
}
}
ChangeInput();
TreeNodeAdvMouseEventArgs args = CreateMouseArgs(e);
if (args.Node != null && args.Control != null)
args.Control.MouseDown(args);
if (!args.Handled)
Input.MouseDown(args);
}
protected override void OnMouseUp(MouseEventArgs e)
{
TreeNodeAdvMouseEventArgs args = CreateMouseArgs(e);
if (Input is ResizeColumnState)
Input.MouseUp(args);
else
{
base.OnMouseUp(e);
if (args.Node != null && args.Control != null)
args.Control.MouseUp(args);
if (!args.Handled)
Input.MouseUp(args);
}
}
protected override void OnMouseDoubleClick(MouseEventArgs e)
{
base.OnMouseDoubleClick(e);
TreeNodeAdvMouseEventArgs args = CreateMouseArgs(e);
if (args.Node != null)
{
OnNodeMouseDoubleClick(args);
if (args.Handled)
return;
}
if (args.Node != null && args.Control != null)
args.Control.MouseDoubleClick(args);
if (!args.Handled)
{
if (args.Node != null && args.Button == MouseButtons.Left)
args.Node.IsExpanded = !args.Node.IsExpanded;
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (Input.MouseMove(e))
return;
base.OnMouseMove(e);
SetCursor(e);
if (e.Location.Y <= ColumnHeaderHeight)
{
_toolTip.Active = false;
}
else
{
UpdateToolTip(e);
if (ItemDragMode && Dist(e.Location, ItemDragStart) > ItemDragSensivity
&& CurrentNode != null && CurrentNode.IsSelected)
{
ItemDragMode = false;
_toolTip.Active = false;
OnItemDrag(e.Button, Selection.ToArray());
}
}
}
private void SetCursor(MouseEventArgs e)
{
if (GetColumnDividerAt(e.Location) == null)
this.Cursor = Cursors.Default;
else
this.Cursor = Cursors.VSplit;
}
private TreeColumn GetColumnDividerAt(Point p)
{
if (p.Y > ColumnHeaderHeight)
return null;
int x = -OffsetX;
foreach (TreeColumn c in Columns)
{
if (c.IsVisible)
{
x += c.Width;
Rectangle rect = new Rectangle(x - DividerWidth / 2, 0, DividerWidth, ColumnHeaderHeight);
if (rect.Contains(p))
return c;
}
}
return null;
}
TreeNodeAdv _hoverNode;
NodeControl _hoverControl;
private void UpdateToolTip(MouseEventArgs e)
{
if (_showNodeToolTips)
{
TreeNodeAdvMouseEventArgs args = CreateMouseArgs(e);
if (args.Node != null)
{
if (args.Node != _hoverNode || args.Control != _hoverControl)
{
string msg = args.Control.GetToolTip(args.Node);
if (!String.IsNullOrEmpty(msg))
{
_toolTip.SetToolTip(this, msg);
_toolTip.Active = true;
}
else
_toolTip.SetToolTip(this, null);
}
}
else
_toolTip.SetToolTip(this, null);
_hoverControl = args.Control;
_hoverNode = args.Node;
}
else
_toolTip.SetToolTip(this, null);
}
#endregion
#region DragDrop
protected override void OnDragOver(DragEventArgs drgevent)
{
ItemDragMode = false;
_dragMode = true;
Point pt = PointToClient(new Point(drgevent.X, drgevent.Y));
SetDropPosition(pt);
UpdateView();
base.OnDragOver(drgevent);
}
protected override void OnDragLeave(EventArgs e)
{
_dragMode = false;
UpdateView();
base.OnDragLeave(e);
}
protected override void OnDragDrop(DragEventArgs drgevent)
{
_dragMode = false;
UpdateView();
base.OnDragDrop(drgevent);
}
#endregion
private IEnumerable<NodeControlInfo> GetNodeControls(TreeNodeAdv node)
{
if (node == null)
yield break;
int y = node.Row * RowHeight + TopMargin;
int x = (node.Level - 1) * _indent + LeftMargin;
int width = _plusMinus.MeasureSize(node).Width;
Rectangle rect = new Rectangle(x, y, width, RowHeight);
if (UseColumns && Columns.Count > 0 && Columns[0].Width < rect.Right)
rect.Width = Columns[0].Width - x;
yield return new NodeControlInfo(_plusMinus, rect);
x += (width + 1);
if (!UseColumns)
{
foreach (NodeControl c in NodeControls)
{
width = c.MeasureSize(node).Width;
rect = new Rectangle(x, y, width, RowHeight);
x += (width + 1);
yield return new NodeControlInfo(c, rect);
}
}
else
{
int right = 0;
foreach (TreeColumn col in Columns)
{
if (col.IsVisible)
{
right += col.Width;
for (int i = 0; i < NodeControls.Count; i++)
{
NodeControl nc = NodeControls[i];
if (nc.Column == col.Index)
{
bool isLastControl = true;
for (int k = i + 1; k < NodeControls.Count; k++)
if (NodeControls[k].Column == col.Index)
{
isLastControl = false;
break;
}
width = right - x;
if (!isLastControl)
width = nc.MeasureSize(node).Width;
int maxWidth = Math.Max(0, right - x);
rect = new Rectangle(x, y, Math.Min(maxWidth, width), RowHeight);
x += (width + 1);
yield return new NodeControlInfo(nc, rect);
}
}
x = right;
}
}
}
}
private static double Dist(Point p1, Point p2)
{
return Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2));
}
private void SetDropPosition(Point pt)
{
TreeNodeAdv node = GetNodeAt(pt);
_dropPosition.Node = node;
if (node != null)
{
float pos = (pt.Y - ColumnHeaderHeight - ((node.Row - FirstVisibleRow) * RowHeight)) / (float)RowHeight;
if (pos < TopEdgeSensivity)
_dropPosition.Position = NodePosition.Before;
else if (pos > (1 - BottomEdgeSensivity))
_dropPosition.Position = NodePosition.After;
else
_dropPosition.Position = NodePosition.Inside;
}
}
internal void FullUpdate()
{
CreateRowMap();
SafeUpdateScrollBars();
UpdateView();
_needFullUpdate = false;
}
internal void UpdateView()
{
if (!_suspendUpdate)
Invalidate(false);
}
private void CreateNodes()
{
Selection.Clear();
SelectionStart = null;
_root = new TreeNodeAdv(this, null);
_root.IsExpanded = true;
if (_root.Nodes.Count > 0)
CurrentNode = _root.Nodes[0];
else
CurrentNode = null;
}
internal void ReadChilds(TreeNodeAdv parentNode)
{
ReadChilds(parentNode, null);
}
private void ReadChilds(TreeNodeAdv parentNode, Collection<ExpandedNode> expandedNodes)
{
if (!parentNode.IsLeaf)
{
parentNode.IsExpandedOnce = true;
foreach (TreeNodeAdv n in parentNode.Nodes)
{
n.Parent = null;
}
parentNode.Nodes.Clear();
if (Model != null)
{
IEnumerable items = Model.GetChildren(GetPath(parentNode));
if (items != null)
{
foreach (object obj in items)
{
Collection<ExpandedNode> expandedChildren = null;
if (expandedNodes != null)
foreach(ExpandedNode str in expandedNodes)
{
if (str.Tag == obj)
{
expandedChildren = str.Children;
break;
}
}
AddNode(parentNode, obj, -1, expandedChildren);
}
}
}
}
}
private void AddNode(TreeNodeAdv parent, object tag, int index, Collection<ExpandedNode> expandedChildren)
{
TreeNodeAdv node = new TreeNodeAdv(this, tag);
node.Parent = parent;
if (index >= 0 && index < parent.Nodes.Count)
parent.Nodes.Insert(index, node);
else
parent.Nodes.Add(node);
node.IsLeaf = Model.IsLeaf(GetPath(node));
if (!LoadOnDemand)
ReadChilds(node);
else if (expandedChildren != null)
{
ReadChilds(node, expandedChildren);
node.IsExpanded = true;
}
}
private void AddNode(TreeNodeAdv parent, object tag, int index)
{
AddNode(parent, tag, index, null);
}
private void CreateRowMap()
{
_rowMap.Clear();
int row = 0;
_contentWidth = 0;
foreach (TreeNodeAdv node in ExpandedNodes)
{
node.Row = row;
_rowMap.Add(node);
if (!UseColumns)
{
Rectangle rect = GetNodeBounds(node);
_contentWidth = Math.Max(_contentWidth, rect.Right);
}
row++;
}
if (UseColumns)
{
_contentWidth = 0;
foreach (TreeColumn col in _columns)
if (col.IsVisible)
_contentWidth += col.Width;
}
}
private NodeControlInfo GetNodeControlInfoAt(TreeNodeAdv node, Point point)
{
foreach (NodeControlInfo info in GetNodeControls(node))
if (info.Bounds.Contains(point))
return info;
return NodeControlInfo.Empty;
}
private Rectangle GetNodeBounds(TreeNodeAdv node)
{
Rectangle res = Rectangle.Empty;
foreach (NodeControlInfo info in GetNodeControls(node))
{
if (res == Rectangle.Empty)
res = info.Bounds;
else
res = Rectangle.Union(res, info.Bounds);
}
return res;
}
private void _vScrollBar_ValueChanged(object sender, EventArgs e)
{
FirstVisibleRow = _vScrollBar.Value;
}
private void _hScrollBar_ValueChanged(object sender, EventArgs e)
{
OffsetX = _hScrollBar.Value;
}
private void SetIsExpanded(TreeNodeAdv root, bool value)
{
foreach (TreeNodeAdv node in root.Nodes)
{
node.IsExpanded = value;
SetIsExpanded(node, value);
}
}
public void ClearSelection()
{
while (Selection.Count > 0)
Selection[0].IsSelected = false;
}
internal void SmartFullUpdate()
{
if (_suspendUpdate || _structureUpdating)
_needFullUpdate = true;
else
FullUpdate();
}
internal bool IsMyNode(TreeNodeAdv node)
{
if (node == null)
return false;
if (node.Tree != this)
return false;
while (node.Parent != null)
node = node.Parent;
return node == _root;
}
private void UpdateSelection()
{
bool flag = false;
if (!IsMyNode(CurrentNode))
CurrentNode = null;
if (!IsMyNode(_selectionStart))
_selectionStart = null;
for (int i = Selection.Count - 1; i >= 0; i--)
if (!IsMyNode(Selection[i]))
{
flag = true;
Selection.RemoveAt(i);
}
if (flag)
OnSelectionChanged();
}
internal void UpdateHeaders()
{
UpdateView();
}
internal void UpdateColumns()
{
FullUpdate();
}
internal void ChangeColumnWidth(TreeColumn column)
{
if (!(_input is ResizeColumnState))
{
FullUpdate();
OnColumnWidthChanged(column);
}
}
#region Draw
protected override void OnPaint(PaintEventArgs e)
{
DrawContext context = new DrawContext();
context.Graphics = e.Graphics;
context.Font = this.Font;
context.Enabled = Enabled;
int y = 0;
if (UseColumns)
{
DrawColumnHeaders(e.Graphics);
y = ColumnHeaderHeight;
if (Columns.Count == 0)
return;
}
e.Graphics.ResetTransform();
e.Graphics.TranslateTransform(-OffsetX, y - (FirstVisibleRow * RowHeight));
int row = FirstVisibleRow;
while (row < RowCount && row - FirstVisibleRow <= PageRowCount)
{
TreeNodeAdv node = _rowMap[row];
context.DrawSelection = DrawSelectionMode.None;
context.CurrentEditorOwner = _currentEditorOwner;
if (_dragMode)
{
if ((_dropPosition.Node == node) && _dropPosition.Position == NodePosition.Inside)
context.DrawSelection = DrawSelectionMode.Active;
}
else
{
if (node.IsSelected && Focused)
context.DrawSelection = DrawSelectionMode.Active;
else if (node.IsSelected && !Focused && !HideSelection)
context.DrawSelection = DrawSelectionMode.Inactive;
}
context.DrawFocus = Focused && CurrentNode == node;
if (FullRowSelect)
{
context.DrawFocus = false;
if (context.DrawSelection == DrawSelectionMode.Active || context.DrawSelection == DrawSelectionMode.Inactive)
{
Rectangle focusRect = new Rectangle(OffsetX, row * RowHeight, ClientRectangle.Width, RowHeight);
if (context.DrawSelection == DrawSelectionMode.Active)
{
e.Graphics.FillRectangle(SystemBrushes.Highlight, focusRect);
context.DrawSelection = DrawSelectionMode.FullRowSelect;
}
else
{
e.Graphics.FillRectangle(SystemBrushes.InactiveBorder, focusRect);
context.DrawSelection = DrawSelectionMode.None;
}
}
}
if (ShowLines)
DrawLines(e.Graphics, node);
DrawNode(node, context);
row++;
}
if (_dropPosition.Node != null && _dragMode)
DrawDropMark(e.Graphics);
e.Graphics.ResetTransform();
DrawScrollBarsBox(e.Graphics);
}
private void DrawColumnHeaders(Graphics gr)
{
int x = 0;
VisualStyleRenderer renderer = null;
if (Application.RenderWithVisualStyles)
renderer = new VisualStyleRenderer(VisualStyleElement.Header.Item.Normal);
DrawHeaderBackground(gr, renderer, new Rectangle(0, 0, ClientRectangle.Width + 10, ColumnHeaderHeight));
gr.TranslateTransform(-OffsetX, 0);
foreach (TreeColumn c in Columns)
{
if (c.IsVisible)
{
Rectangle rect = new Rectangle(x, 0, c.Width, ColumnHeaderHeight);
x += c.Width;
DrawHeaderBackground(gr, renderer, rect);
c.Draw(gr, rect, Font);
}
}
}
private static void DrawHeaderBackground(Graphics gr, VisualStyleRenderer renderer, Rectangle rect)
{
if (renderer != null)
renderer.DrawBackground(gr, rect);
else
{
gr.FillRectangle(SystemBrushes.Control, rect);
gr.DrawLine(SystemPens.ControlDark, rect.X, rect.Bottom - 2, rect.Right, rect.Bottom - 2);
gr.DrawLine(SystemPens.ControlLightLight, rect.X, rect.Bottom - 1, rect.Right, rect.Bottom - 1);
gr.DrawLine(SystemPens.ControlDark, rect.Right - 2, rect.Y, rect.Right - 2, rect.Bottom - 2);
gr.DrawLine(SystemPens.ControlLightLight, rect.Right - 1, rect.Y, rect.Right - 1, rect.Bottom - 1);
}
}
public void DrawNode(TreeNodeAdv node, DrawContext context)
{
foreach (NodeControlInfo item in GetNodeControls(node))
{
context.Bounds = item.Bounds;
context.Graphics.SetClip(item.Bounds);
item.Control.Draw(node, context);
context.Graphics.ResetClip();
}
}
private void DrawScrollBarsBox(Graphics gr)
{
Rectangle r1 = DisplayRectangle;
Rectangle r2 = ClientRectangle;
gr.FillRectangle(SystemBrushes.Control,
new Rectangle(r1.Right, r1.Bottom, r2.Width - r1.Width, r2.Height - r1.Height));
}
private void DrawDropMark(Graphics gr)
{
if (_dropPosition.Position == NodePosition.Inside)
return;
Rectangle rect = GetNodeBounds(_dropPosition.Node);
int right = DisplayRectangle.Right - LeftMargin + OffsetX;
int y = rect.Y;
if (_dropPosition.Position == NodePosition.After)
y = rect.Bottom;
gr.DrawLine(_markPen, rect.X, y, right, y);
}
private void DrawLines(Graphics gr, TreeNodeAdv node)
{
if (UseColumns && Columns.Count > 0)
gr.SetClip(new Rectangle(0, 0, Columns[0].Width, RowCount * RowHeight + ColumnHeaderHeight));
int row = node.Row;
TreeNodeAdv curNode = node;
while (curNode != _root)
{
int level = curNode.Level;
int x = (level - 1) * _indent + NodePlusMinus.ImageSize / 2 + LeftMargin;
int width = NodePlusMinus.Width - NodePlusMinus.ImageSize / 2;
int y = row * RowHeight + TopMargin;
int y2 = y + RowHeight;
if (curNode == node)
{
int midy = y + RowHeight / 2;
gr.DrawLine(_linePen, x, midy, x + width, midy);
if (curNode.NextNode == null)
y2 = y + RowHeight / 2;
}
if (node.Row == 0)
y = RowHeight / 2;
if (curNode.NextNode != null || curNode == node)
gr.DrawLine(_linePen, x, y, x, y2);
curNode = curNode.Parent;
}
gr.ResetClip();
}
#endregion
#region Editor
public void DisplayEditor(Control control, EditableControl owner)
{
if (control == null || owner == null)
throw new ArgumentNullException();
if (CurrentNode != null)
{
DisposeEditor();
EditorContext context = new EditorContext();
context.Owner = owner;
context.CurrentNode = CurrentNode;
context.Editor = control;
SetEditorBounds(context);
_currentEditor = control;
_currentEditorOwner = owner;
UpdateView();
control.Parent = this;
control.Focus();
owner.UpdateEditor(control);
}
}
private void SetEditorBounds(EditorContext context)
{
foreach (NodeControlInfo info in GetNodeControls(context.CurrentNode))
{
if (context.Owner == info.Control && info.Control is EditableControl)
{
Point p = ToViewLocation(info.Bounds.Location);
int width = DisplayRectangle.Width - p.X;
if (UseColumns && info.Control.Column < Columns.Count)
{
Rectangle rect = GetColumnBounds(info.Control.Column);
width = rect.Right - OffsetX - p.X;
}
context.Bounds = new Rectangle(p.X, p.Y, width, info.Bounds.Height);
((EditableControl)info.Control).SetEditorBounds(context);
return;
}
}
}
private Rectangle GetColumnBounds(int column)
{
int x = 0;
for (int i = 0; i < Columns.Count; i++)
{
if (Columns[i].IsVisible)
{
if (i < column)
x += Columns[i].Width;
else
return new Rectangle(x, 0, Columns[i].Width, 0);
}
}
return Rectangle.Empty;
}
public void HideEditor()
{
this.Focus();
DisposeEditor();
}
public void UpdateEditorBounds()
{
if (_currentEditor != null)
{
EditorContext context = new EditorContext();
context.Owner = _currentEditorOwner;
context.CurrentNode = CurrentNode;
context.Editor = _currentEditor;
SetEditorBounds(context);
}
}
private void DisposeEditor()
{
if (_currentEditor != null)
_currentEditor.Parent = null;
if (_currentEditor != null)
_currentEditor.Dispose();
_currentEditor = null;
_currentEditorOwner = null;
}
#endregion
#region ModelEvents
private void BindModelEvents()
{
_model.NodesChanged += new EventHandler<TreeModelEventArgs>(_model_NodesChanged);
_model.NodesInserted += new EventHandler<TreeModelEventArgs>(_model_NodesInserted);
_model.NodesRemoved += new EventHandler<TreeModelEventArgs>(_model_NodesRemoved);
_model.StructureChanged += new EventHandler<TreePathEventArgs>(_model_StructureChanged);
}
private void UnbindModelEvents()
{
_model.NodesChanged -= new EventHandler<TreeModelEventArgs>(_model_NodesChanged);
_model.NodesInserted -= new EventHandler<TreeModelEventArgs>(_model_NodesInserted);
_model.NodesRemoved -= new EventHandler<TreeModelEventArgs>(_model_NodesRemoved);
_model.StructureChanged -= new EventHandler<TreePathEventArgs>(_model_StructureChanged);
}
private void _model_StructureChanged(object sender, TreePathEventArgs e)
{
if (e.Path == null)
throw new ArgumentNullException();
TreeNodeAdv node = FindNode(e.Path);
if (node != null)
{
Collection<ExpandedNode> expandedNodes = null;
if (KeepNodesExpanded && node.IsExpanded)
{
expandedNodes = FindExpandedNodes(node);
}
_structureUpdating = true;
try
{
ReadChilds(node, expandedNodes);
UpdateSelection();
}
finally
{
_structureUpdating = false;
}
SmartFullUpdate();
}
}
private Collection<ExpandedNode> FindExpandedNodes(TreeNodeAdv parent)
{
Collection<ExpandedNode> expandedNodes = null;
expandedNodes = new Collection<ExpandedNode>();
foreach (TreeNodeAdv child in parent.Nodes)
{
if (child.IsExpanded)
{
ExpandedNode str = new ExpandedNode();
str.Tag = child.Tag;
str.Children = FindExpandedNodes(child);
expandedNodes.Add(str);
}
}
return expandedNodes;
}
private void _model_NodesRemoved(object sender, TreeModelEventArgs e)
{
TreeNodeAdv parent = FindNode(e.Path);
if (parent != null)
{
if (e.Indices != null)
{
List<int> list = new List<int>(e.Indices);
list.Sort();
for (int n = list.Count - 1; n >= 0; n--)
{
int index = list[n];
if (index >= 0 && index <= parent.Nodes.Count)
{
parent.Nodes[index].Parent = null;
parent.Nodes.RemoveAt(index);
}
else
throw new ArgumentOutOfRangeException("Index out of range");
}
}
else
{
for (int i = parent.Nodes.Count - 1; i >= 0; i--)
{
for (int n = 0; n < e.Children.Length; n++)
if (parent.Nodes[i].Tag == e.Children[n])
{
parent.Nodes[i].Parent = null;
parent.Nodes.RemoveAt(i);
break;
}
}
}
}
UpdateSelection();
SmartFullUpdate();
}
private void _model_NodesInserted(object sender, TreeModelEventArgs e)
{
if (e.Indices == null)
throw new ArgumentNullException("Indices");
TreeNodeAdv parent = FindNode(e.Path);
if (parent != null)
{
for (int i = 0; i < e.Children.Length; i++)
AddNode(parent, e.Children[i], e.Indices[i]);
}
SmartFullUpdate();
}
private void _model_NodesChanged(object sender, TreeModelEventArgs e)
{
TreeNodeAdv parent = FindNode(e.Path);
if (parent != null)
{
if (e.Indices != null)
{
foreach (int index in e.Indices)
{
if (index >= 0 && index < parent.Nodes.Count)
{
TreeNodeAdv node = parent.Nodes[index];
Rectangle rect = GetNodeBounds(node);
_contentWidth = Math.Max(_contentWidth, rect.Right);
}
else
throw new ArgumentOutOfRangeException("Index out of range");
}
}
else
{
foreach (TreeNodeAdv node in parent.Nodes)
{
foreach (object obj in e.Children)
if (node.Tag == obj)
{
Rectangle rect = GetNodeBounds(node);
_contentWidth = Math.Max(_contentWidth, rect.Right);
}
}
}
}
SafeUpdateScrollBars();
UpdateView();
}
public TreeNodeAdv FindNode(TreePath path)
{
if (path.IsEmpty())
return _root;
else
return FindNode(_root, path, 0);
}
private TreeNodeAdv FindNode(TreeNodeAdv root, TreePath path, int level)
{
foreach (TreeNodeAdv node in root.Nodes)
if (node.Tag == path.FullPath[level])
{
if (level == path.FullPath.Length - 1)
return node;
else
return FindNode(node, path, level + 1);
}
return null;
}
#endregion
}
}