moel@345: using System; moel@345: using System.Collections.Generic; moel@345: using System.Collections.ObjectModel; moel@345: using System.ComponentModel; moel@345: using System.Drawing; moel@345: using System.Security.Permissions; moel@345: using System.Threading; moel@345: using System.Windows.Forms; moel@345: using System.Collections; moel@345: moel@345: using Aga.Controls.Tree.NodeControls; moel@345: using Aga.Controls.Threading; moel@345: moel@345: moel@345: namespace Aga.Controls.Tree moel@345: { moel@345: /// moel@345: /// Extensible advanced implemented in 100% managed C# code. moel@345: /// Features: Model/View architecture. Multiple column per node. Ability to select moel@345: /// multiple tree nodes. Different types of controls for each node column: moel@345: /// , Icon, Label... Drag and Drop highlighting. Load on moel@345: /// demand of nodes. Incremental search of nodes. moel@345: /// moel@345: public partial class TreeViewAdv : Control moel@345: { moel@345: private const int LeftMargin = 7; moel@345: internal const int ItemDragSensivity = 4; moel@345: private readonly int _columnHeaderHeight; moel@345: private const int DividerWidth = 9; moel@345: private const int DividerCorrectionGap = -2; moel@345: moel@345: private Pen _linePen; moel@345: private Pen _markPen; moel@345: private bool _suspendUpdate; moel@345: private bool _needFullUpdate; moel@345: private bool _fireSelectionEvent; moel@345: private NodePlusMinus _plusMinus; moel@345: private ToolTip _toolTip; moel@345: private DrawContext _measureContext; moel@345: private TreeColumn _hotColumn; moel@345: private IncrementalSearch _search; moel@345: private List _expandingNodes = new List(); moel@345: private AbortableThreadPool _threadPool = new AbortableThreadPool(); moel@345: moel@345: #region Public Events moel@345: moel@345: [Category("Action")] moel@345: public event ItemDragEventHandler ItemDrag; moel@345: private void OnItemDrag(MouseButtons buttons, object item) moel@345: { moel@345: if (ItemDrag != null) moel@345: ItemDrag(this, new ItemDragEventArgs(buttons, item)); moel@345: } moel@345: moel@345: [Category("Behavior")] moel@345: public event EventHandler NodeMouseClick; moel@345: private void OnNodeMouseClick(TreeNodeAdvMouseEventArgs args) moel@345: { moel@345: if (NodeMouseClick != null) moel@345: NodeMouseClick(this, args); moel@345: } moel@345: moel@345: [Category("Behavior")] moel@345: public event EventHandler NodeMouseDoubleClick; moel@345: private void OnNodeMouseDoubleClick(TreeNodeAdvMouseEventArgs args) moel@345: { moel@345: if (NodeMouseDoubleClick != null) moel@345: NodeMouseDoubleClick(this, args); moel@345: } moel@345: moel@345: [Category("Behavior")] moel@345: public event EventHandler ColumnWidthChanged; moel@345: internal void OnColumnWidthChanged(TreeColumn column) moel@345: { moel@345: if (ColumnWidthChanged != null) moel@345: ColumnWidthChanged(this, new TreeColumnEventArgs(column)); moel@345: } moel@345: moel@345: [Category("Behavior")] moel@345: public event EventHandler ColumnReordered; moel@345: internal void OnColumnReordered(TreeColumn column) moel@345: { moel@345: if (ColumnReordered != null) moel@345: ColumnReordered(this, new TreeColumnEventArgs(column)); moel@345: } moel@345: moel@345: [Category("Behavior")] moel@345: public event EventHandler ColumnClicked; moel@345: internal void OnColumnClicked(TreeColumn column) moel@345: { moel@345: if (ColumnClicked != null) moel@345: ColumnClicked(this, new TreeColumnEventArgs(column)); moel@345: } moel@345: moel@345: [Category("Behavior")] moel@345: public event EventHandler SelectionChanged; moel@345: internal void OnSelectionChanged() moel@345: { moel@345: if (SuspendSelectionEvent) moel@345: _fireSelectionEvent = true; moel@345: else moel@345: { moel@345: _fireSelectionEvent = false; moel@345: if (SelectionChanged != null) moel@345: SelectionChanged(this, EventArgs.Empty); moel@345: } moel@345: } moel@345: moel@345: [Category("Behavior")] moel@345: public event EventHandler Collapsing; moel@345: private void OnCollapsing(TreeNodeAdv node) moel@345: { moel@345: if (Collapsing != null) moel@345: Collapsing(this, new TreeViewAdvEventArgs(node)); moel@345: } moel@345: moel@345: [Category("Behavior")] moel@345: public event EventHandler Collapsed; moel@345: private void OnCollapsed(TreeNodeAdv node) moel@345: { moel@345: if (Collapsed != null) moel@345: Collapsed(this, new TreeViewAdvEventArgs(node)); moel@345: } moel@345: moel@345: [Category("Behavior")] moel@345: public event EventHandler Expanding; moel@345: private void OnExpanding(TreeNodeAdv node) moel@345: { moel@345: if (Expanding != null) moel@345: Expanding(this, new TreeViewAdvEventArgs(node)); moel@345: } moel@345: moel@345: [Category("Behavior")] moel@345: public event EventHandler Expanded; moel@345: private void OnExpanded(TreeNodeAdv node) moel@345: { moel@345: if (Expanded != null) moel@345: Expanded(this, new TreeViewAdvEventArgs(node)); moel@345: } moel@345: moel@345: [Category("Behavior")] moel@345: public event EventHandler GridLineStyleChanged; moel@345: private void OnGridLineStyleChanged() moel@345: { moel@345: if (GridLineStyleChanged != null) moel@345: GridLineStyleChanged(this, EventArgs.Empty); moel@345: } moel@345: moel@345: [Category("Behavior")] moel@345: public event ScrollEventHandler Scroll; moel@345: protected virtual void OnScroll(ScrollEventArgs e) moel@345: { moel@345: if (Scroll != null) moel@345: Scroll(this, e); moel@345: } moel@345: moel@345: [Category("Behavior")] moel@345: public event EventHandler RowDraw; moel@345: protected virtual void OnRowDraw(PaintEventArgs e, TreeNodeAdv node, DrawContext context, int row, Rectangle rowRect) moel@345: { moel@345: if (RowDraw != null) moel@345: { moel@345: TreeViewRowDrawEventArgs args = new TreeViewRowDrawEventArgs(e.Graphics, e.ClipRectangle, node, context, row, rowRect); moel@345: RowDraw(this, args); moel@345: } moel@345: } moel@345: moel@345: /// moel@345: /// Fires when control is going to draw. Can be used to change text or back color moel@345: /// moel@345: [Category("Behavior")] moel@345: public event EventHandler DrawControl; moel@345: moel@345: internal bool DrawControlMustBeFired() moel@345: { moel@345: return DrawControl != null; moel@345: } moel@345: moel@345: internal void FireDrawControl(DrawEventArgs args) moel@345: { moel@345: OnDrawControl(args); moel@345: } moel@345: moel@345: protected virtual void OnDrawControl(DrawEventArgs args) moel@345: { moel@345: if (DrawControl != null) moel@345: DrawControl(this, args); moel@345: } moel@345: moel@345: moel@345: [Category("Drag Drop")] moel@345: public event EventHandler DropNodeValidating; moel@345: protected virtual void OnDropNodeValidating(Point point, ref TreeNodeAdv node) moel@345: { moel@345: if (DropNodeValidating != null) moel@345: { moel@345: DropNodeValidatingEventArgs args = new DropNodeValidatingEventArgs(point, node); moel@345: DropNodeValidating(this, args); moel@345: node = args.Node; moel@345: } moel@345: } moel@345: #endregion moel@345: moel@345: public TreeViewAdv() moel@345: { moel@345: InitializeComponent(); moel@345: SetStyle(ControlStyles.AllPaintingInWmPaint moel@345: | ControlStyles.UserPaint moel@345: | ControlStyles.OptimizedDoubleBuffer moel@345: | ControlStyles.ResizeRedraw moel@345: | ControlStyles.Selectable moel@345: , true); moel@345: moel@345: moel@345: if (Application.RenderWithVisualStyles) moel@345: _columnHeaderHeight = 20; moel@345: else moel@345: _columnHeaderHeight = 17; moel@345: moel@345: //BorderStyle = BorderStyle.Fixed3D; moel@345: _hScrollBar.Height = SystemInformation.HorizontalScrollBarHeight; moel@345: _vScrollBar.Width = SystemInformation.VerticalScrollBarWidth; moel@345: _rowLayout = new FixedRowHeightLayout(this, RowHeight); moel@345: _rowMap = new List(); moel@345: _selection = new List(); moel@345: _readonlySelection = new ReadOnlyCollection(_selection); moel@345: _columns = new TreeColumnCollection(this); moel@345: _toolTip = new ToolTip(); moel@345: moel@345: _measureContext = new DrawContext(); moel@345: _measureContext.Font = Font; moel@345: _measureContext.Graphics = Graphics.FromImage(new Bitmap(1, 1)); moel@345: moel@345: Input = new NormalInputState(this); moel@345: _search = new IncrementalSearch(this); moel@345: CreateNodes(); moel@345: CreatePens(); moel@345: moel@345: ArrangeControls(); moel@345: moel@345: _plusMinus = new NodePlusMinus(); moel@345: _controls = new NodeControlsCollection(this); moel@345: moel@345: Font = _font; moel@345: ExpandingIcon.IconChanged += ExpandingIconChanged; moel@345: } moel@345: moel@345: void ExpandingIconChanged(object sender, EventArgs e) moel@345: { moel@345: if (IsHandleCreated && !IsDisposed) moel@345: BeginInvoke(new MethodInvoker(DrawIcons)); moel@345: } moel@345: moel@345: private void DrawIcons() moel@345: { moel@345: using (Graphics gr = Graphics.FromHwnd(this.Handle)) moel@345: { moel@345: //Apply the same Graphics Transform logic as used in OnPaint. moel@345: int y = 0; moel@345: if (UseColumns) moel@345: { moel@345: y += ColumnHeaderHeight; moel@345: if (Columns.Count == 0) moel@345: return; moel@345: } moel@345: int firstRowY = _rowLayout.GetRowBounds(FirstVisibleRow).Y; moel@345: y -= firstRowY; moel@345: gr.ResetTransform(); moel@345: gr.TranslateTransform(-OffsetX, y); moel@345: moel@345: DrawContext context = new DrawContext(); moel@345: context.Graphics = gr; moel@345: for (int i = 0; i < _expandingNodes.Count; i++) moel@345: { moel@345: foreach (NodeControlInfo item in GetNodeControls(_expandingNodes[i])) moel@345: { moel@345: if (item.Control is ExpandingIcon) moel@345: { moel@345: Rectangle bounds = item.Bounds; moel@345: if (item.Node.Parent == null && UseColumns) moel@345: bounds.Location = Point.Empty; // display root expanding icon at 0,0 moel@345: moel@345: context.Bounds = bounds; moel@345: item.Control.Draw(item.Node, context); moel@345: } moel@345: } moel@345: } moel@345: } moel@345: } moel@345: moel@345: #region Public Methods moel@345: moel@345: public TreePath GetPath(TreeNodeAdv node) moel@345: { moel@345: if (node == _root) moel@345: return TreePath.Empty; moel@345: else moel@345: { moel@345: Stack stack = new Stack(); moel@345: while (node != _root && node != null) moel@345: { moel@345: stack.Push(node.Tag); moel@345: node = node.Parent; moel@345: } moel@345: return new TreePath(stack.ToArray()); moel@345: } moel@345: } moel@345: moel@345: public TreeNodeAdv GetNodeAt(Point point) moel@345: { moel@345: NodeControlInfo info = GetNodeControlInfoAt(point); moel@345: return info.Node; moel@345: } moel@345: moel@345: public NodeControlInfo GetNodeControlInfoAt(Point point) moel@345: { moel@345: if (point.X < 0 || point.Y < 0) moel@345: return NodeControlInfo.Empty; moel@345: moel@345: int row = _rowLayout.GetRowAt(point); moel@345: if (row < RowCount && row >= 0) moel@345: return GetNodeControlInfoAt(RowMap[row], point); moel@345: else moel@345: return NodeControlInfo.Empty; moel@345: } moel@345: moel@345: private NodeControlInfo GetNodeControlInfoAt(TreeNodeAdv node, Point point) moel@345: { moel@345: Rectangle rect = _rowLayout.GetRowBounds(FirstVisibleRow); moel@345: point.Y += (rect.Y - ColumnHeaderHeight); moel@345: point.X += OffsetX; moel@345: foreach (NodeControlInfo info in GetNodeControls(node)) moel@345: if (info.Bounds.Contains(point)) moel@345: return info; moel@345: moel@345: if (FullRowSelect) moel@345: return new NodeControlInfo(null, Rectangle.Empty, node); moel@345: else moel@345: return NodeControlInfo.Empty; moel@345: } moel@345: moel@345: public void BeginUpdate() moel@345: { moel@345: _suspendUpdate = true; moel@345: SuspendSelectionEvent = true; moel@345: } moel@345: moel@345: public void EndUpdate() moel@345: { moel@345: _suspendUpdate = false; moel@345: if (_needFullUpdate) moel@345: FullUpdate(); moel@345: else moel@345: UpdateView(); moel@345: SuspendSelectionEvent = false; moel@345: } moel@345: moel@345: public void ExpandAll() moel@345: { moel@345: _root.ExpandAll(); moel@345: } moel@345: moel@345: public void CollapseAll() moel@345: { moel@345: _root.CollapseAll(); moel@345: } moel@345: moel@345: /// moel@345: /// Expand all parent nodes, andd scroll to the specified node moel@345: /// moel@345: public void EnsureVisible(TreeNodeAdv node) moel@345: { moel@345: if (node == null) moel@345: throw new ArgumentNullException("node"); moel@345: moel@345: if (!IsMyNode(node)) moel@345: throw new ArgumentException(); moel@345: moel@345: TreeNodeAdv parent = node.Parent; moel@345: while (parent != _root) moel@345: { moel@345: parent.IsExpanded = true; moel@345: parent = parent.Parent; moel@345: } moel@345: ScrollTo(node); moel@345: } moel@345: moel@345: /// moel@345: /// Make node visible, scroll if needed. All parent nodes of the specified node must be expanded moel@345: /// moel@345: /// moel@345: public void ScrollTo(TreeNodeAdv node) moel@345: { moel@345: if (node == null) moel@345: throw new ArgumentNullException("node"); moel@345: moel@345: if (!IsMyNode(node)) moel@345: throw new ArgumentException(); moel@345: moel@345: if (node.Row < 0) moel@345: CreateRowMap(); moel@345: moel@345: int row = -1; moel@345: moel@345: if (node.Row < FirstVisibleRow) moel@345: row = node.Row; moel@345: else moel@345: { moel@345: int pageStart = _rowLayout.GetRowBounds(FirstVisibleRow).Top; moel@345: int rowBottom = _rowLayout.GetRowBounds(node.Row).Bottom; moel@345: if (rowBottom > pageStart + DisplayRectangle.Height - ColumnHeaderHeight) moel@345: row = _rowLayout.GetFirstRow(node.Row); moel@345: } moel@345: moel@345: if (row >= _vScrollBar.Minimum && row <= _vScrollBar.Maximum) moel@345: _vScrollBar.Value = row; moel@345: } moel@345: moel@345: public void ClearSelection() moel@345: { moel@345: BeginUpdate(); moel@345: try moel@345: { moel@345: ClearSelectionInternal(); moel@345: } moel@345: finally moel@345: { moel@345: EndUpdate(); moel@345: } moel@345: } moel@345: moel@345: internal void ClearSelectionInternal() moel@345: { moel@345: while (Selection.Count > 0) moel@345: { moel@345: var t = Selection[0]; moel@345: t.IsSelected = false; moel@345: Selection.Remove(t); //hack moel@345: } moel@345: } moel@345: moel@345: #endregion moel@345: moel@345: protected override void OnSizeChanged(EventArgs e) moel@345: { moel@345: ArrangeControls(); moel@345: SafeUpdateScrollBars(); moel@345: base.OnSizeChanged(e); moel@345: } moel@345: moel@345: private void ArrangeControls() moel@345: { moel@345: int hBarSize = _hScrollBar.Height; moel@345: int vBarSize = _vScrollBar.Width; moel@345: Rectangle clientRect = ClientRectangle; moel@345: moel@345: _hScrollBar.SetBounds(clientRect.X, clientRect.Bottom - hBarSize, moel@345: clientRect.Width - vBarSize, hBarSize); moel@345: moel@345: _vScrollBar.SetBounds(clientRect.Right - vBarSize, clientRect.Y, moel@345: vBarSize, clientRect.Height - hBarSize); moel@345: } moel@345: moel@345: private void SafeUpdateScrollBars() moel@345: { moel@345: if (InvokeRequired) moel@345: BeginInvoke(new MethodInvoker(UpdateScrollBars)); moel@345: else moel@345: UpdateScrollBars(); moel@345: } moel@345: moel@345: private void UpdateScrollBars() moel@345: { moel@345: UpdateVScrollBar(); moel@345: UpdateHScrollBar(); moel@345: UpdateVScrollBar(); moel@345: UpdateHScrollBar(); moel@345: _hScrollBar.Width = DisplayRectangle.Width; moel@345: _vScrollBar.Height = DisplayRectangle.Height; moel@345: } moel@345: moel@345: private void UpdateHScrollBar() moel@345: { moel@345: _hScrollBar.Maximum = ContentWidth; moel@345: _hScrollBar.LargeChange = Math.Max(DisplayRectangle.Width, 0); moel@345: _hScrollBar.SmallChange = 5; moel@345: _hScrollBar.Visible = _hScrollBar.LargeChange < _hScrollBar.Maximum; moel@345: _hScrollBar.Value = Math.Min(_hScrollBar.Value, _hScrollBar.Maximum - _hScrollBar.LargeChange + 1); moel@345: } moel@345: moel@345: private void UpdateVScrollBar() moel@345: { moel@345: _vScrollBar.Maximum = Math.Max(RowCount - 1, 0); moel@345: _vScrollBar.LargeChange = _rowLayout.PageRowCount; moel@345: _vScrollBar.Visible = (RowCount > 0) && (_vScrollBar.LargeChange <= _vScrollBar.Maximum); moel@345: _vScrollBar.Value = Math.Min(_vScrollBar.Value, _vScrollBar.Maximum - _vScrollBar.LargeChange + 1); moel@345: } moel@345: moel@345: protected override CreateParams CreateParams moel@345: { moel@345: [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] moel@345: get moel@345: { moel@345: CreateParams res = base.CreateParams; moel@345: switch (BorderStyle) moel@345: { moel@345: case BorderStyle.FixedSingle: moel@345: res.Style |= 0x800000; moel@345: break; moel@345: case BorderStyle.Fixed3D: moel@345: res.ExStyle |= 0x200; moel@345: break; moel@345: } moel@345: return res; moel@345: } moel@345: } moel@345: moel@345: protected override void OnGotFocus(EventArgs e) moel@345: { moel@345: UpdateView(); moel@345: ChangeInput(); moel@345: base.OnGotFocus(e); moel@345: } moel@345: moel@345: protected override void OnFontChanged(EventArgs e) moel@345: { moel@345: base.OnFontChanged(e); moel@345: _measureContext.Font = Font; moel@345: FullUpdate(); moel@345: } moel@345: moel@345: internal IEnumerable GetNodeControls(TreeNodeAdv node) moel@345: { moel@345: if (node == null) moel@345: yield break; moel@345: Rectangle rowRect = _rowLayout.GetRowBounds(node.Row); moel@345: foreach (NodeControlInfo n in GetNodeControls(node, rowRect)) moel@345: yield return n; moel@345: } moel@345: moel@345: internal IEnumerable GetNodeControls(TreeNodeAdv node, Rectangle rowRect) moel@345: { moel@345: if (node == null) moel@345: yield break; moel@345: moel@345: int y = rowRect.Y; moel@345: int x = (node.Level - 1) * _indent + LeftMargin; moel@345: int width = 0; moel@345: if (node.Row == 0 && ShiftFirstNode) moel@345: x -= _indent; moel@345: Rectangle rect = Rectangle.Empty; moel@345: moel@345: if (ShowPlusMinus) moel@345: { moel@345: width = _plusMinus.GetActualSize(node, _measureContext).Width; moel@345: rect = new Rectangle(x, y, width, rowRect.Height); moel@345: if (UseColumns && Columns.Count > 0 && Columns[0].Width < rect.Right) moel@345: rect.Width = Columns[0].Width - x; moel@345: moel@345: yield return new NodeControlInfo(_plusMinus, rect, node); moel@345: x += width; moel@345: } moel@345: moel@345: if (!UseColumns) moel@345: { moel@345: foreach (NodeControl c in NodeControls) moel@345: { moel@345: Size s = c.GetActualSize(node, _measureContext); moel@345: if (!s.IsEmpty) moel@345: { moel@345: width = s.Width; moel@345: rect = new Rectangle(x, y, width, rowRect.Height); moel@345: x += rect.Width; moel@345: yield return new NodeControlInfo(c, rect, node); moel@345: } moel@345: } moel@345: } moel@345: else moel@345: { moel@345: int right = 0; moel@345: foreach (TreeColumn col in Columns) moel@345: { moel@345: if (col.IsVisible && col.Width > 0) moel@345: { moel@345: right += col.Width; moel@345: for (int i = 0; i < NodeControls.Count; i++) moel@345: { moel@345: NodeControl nc = NodeControls[i]; moel@345: if (nc.ParentColumn == col) moel@345: { moel@345: Size s = nc.GetActualSize(node, _measureContext); moel@345: if (!s.IsEmpty) moel@345: { moel@345: bool isLastControl = true; moel@345: for (int k = i + 1; k < NodeControls.Count; k++) moel@345: if (NodeControls[k].ParentColumn == col) moel@345: { moel@345: isLastControl = false; moel@345: break; moel@345: } moel@345: moel@345: width = right - x; moel@345: if (!isLastControl) moel@345: width = s.Width; moel@345: int maxWidth = Math.Max(0, right - x); moel@345: rect = new Rectangle(x, y, Math.Min(maxWidth, width), rowRect.Height); moel@345: x += width; moel@345: yield return new NodeControlInfo(nc, rect, node); moel@345: } moel@345: } moel@345: } moel@345: x = right; moel@345: } moel@345: } moel@345: } moel@345: } moel@345: moel@345: internal static double Dist(Point p1, Point p2) moel@345: { moel@345: return Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2)); moel@345: } moel@345: moel@345: public void FullUpdate() moel@345: { moel@345: HideEditor(); moel@345: if (InvokeRequired) moel@345: BeginInvoke(new MethodInvoker(UnsafeFullUpdate)); moel@345: else moel@345: UnsafeFullUpdate(); moel@345: } moel@345: moel@345: private void UnsafeFullUpdate() moel@345: { moel@345: _rowLayout.ClearCache(); moel@345: CreateRowMap(); moel@345: SafeUpdateScrollBars(); moel@345: UpdateView(); moel@345: _needFullUpdate = false; moel@345: } moel@345: moel@345: internal void UpdateView() moel@345: { moel@345: if (!_suspendUpdate) moel@345: Invalidate(false); moel@345: } moel@345: moel@345: internal void UpdateHeaders() moel@345: { moel@345: Invalidate(new Rectangle(0, 0, Width, ColumnHeaderHeight)); moel@345: } moel@345: moel@345: internal void UpdateColumns() moel@345: { moel@345: FullUpdate(); moel@345: } moel@345: moel@345: private void CreateNodes() moel@345: { moel@345: Selection.Clear(); moel@345: SelectionStart = null; moel@345: _root = new TreeNodeAdv(this, null); moel@345: _root.IsExpanded = true; moel@345: if (_root.Nodes.Count > 0) moel@345: CurrentNode = _root.Nodes[0]; moel@345: else moel@345: CurrentNode = null; moel@345: } moel@345: moel@345: internal void ReadChilds(TreeNodeAdv parentNode) moel@345: { moel@345: ReadChilds(parentNode, false); moel@345: } moel@345: moel@345: internal void ReadChilds(TreeNodeAdv parentNode, bool performFullUpdate) moel@345: { moel@345: if (!parentNode.IsLeaf) moel@345: { moel@345: parentNode.IsExpandedOnce = true; moel@345: parentNode.Nodes.Clear(); moel@345: moel@345: if (Model != null) moel@345: { moel@345: IEnumerable items = Model.GetChildren(GetPath(parentNode)); moel@345: if (items != null) moel@345: foreach (object obj in items) moel@345: { moel@345: AddNewNode(parentNode, obj, -1); moel@345: if (performFullUpdate) moel@345: FullUpdate(); moel@345: } moel@345: } moel@345: moel@345: if (parentNode.AutoExpandOnStructureChanged) moel@345: parentNode.ExpandAll(); moel@345: } moel@345: } moel@345: moel@345: private void AddNewNode(TreeNodeAdv parent, object tag, int index) moel@345: { moel@345: TreeNodeAdv node = new TreeNodeAdv(this, tag); moel@345: AddNode(parent, index, node); moel@345: } moel@345: moel@345: private void AddNode(TreeNodeAdv parent, int index, TreeNodeAdv node) moel@345: { moel@345: if (index >= 0 && index < parent.Nodes.Count) moel@345: parent.Nodes.Insert(index, node); moel@345: else moel@345: parent.Nodes.Add(node); moel@345: moel@345: node.IsLeaf = Model.IsLeaf(GetPath(node)); moel@345: if (node.IsLeaf) moel@345: node.Nodes.Clear(); moel@345: if (!LoadOnDemand || node.IsExpandedOnce) moel@345: ReadChilds(node); moel@345: } moel@345: moel@345: private struct ExpandArgs moel@345: { moel@345: public TreeNodeAdv Node; moel@345: public bool Value; moel@345: public bool IgnoreChildren; moel@345: } moel@345: moel@345: public void AbortBackgroundExpandingThreads() moel@345: { moel@345: _threadPool.CancelAll(true); moel@345: for (int i = 0; i < _expandingNodes.Count; i++) moel@345: _expandingNodes[i].IsExpandingNow = false; moel@345: _expandingNodes.Clear(); moel@345: Invalidate(); moel@345: } moel@345: moel@345: internal void SetIsExpanded(TreeNodeAdv node, bool value, bool ignoreChildren) moel@345: { moel@345: ExpandArgs eargs = new ExpandArgs(); moel@345: eargs.Node = node; moel@345: eargs.Value = value; moel@345: eargs.IgnoreChildren = ignoreChildren; moel@345: moel@345: if (AsyncExpanding && LoadOnDemand && !_threadPool.IsMyThread(Thread.CurrentThread)) moel@345: { moel@345: WaitCallback wc = delegate(object argument) { SetIsExpanded((ExpandArgs)argument); }; moel@345: _threadPool.QueueUserWorkItem(wc, eargs); moel@345: } moel@345: else moel@345: SetIsExpanded(eargs); moel@345: } moel@345: moel@345: private void SetIsExpanded(ExpandArgs eargs) moel@345: { moel@345: bool update = !eargs.IgnoreChildren && !AsyncExpanding; moel@345: if (update) moel@345: BeginUpdate(); moel@345: try moel@345: { moel@345: if (IsMyNode(eargs.Node) && eargs.Node.IsExpanded != eargs.Value) moel@345: SetIsExpanded(eargs.Node, eargs.Value); moel@345: moel@345: if (!eargs.IgnoreChildren) moel@345: SetIsExpandedRecursive(eargs.Node, eargs.Value); moel@345: } moel@345: finally moel@345: { moel@345: if (update) moel@345: EndUpdate(); moel@345: } moel@345: } moel@345: moel@345: internal void SetIsExpanded(TreeNodeAdv node, bool value) moel@345: { moel@345: if (Root == node && !value) moel@345: return; //Can't collapse root node moel@345: moel@345: if (value) moel@345: { moel@345: OnExpanding(node); moel@345: node.OnExpanding(); moel@345: } moel@345: else moel@345: { moel@345: OnCollapsing(node); moel@345: node.OnCollapsing(); moel@345: } moel@345: moel@345: if (value && !node.IsExpandedOnce) moel@345: { moel@345: if (AsyncExpanding && LoadOnDemand) moel@345: { moel@345: AddExpandingNode(node); moel@345: node.AssignIsExpanded(true); moel@345: Invalidate(); moel@345: } moel@345: ReadChilds(node, AsyncExpanding); moel@345: RemoveExpandingNode(node); moel@345: } moel@345: node.AssignIsExpanded(value); moel@345: SmartFullUpdate(); moel@345: moel@345: if (value) moel@345: { moel@345: OnExpanded(node); moel@345: node.OnExpanded(); moel@345: } moel@345: else moel@345: { moel@345: OnCollapsed(node); moel@345: node.OnCollapsed(); moel@345: } moel@345: } moel@345: moel@345: private void RemoveExpandingNode(TreeNodeAdv node) moel@345: { moel@345: node.IsExpandingNow = false; moel@345: _expandingNodes.Remove(node); moel@345: if (_expandingNodes.Count <= 0) moel@345: ExpandingIcon.Stop(); moel@345: } moel@345: moel@345: private void AddExpandingNode(TreeNodeAdv node) moel@345: { moel@345: node.IsExpandingNow = true; moel@345: _expandingNodes.Add(node); moel@345: ExpandingIcon.Start(); moel@345: } moel@345: moel@345: internal void SetIsExpandedRecursive(TreeNodeAdv root, bool value) moel@345: { moel@345: for (int i = 0; i < root.Nodes.Count; i++) moel@345: { moel@345: TreeNodeAdv node = root.Nodes[i]; moel@345: node.IsExpanded = value; moel@345: SetIsExpandedRecursive(node, value); moel@345: } moel@345: } moel@345: moel@345: private void CreateRowMap() moel@345: { moel@345: RowMap.Clear(); moel@345: int row = 0; moel@345: _contentWidth = 0; moel@345: foreach (TreeNodeAdv node in VisibleNodes) moel@345: { moel@345: node.Row = row; moel@345: RowMap.Add(node); moel@345: if (!UseColumns) moel@345: { moel@345: _contentWidth = Math.Max(_contentWidth, GetNodeWidth(node)); moel@345: } moel@345: row++; moel@345: } moel@345: if (UseColumns) moel@345: { moel@345: _contentWidth = 0; moel@345: foreach (TreeColumn col in _columns) moel@345: if (col.IsVisible) moel@345: _contentWidth += col.Width; moel@345: } moel@345: } moel@345: moel@345: private int GetNodeWidth(TreeNodeAdv node) moel@345: { moel@345: if (node.RightBounds == null) moel@345: { moel@345: Rectangle res = GetNodeBounds(GetNodeControls(node, Rectangle.Empty)); moel@345: node.RightBounds = res.Right; moel@345: } moel@345: return node.RightBounds.Value; moel@345: } moel@345: moel@345: internal Rectangle GetNodeBounds(TreeNodeAdv node) moel@345: { moel@345: return GetNodeBounds(GetNodeControls(node)); moel@345: } moel@345: moel@345: private Rectangle GetNodeBounds(IEnumerable nodeControls) moel@345: { moel@345: Rectangle res = Rectangle.Empty; moel@345: foreach (NodeControlInfo info in nodeControls) moel@345: { moel@345: if (res == Rectangle.Empty) moel@345: res = info.Bounds; moel@345: else moel@345: res = Rectangle.Union(res, info.Bounds); moel@345: } moel@345: return res; moel@345: } moel@345: moel@345: private void _vScrollBar_ValueChanged(object sender, EventArgs e) moel@345: { moel@345: FirstVisibleRow = _vScrollBar.Value; moel@345: } moel@345: moel@345: private void _hScrollBar_ValueChanged(object sender, EventArgs e) moel@345: { moel@345: OffsetX = _hScrollBar.Value; moel@345: } moel@345: moel@345: private void _vScrollBar_Scroll(object sender, ScrollEventArgs e) moel@345: { moel@345: OnScroll(e); moel@345: } moel@345: moel@345: private void _hScrollBar_Scroll(object sender, ScrollEventArgs e) moel@345: { moel@345: OnScroll(e); moel@345: } moel@345: moel@345: internal void SmartFullUpdate() moel@345: { moel@345: if (_suspendUpdate) moel@345: _needFullUpdate = true; moel@345: else moel@345: FullUpdate(); moel@345: } moel@345: moel@345: internal bool IsMyNode(TreeNodeAdv node) moel@345: { moel@345: if (node == null) moel@345: return false; moel@345: moel@345: if (node.Tree != this) moel@345: return false; moel@345: moel@345: while (node.Parent != null) moel@345: node = node.Parent; moel@345: moel@345: return node == _root; moel@345: } moel@345: moel@345: internal void UpdateSelection() moel@345: { moel@345: bool flag = false; moel@345: moel@345: if (!IsMyNode(CurrentNode)) moel@345: CurrentNode = null; moel@345: if (!IsMyNode(_selectionStart)) moel@345: _selectionStart = null; moel@345: moel@345: for (int i = Selection.Count - 1; i >= 0; i--) moel@345: if (!IsMyNode(Selection[i])) moel@345: { moel@345: flag = true; moel@345: Selection.RemoveAt(i); moel@345: } moel@345: moel@345: if (flag) moel@345: OnSelectionChanged(); moel@345: } moel@345: moel@345: internal void ChangeColumnWidth(TreeColumn column) moel@345: { moel@345: if (!(_input is ResizeColumnState)) moel@345: { moel@345: FullUpdate(); moel@345: OnColumnWidthChanged(column); moel@345: } moel@345: } moel@345: moel@345: public TreeNodeAdv FindNode(TreePath path) moel@345: { moel@345: return FindNode(path, false); moel@345: } moel@345: moel@345: public TreeNodeAdv FindNode(TreePath path, bool readChilds) moel@345: { moel@345: if (path.IsEmpty()) moel@345: return _root; moel@345: else moel@345: return FindNode(_root, path, 0, readChilds); moel@345: } moel@345: moel@345: private TreeNodeAdv FindNode(TreeNodeAdv root, TreePath path, int level, bool readChilds) moel@345: { moel@345: if (!root.IsExpandedOnce && readChilds) moel@345: ReadChilds(root); moel@345: moel@345: for (int i = 0; i < root.Nodes.Count; i++) moel@345: { moel@345: TreeNodeAdv node = root.Nodes[i]; moel@345: if (node.Tag == path.FullPath[level]) moel@345: { moel@345: if (level == path.FullPath.Length - 1) moel@345: return node; moel@345: else moel@345: return FindNode(node, path, level + 1, readChilds); moel@345: } moel@345: } moel@345: return null; moel@345: } moel@345: moel@345: public TreeNodeAdv FindNodeByTag(object tag) moel@345: { moel@345: return FindNodeByTag(_root, tag); moel@345: } moel@345: moel@345: private TreeNodeAdv FindNodeByTag(TreeNodeAdv root, object tag) moel@345: { moel@345: foreach (TreeNodeAdv node in root.Nodes) moel@345: { moel@345: if (node.Tag == tag) moel@345: return node; moel@345: TreeNodeAdv res = FindNodeByTag(node, tag); moel@345: if (res != null) moel@345: return res; moel@345: } moel@345: return null; moel@345: } moel@345: moel@345: public void SelectAllNodes() moel@345: { moel@345: SuspendSelectionEvent = true; moel@345: try moel@345: { moel@345: if (SelectionMode == TreeSelectionMode.MultiSameParent) moel@345: { moel@345: if (CurrentNode != null) moel@345: { moel@345: foreach (TreeNodeAdv n in CurrentNode.Parent.Nodes) moel@345: n.IsSelected = true; moel@345: } moel@345: } moel@345: else if (SelectionMode == TreeSelectionMode.Multi) moel@345: { moel@345: SelectNodes(Root.Nodes); moel@345: } moel@345: } moel@345: finally moel@345: { moel@345: SuspendSelectionEvent = false; moel@345: } moel@345: } moel@345: moel@345: private void SelectNodes(Collection nodes) moel@345: { moel@345: foreach (TreeNodeAdv n in nodes) moel@345: { moel@345: n.IsSelected = true; moel@345: if (n.IsExpanded) moel@345: SelectNodes(n.Nodes); moel@345: } moel@345: } moel@345: moel@345: #region ModelEvents moel@345: private void BindModelEvents() moel@345: { moel@345: _model.NodesChanged += new EventHandler(_model_NodesChanged); moel@345: _model.NodesInserted += new EventHandler(_model_NodesInserted); moel@345: _model.NodesRemoved += new EventHandler(_model_NodesRemoved); moel@345: _model.StructureChanged += new EventHandler(_model_StructureChanged); moel@345: } moel@345: moel@345: private void UnbindModelEvents() moel@345: { moel@345: _model.NodesChanged -= new EventHandler(_model_NodesChanged); moel@345: _model.NodesInserted -= new EventHandler(_model_NodesInserted); moel@345: _model.NodesRemoved -= new EventHandler(_model_NodesRemoved); moel@345: _model.StructureChanged -= new EventHandler(_model_StructureChanged); moel@345: } moel@345: moel@345: private void _model_StructureChanged(object sender, TreePathEventArgs e) moel@345: { moel@345: if (e.Path == null) moel@345: throw new ArgumentNullException(); moel@345: moel@345: TreeNodeAdv node = FindNode(e.Path); moel@345: if (node != null) moel@345: { moel@345: if (node != Root) moel@345: node.IsLeaf = Model.IsLeaf(GetPath(node)); moel@345: moel@345: var list = new Dictionary(); moel@345: SaveExpandedNodes(node, list); moel@345: ReadChilds(node); moel@345: RestoreExpandedNodes(node, list); moel@345: moel@345: UpdateSelection(); moel@345: SmartFullUpdate(); moel@345: } moel@345: //else moel@345: // throw new ArgumentException("Path not found"); moel@345: } moel@345: moel@345: private void RestoreExpandedNodes(TreeNodeAdv node, Dictionary list) moel@345: { moel@345: if (node.Tag != null && list.ContainsKey(node.Tag)) moel@345: { moel@345: node.IsExpanded = true; moel@345: foreach (var child in node.Children) moel@345: RestoreExpandedNodes(child, list); moel@345: } moel@345: } moel@345: moel@345: private void SaveExpandedNodes(TreeNodeAdv node, Dictionary list) moel@345: { moel@345: if (node.IsExpanded && node.Tag != null) moel@345: { moel@345: list.Add(node.Tag, null); moel@345: foreach (var child in node.Children) moel@345: SaveExpandedNodes(child, list); moel@345: } moel@345: } moel@345: moel@345: private void _model_NodesRemoved(object sender, TreeModelEventArgs e) moel@345: { moel@345: TreeNodeAdv parent = FindNode(e.Path); moel@345: if (parent != null) moel@345: { moel@345: if (e.Indices != null) moel@345: { moel@345: List list = new List(e.Indices); moel@345: list.Sort(); moel@345: for (int n = list.Count - 1; n >= 0; n--) moel@345: { moel@345: int index = list[n]; moel@345: if (index >= 0 && index <= parent.Nodes.Count) moel@345: parent.Nodes.RemoveAt(index); moel@345: else moel@345: throw new ArgumentOutOfRangeException("Index out of range"); moel@345: } moel@345: } moel@345: else moel@345: { moel@345: for (int i = parent.Nodes.Count - 1; i >= 0; i--) moel@345: { moel@345: for (int n = 0; n < e.Children.Length; n++) moel@345: if (parent.Nodes[i].Tag == e.Children[n]) moel@345: { moel@345: parent.Nodes.RemoveAt(i); moel@345: break; moel@345: } moel@345: } moel@345: } moel@345: } moel@345: UpdateSelection(); moel@345: SmartFullUpdate(); moel@345: } moel@345: moel@345: private void _model_NodesInserted(object sender, TreeModelEventArgs e) moel@345: { moel@345: if (e.Indices == null) moel@345: throw new ArgumentNullException("Indices"); moel@345: moel@345: TreeNodeAdv parent = FindNode(e.Path); moel@345: if (parent != null) moel@345: { moel@345: for (int i = 0; i < e.Children.Length; i++) moel@345: AddNewNode(parent, e.Children[i], e.Indices[i]); moel@345: } moel@345: SmartFullUpdate(); moel@345: } moel@345: moel@345: private void _model_NodesChanged(object sender, TreeModelEventArgs e) moel@345: { moel@345: TreeNodeAdv parent = FindNode(e.Path); moel@345: if (parent != null && parent.IsVisible && parent.IsExpanded) moel@345: { moel@345: if (InvokeRequired) moel@345: BeginInvoke(new UpdateContentWidthDelegate(ClearNodesSize), e, parent); moel@345: else moel@345: ClearNodesSize(e, parent); moel@345: SmartFullUpdate(); moel@345: } moel@345: } moel@345: moel@345: private delegate void UpdateContentWidthDelegate(TreeModelEventArgs e, TreeNodeAdv parent); moel@345: private void ClearNodesSize(TreeModelEventArgs e, TreeNodeAdv parent) moel@345: { moel@345: if (e.Indices != null) moel@345: { moel@345: foreach (int index in e.Indices) moel@345: { moel@345: if (index >= 0 && index < parent.Nodes.Count) moel@345: { moel@345: TreeNodeAdv node = parent.Nodes[index]; moel@345: node.Height = node.RightBounds = null; moel@345: } moel@345: else moel@345: throw new ArgumentOutOfRangeException("Index out of range"); moel@345: } moel@345: } moel@345: else moel@345: { moel@345: foreach (TreeNodeAdv node in parent.Nodes) moel@345: { moel@345: foreach (object obj in e.Children) moel@345: if (node.Tag == obj) moel@345: { moel@345: node.Height = node.RightBounds = null; moel@345: } moel@345: } moel@345: } moel@345: } moel@345: #endregion moel@345: } moel@345: }