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