public RectangleF GetNodeBounds(TreeNodeEx node) { if (node.parent != null) { int nodeFromTop = -1; NodeEnumeratorEx nodes = new NodeEnumeratorEx(this.nodes); while (nodes.MoveNext()) { if (nodes.currentNode == topNode) { // We are at the top of the control. nodeFromTop = 0; } if (nodes.currentNode == node) { using (Graphics g = CreateGraphics()) { return GetTextBounds(g, node, nodeFromTop, nodes.level); } } if (nodeFromTop >= 0) { nodeFromTop++; } } } return Rectangle.Empty; }
// Render the treeview starting from startingLine internal void Draw(Graphics g, TreeNodeEx startNode) { if (updating > 0) { return; } Rectangle clientRectangle = ClientRectangle; int drawableHeight = clientRectangle.Height; int drawableWidth = clientRectangle.Width - xOffset; // We count the visible rows to see if we need the v scrollbar but we wait before deciding if we need the h scroll bar. bool needsHScrollBar = false; bool needsVScrollBar = GetNeedVScrollBar() && scrollable; bool createNewVScrollBar = false; bool createNewHScrollBar = false; if (needsVScrollBar) { // Don't allow drawing on the area that is going to be the scroll bar. // Create the scroll bar so we can get its width. if (vScrollBar == null) { vScrollBar = new Forms.VScrollBar(); createNewVScrollBar = true; } drawableWidth -= vScrollBar.Width; Rectangle rect = new Rectangle(drawableWidth + xOffset, 0, vScrollBar.Width, clientRectangle.Height); g.ExcludeClip(rect); } else { // Check to see if the top node is not the first node and we have room for the whole tree. // If so, abandon the draw and redraw the whole tree from the top. if (topNode != null && topNode != this.nodes[0]) { topNode = null; Invalidate(); return; } if (vScrollBar != null) { // We don't need the scroll bar anymore. Controls.Remove(vScrollBar); vScrollBar.Dispose(); vScrollBar = null; } } // Is the node being processed on the screen. bool drawing = false; // Start counting from the top. int nodeFromTop = -1; // Number of nodes. int nodeCount = 0; int topNodePosition = 0; // The maximum width of a displayed node. float maxWidth = 0; //StringFormat format = new StringFormat(StringFormatFlags.NoWrap); if (topNode == null && this.nodes.Count > 0) { topNode = this.nodes[0]; } RectangleF textBounds = Rectangle.Empty; NodeEnumeratorEx nodes = new NodeEnumeratorEx(this.nodes); using (Pen markerPen = new Pen(SystemColors.ControlDarkDark)) { markerPen.DashStyle = DashStyle.Dot; while (nodes.MoveNext()) { // If we havnt started drawing yet, then see if we need to and if so clear the background. if (!drawing) { if (nodes.currentNode == topNode) { // We are at the top node. nodeFromTop = 0; topNodePosition = nodeCount; } // Check to see if we must start drawing. Clear the background. if (nodeFromTop >= 0 && (nodes.currentNode == startNode || startNode == root)) { // Clear background. int y = ItemHeight * nodeFromTop; using (SolidBrush b = new SolidBrush(BackColor)) { g.FillRectangle(b, 0, y, ClientSize.Width, ClientSize.Height - y); } drawing = true; } } // Even if we arnt drawing nodes yet, we need to measure if the nodes are visible, for hscrollbar purposes. if (nodeFromTop >= 0 && drawableHeight > 0) { textBounds = GetTextBounds(g, nodes.currentNode, nodeFromTop, nodes.level); // Is the text too wide to fit in - if so we need an h scroll bar. if (textBounds.Right > drawableWidth && !needsHScrollBar && scrollable) { needsHScrollBar = true; if (hScrollBar == null) { hScrollBar = new Forms.HScrollBar(); createNewHScrollBar = true; } drawableHeight -= hScrollBar.Height; // Don't allow drawing on the area that is going to be the scroll bar. Rectangle rect = new Rectangle(0, clientRectangle.Height - hScrollBar.Height, clientRectangle.Width, hScrollBar.Height); g.ExcludeClip(rect); } if (textBounds.Right > maxWidth) { maxWidth = textBounds.Right; } } // Draw the node if we still have space. if (drawing && drawableHeight > 0) { RectangleF bounds; // Draw the lines and the expander. DrawExpanderMarker(g, markerPen, nodes.currentNode, nodeFromTop, nodes.level); // Draw checkboxes. if (checkBoxes) { bounds = GetCheckBounds(nodeFromTop, nodes.level); Forms.ButtonState state; if (nodes.currentNode.isChecked) { state = Forms.ButtonState.Checked; } else { state = Forms.ButtonState.Normal; } Forms.ControlPaint.DrawCheckBox(g, RectIFromRectF(bounds), state); } // Draw the node image. if (imageList != null) { bounds = GetImageBounds(nodeFromTop, nodes.level); int index = GetDisplayIndex(nodes.currentNode); if (index < imageList.Images.Count && index >= 0) { Image image = imageList.Images[index]; g.DrawImage(image, bounds.X, bounds.Y); } } bounds = textBounds; // The height may be too small now. // If we are currently editing a node then dont draw it. if (drawableHeight > 0 && nodes.currentNode != editNode) { // Draw the node text. var bnds = RectIFromRectF(bounds); if (selectedDropNode == nodes.currentNode) { if (dragMoveDirection == DragMoveDirection.Above) g.DrawLine(Pens.Black, new PointF(bnds.X, bnds.Y), new PointF(bnds.X + bnds.Width, bnds.Y)); else if (dragMoveDirection == DragMoveDirection.Below) g.DrawLine(Pens.Black, new PointF(bnds.X, bnds.Y + bnds.Height), new PointF(bnds.X + bnds.Width, bnds.Y + bnds.Height)); else g.FillRectangle(SystemBrushes.Highlight, bnds); } if ((selectedDropNode == nodes.currentNode && dragMoveDirection == DragMoveDirection.On) || (((nodeToBeDropped == null && nodes.currentNode == selectedNode) || (nodes.currentNode == nodeToBeDropped)) && (Focused || !hideSelection))) { // TODO: FullRowSelect g.FillRectangle(SystemBrushes.Highlight, bnds); Forms.TextRenderer.DrawText(g, nodes.currentNode.Text, Font, bnds, SystemColors.HighlightText, Forms.TextFormatFlags.NoClipping); // Draw the focus rectangle. Rectangle r = new Rectangle((int)(bounds.X), (int)(bounds.Y), (int)(bounds.Width), (int)(bounds.Height)); Forms.ControlPaint.DrawFocusRectangle(g, r); } else { Forms.TextRenderer.DrawText(g, nodes.currentNode.Text, Font, bnds, SystemColors.ControlText, Forms.TextFormatFlags.NoClipping); } } drawableHeight -= ItemHeight; } if (nodeFromTop >= 0) { nodeFromTop++; } nodeCount++; } } // If we need a v scroll bar, then set it up. if (needsVScrollBar) { SetupVScrollBar(nodeCount, needsHScrollBar, createNewVScrollBar, topNodePosition); } if (needsHScrollBar) { SetupHScrollBar(needsVScrollBar, (int)maxWidth, createNewHScrollBar, g); } else if (hScrollBar != null) { // We dont need the scroll bar. // If we have scrolled then we need to reset the position. if (xOffset != 0) { xOffset = 0; Invalidate(); } Controls.Remove(hScrollBar); hScrollBar.Dispose(); hScrollBar = null; } }
private void vScrollBar_ValueChanged(object sender, EventArgs e) { int nodeFromTop = 0; NodeEnumeratorEx nodes = new NodeEnumeratorEx(this.nodes); while (nodes.MoveNext()) { if (nodeFromTop == vScrollBar.Value) { topNode = nodes.currentNode; Invalidate(); return; } nodeFromTop++; } }
public TreeNodeEx GetNodeAt(int x, int y) { int height = ItemHeight; int nodeFromTop = -1; NodeEnumeratorEx nodes = new NodeEnumeratorEx(this.nodes); while (nodes.MoveNext()) { if (nodes.currentNode == topNode) { // We are now at the top of the control. nodeFromTop = 1; } if (nodeFromTop > -1) { if (y < height * nodeFromTop) { return nodes.currentNode; } nodeFromTop++; } } return null; }
void ProcessClick(int x, int y, bool rightMouse) { int nodeFromTop = -1; int height = ItemHeight; using (Graphics g = CreateGraphics()) { // Iterate through all the nodes, looking for the bounds that match. NodeEnumeratorEx nodes = new NodeEnumeratorEx(this.nodes); while (nodes.MoveNext()) { if (nodes.currentNode == topNode) { // We are now at the top of the control. nodeFromTop = 0; } if (nodeFromTop > -1) { // Check if the y matches this node. if (y < height * (nodeFromTop + 1)) { bool allowEdit = false; bool allowSelect = true; // Clicking the image can be used to select. if (imageList == null || !GetImageBounds(nodeFromTop, nodes.level).Contains(x, y)) { // Clicking the text can be used to edit and select. // if false then the hierarchy marker must have been clicked. if (GetTextBounds(g, nodes.currentNode, nodeFromTop, nodes.level).Contains(x, y)) { allowEdit = true; } else { allowSelect = false; } } if (SelectedNode == nodes.Current && mouseClickTimer != null && mouseClickTimer.Enabled && (allowEdit || allowSelect)) { mouseClickTimer.Stop(); nodeToEdit = null; nodes.currentNode.Toggle(); return; } if (allowSelect || rightMouse) { if (selectedNode == nodes.Current) { if (labelEdit && allowEdit && !rightMouse) { if (selectedNode.CanRename()) nodeToEdit = nodes.currentNode; } Focus(); } else { nodeToEdit = null; // Do the events. TreeViewExCancelEventArgs eventArgs = new TreeViewExCancelEventArgs(nodes.currentNode, false, TreeViewExAction.ByMouse); OnBeforeSelect(eventArgs); if (!eventArgs.Cancel) { SelectedNode = nodes.currentNode; Focus(); } } if (rightMouse) { return; } if (mouseClickTimer == null) { mouseClickTimer = new Forms.Timer(); mouseClickTimer.Tick +=new EventHandler(mouseClickTimer_Tick); mouseClickTimer.Interval = mouseEditTimeout; } mouseClickTimer.Start(); break; } } nodeFromTop++; } } } }
// Returns true if we dont have vertical space to draw all the items. private bool GetNeedVScrollBar() { int fullNodes = VisibleCount; NodeEnumeratorEx nodes = new NodeEnumeratorEx(Nodes); while (nodes.MoveNext()) { if (--fullNodes == 0) { return true; } } return false; }
protected override bool ProcessDialogKey(Forms.Keys keyData) { if ((keyData & Forms.Keys.Alt) == 0) { Forms.Keys key = keyData & Forms.Keys.KeyCode; bool shiftKey = (keyData & Forms.Keys.Shift) != 0; bool controlKey = (keyData & Forms.Keys.Control) != 0; TreeNodeEx selectedNode = SelectedNode; switch (key) { case Forms.Keys.Left: if (selectedNode != null) { if (selectedNode.IsExpanded) { selectedNode.Collapse(); } else if (selectedNode.Parent != null) { SelectedNode = selectedNode.Parent; } } return true; case Forms.Keys.Right: if (selectedNode != null && selectedNode.Nodes.Count != 0) { if (selectedNode.IsExpanded) { SelectedNode = selectedNode.NextVisibleNode; } else { selectedNode.Expand(); } } return true; case Forms.Keys.Up: if (selectedNode != null) { selectedNode = selectedNode.PrevVisibleNode; if (selectedNode != null) { SelectedNode = selectedNode; } } return true; case Forms.Keys.Down: if (selectedNode != null) { selectedNode = selectedNode.NextVisibleNode; if (selectedNode != null) { SelectedNode = selectedNode; } } return true; case Forms.Keys.Home: if (Nodes[0] != null) { SelectedNode = Nodes[0]; } return true; case Forms.Keys.End: { NodeEnumeratorEx nodes = new NodeEnumeratorEx(this.nodes); while (nodes.MoveNext()) { } SelectedNode = nodes.currentNode; return true; } case Forms.Keys.Prior: { int nodePosition = 0; // Get the position of the current selected node. NodeEnumeratorEx nodes = new NodeEnumeratorEx(this.nodes); while (nodes.MoveNext()) { if (nodes.currentNode == selectedNode) { break; } nodePosition++; } nodePosition -= VisibleCountActual - 1; if (nodePosition < 0) { nodePosition = 0; } // Get the node that corresponds to the position. nodes.Reset(); while (nodes.MoveNext()) { if (nodePosition-- == 0) { break; } } // Set the selectedNode. SelectedNode = nodes.currentNode; } return true; case Forms.Keys.Next: { int rows = 0; int rowsPerPage = VisibleCountActual; NodeEnumeratorEx nodes = new NodeEnumeratorEx(this.nodes); while (nodes.MoveNext()) { if (nodes.currentNode == selectedNode || rows > 0) { rows++; if (rows >= rowsPerPage) { break; } } } SelectedNode = nodes.currentNode; return true; } } } return base.ProcessDialogKey(keyData); }
// Non Microsoft member. protected override void OnMouseDown(Forms.MouseEventArgs e) { nodeToEdit = null; if (e.Button == Forms.MouseButtons.Left) { int nodeFromTop = -1; // Iterate through all the nodes, looking for the bounds that match. NodeEnumeratorEx nodes = new NodeEnumeratorEx(this.nodes); bool _clicked = false; while (nodes.MoveNext()) { if (nodes.currentNode == topNode) { // We are now at the top of the control. nodeFromTop = 0; } if (nodeFromTop > -1) { if (GetExpanderBounds(nodeFromTop, nodes.level).Contains(e.X, e.Y)) { nodes.currentNode.Toggle(); _clicked = true; break; } else if (GetCheckBounds(nodeFromTop, nodes.level).Contains(e.X, e.Y)) { TreeViewExCancelEventArgs args = new TreeViewExCancelEventArgs(nodes.currentNode, false, TreeViewExAction.ByMouse); OnBeforeCheck(args); if (!args.Cancel) { nodes.currentNode.isChecked = !nodes.currentNode.isChecked; OnAfterCheck(new TreeViewExEventArgs(nodes.currentNode, TreeViewExAction.ByMouse)); } Invalidate(GetCheckBounds(nodeFromTop, nodes.level)); _clicked = true; break; } nodeFromTop++; } } if (!_clicked) { var pt = new Point(e.X, e.Y); var node = GetNodeAt(e.X, e.Y); if (node != null && node.Bounds.Contains(pt)) { Focus(); nodeToBeDropped = node; if (selectedNode != null) selectedNode.Invalidate(); nodeToBeDropped.Invalidate(); } } } else { ProcessClick(e.X, e.Y, true); } base.OnMouseDown(e); }
// Invalidate from startNode down. internal void InvalidateDown(TreeNodeEx startNode) { if (updating > 0 || this.nodes == null) { return; } // Find the position of startNode relative to the top node. int nodeFromTop = -1; NodeEnumeratorEx nodes = new NodeEnumeratorEx(this.nodes); while (nodes.MoveNext()) { if (nodes.currentNode == topNode) { // We are at the top of the control. nodeFromTop = 0; } if (nodes.currentNode == startNode) { break; } if (nodeFromTop >= 0) { nodeFromTop++; } } // Calculate the y position of startNode. int y = nodeFromTop * ItemHeight; // Invalidate from this position down. // Start one pixel higher to cover the focus rectangle. Invalidate(new Rectangle(0, y - 1, ClientRectangle.Width, ClientRectangle.Height - y + 1)); }