123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- using CustomControls;
- using System.Windows.Controls;
- namespace System.Windows.Forms
- {
- public partial class TreeView : Control
- {
- private bool mouseMoved;
- private Point mouseDownPoint;
- private TreeNode selectedNodeAtMouseDown;
- private TreeNode nodeAtMouseDown;
- private TreeNode editedNode;
- private Timer labelEditTimer;
- private bool nodeDoubleClicked;
- protected new CustomControls.TreeView control { get; }
- public TreeNodeCollection Nodes { get; }
- public bool CheckBoxes
- {
- get => control.CheckBoxes == Visibility.Visible;
- set => control.CheckBoxes = value ? Visibility.Visible : Visibility.Collapsed;
- }
- private ImageList imageList;
- public ImageList ImageList
- {
- get => imageList;
- set
- {
- imageList = value;
- control.Images = value != null ? Visibility.Visible : Visibility.Collapsed;
- ResetImageList();
- }
- }
- public bool LabelEdit { get; set; }
- private TreeNode selectedNode;
- public TreeNode SelectedNode
- {
- get => selectedNode;
- set
- {
- if (value != selectedNode)
- {
- if (value != null)
- {
- value.IsSelected = true;
- }
- else if (SelectedNode != null)
- {
- SelectedNode.IsSelected = false;
- }
- selectedNode = value;
-
- SelectItemHelper.Select(control, value);
- }
- }
- }
- public bool HideSelection
- {
- get => control.HideSelection;
- set => control.HideSelection = value;
- }
- public string PathSeparator { get; set; }
- public bool Sorted { get; set; } // TODO?
- // not applicable / ignored
- public int ImageIndex { get; set; }
- public int SelectedImageIndex { get; set; }
- public bool ShowRootLines { get; set; }
- public bool ShowPlusMinus { get; set; }
- public bool ShowLines { get; set; }
- public int Indent { get; set; }
- public bool RightToLeftLayout { get; set; }
- public bool HotTracking { get; set; }
- public int ItemHeight { get; set; }
- public event TreeViewEventHandler AfterCheck;
- public event TreeViewEventHandler AfterCollapse;
- public event TreeViewEventHandler AfterExpand;
- public event TreeViewEventHandler AfterSelect;
- public event TreeViewCancelEventHandler BeforeCheck;
- public event TreeViewCancelEventHandler BeforeCollapse;
- public event TreeViewCancelEventHandler BeforeExpand;
- public event TreeViewCancelEventHandler BeforeSelect;
- public event ItemDragEventHandler ItemDrag;
- public event TreeNodeMouseClickEventHandler NodeMouseClick;
- public event TreeNodeMouseClickEventHandler NodeMouseDoubleClick;
- public event NodeLabelEditEventHandler AfterLabelEdit;
- public event NodeLabelEditEventHandler BeforeLabelEdit;
- public new event MouseEventHandler MouseUp;
- public new event KeyEventHandler KeyDown;
- private void WireEvents()
- {
- control.AcceptEdit += (sender, e) => editedNode?.EndEdit(false);
- control.CancelEdit += (sender, e) => editedNode?.EndEdit(true);
- control.PreviewMouseDown += (sender, e) =>
- {
- mouseMoved = false;
- selectedNodeAtMouseDown = SelectedNode;
- mouseDownPoint = e.GetPosition(control);
- // do not allow change selection in MouseDown. Do it in MouseUp to be consistent with WinForms
- nodeAtMouseDown = NodeHitTest(mouseDownPoint);
- if (nodeAtMouseDown != null)
- {
- e.Handled = true;
- if (e.ClickCount == 2 && e.LeftButton == Input.MouseButtonState.Pressed)
- nodeDoubleClicked = true;
- }
- };
- control.MouseMove += (sender, e) =>
- {
- if (e.LeftButton == Input.MouseButtonState.Pressed)
- {
- var pos = e.GetPosition(control);
- if (!mouseMoved && Math.Sqrt(Math.Pow(mouseDownPoint.X - pos.X, 2) + Math.Pow(mouseDownPoint.Y - pos.Y, 2)) > 5)
- {
- if (nodeAtMouseDown != null)
- {
- control.Focus();
- OnItemDrag(new ItemDragEventArgs(MouseButtons.Left, nodeAtMouseDown));
- }
- mouseMoved = true;
- }
- }
- };
- control.MouseUp += (sender, e) =>
- {
- if (!mouseMoved)
- {
- var pos = e.GetPosition(control);
- var node = NodeHitTest(pos);
- if (node != null)
- {
- // without this, editing label won't work correctly
- control.Focus();
- mouseMoved = true; // workaround: do not allow MouseMove if context menu was shown in the OnNodeMouseClick
- if (nodeDoubleClicked)
- {
- StopLabelEditTimer();
- OnNodeMouseDoubleClick(new TreeNodeMouseClickEventArgs(nodeAtMouseDown, MouseButtons.Left, 0, (int)(mouseDownPoint.X * DpiScale), (int)(mouseDownPoint.Y * DpiScale)));
- }
- else
- {
- OnNodeMouseClick(new TreeNodeMouseClickEventArgs(node, Helper.GetMouseButton(e.ChangedButton), 0, (int)(pos.X * DpiScale), (int)(pos.Y * DpiScale)));
- }
- OnMouseUp(Helper.GetMouseEventArgs(control, e));
- if (e.ChangedButton == Input.MouseButton.Left)
- {
- node.IsSelected = true;
- if (LabelEdit && !nodeDoubleClicked && selectedNodeAtMouseDown == node && NodeHitTest(pos, true) != null)
- StartLabelEditTimer();
- }
- nodeDoubleClicked = false;
- }
- }
- };
- control.PreviewKeyDown += (sender, e) =>
- {
- if (editedNode == null)
- {
- var args = Helper.GetKeyEventArgs(e);
- OnKeyDown(args);
- if (args.Handled)
- e.Handled = true;
- }
- };
- }
- private TreeNode NodeHitTest(Point point, bool textOnly = false)
- {
- var item = control.InputHitTest(point);
- if (item is TextBlock txt && txt.Name == "PART_TextBlock")
- {
- var node = txt.DataContext as TreeNode;
- if (node != null)
- {
- var rect = txt.BoundsRelativeTo(control);
- node.SetBounds(new Drawing.Rectangle((int)(rect.Left * DpiScale), (int)(rect.Top * DpiScale), (int)(rect.Width * DpiScale), (int)(rect.Height * DpiScale)));
- }
- return node;
- }
- if (!textOnly && item is Image img && img.Name == "PART_Image")
- return img.DataContext as TreeNode;
- return null;
- }
- private void ResetImageList()
- {
- foreach (var node in Nodes)
- node.ResetImageSourceAll();
- }
- private void StartLabelEditTimer()
- {
- if (labelEditTimer == null)
- labelEditTimer = new Timer();
- labelEditTimer.Interval = SystemInformation.DoubleClickTime;
- labelEditTimer.Start();
- labelEditTimer.Tick += (s, e) =>
- {
- labelEditTimer.Stop();
- selectedNodeAtMouseDown?.BeginEdit();
- };
- }
- private void StopLabelEditTimer()
- {
- if (labelEditTimer != null)
- {
- labelEditTimer.Stop();
- labelEditTimer = null;
- }
- }
- protected new virtual void OnMouseUp(MouseEventArgs e) => MouseUp?.Invoke(this, e);
- protected new virtual void OnKeyDown(KeyEventArgs e) => KeyDown?.Invoke(this, e);
- protected virtual void OnAfterCheck(TreeViewEventArgs e) => AfterCheck?.Invoke(this, e);
- protected virtual void OnAfterCollapse(TreeViewEventArgs e) => AfterCollapse?.Invoke(this, e);
- protected virtual void OnAfterExpand(TreeViewEventArgs e) => AfterExpand?.Invoke(this, e);
- protected virtual void OnAfterSelect(TreeViewEventArgs e) => AfterSelect?.Invoke(this, e);
- protected virtual void OnBeforeCheck(TreeViewCancelEventArgs e) => BeforeCheck?.Invoke(this, e);
- protected virtual void OnBeforeCollapse(TreeViewCancelEventArgs e) => BeforeCollapse?.Invoke(this, e);
- protected virtual void OnBeforeExpand(TreeViewCancelEventArgs e) => BeforeExpand?.Invoke(this, e);
- protected virtual void OnBeforeSelect(TreeViewCancelEventArgs e) => BeforeSelect?.Invoke(this, e);
- protected virtual void OnItemDrag(ItemDragEventArgs e) => ItemDrag?.Invoke(this, e);
- protected virtual void OnNodeMouseDoubleClick(TreeNodeMouseClickEventArgs e) => NodeMouseDoubleClick?.Invoke(this, e);
-
- protected virtual void OnNodeMouseClick(TreeNodeMouseClickEventArgs e) => NodeMouseClick?.Invoke(this, e);
- protected virtual void OnAfterLabelEdit(NodeLabelEditEventArgs e) => AfterLabelEdit?.Invoke(this, e);
- protected virtual void OnBeforeLabelEdit(NodeLabelEditEventArgs e) => BeforeLabelEdit?.Invoke(this, e);
- internal bool DoNodeSelect(TreeNode node)
- {
- var args = new TreeViewCancelEventArgs(node, false, TreeViewAction.Unknown);
- OnBeforeSelect(args);
- if (args.Cancel)
- return false;
- selectedNode = node;
- OnAfterSelect(new TreeViewEventArgs(node));
- return true;
- }
- internal void DoAfterCheck(TreeViewEventArgs e) => OnAfterCheck(e);
- internal void DoAfterCollapse(TreeViewEventArgs e) => OnAfterCollapse(e);
- internal void DoAfterExpand(TreeViewEventArgs e) => OnAfterExpand(e);
- internal void DoBeforeCheck(TreeViewCancelEventArgs e) => OnBeforeCheck(e);
- internal void DoBeforeExpand(TreeViewCancelEventArgs e) => OnBeforeExpand(e);
- internal void DoBeforeCollapse(TreeViewCancelEventArgs e) => OnBeforeCollapse(e);
- internal void DoOnAfterLabelEdit(NodeLabelEditEventArgs e) => OnAfterLabelEdit(e);
- internal void DoOnBeforeLabelEdit(NodeLabelEditEventArgs e) => OnBeforeLabelEdit(e);
- internal void SetEditedNode(TreeNode node) => editedNode = node;
- public void ExpandAll()
- {
- foreach (var node in Nodes)
- node.ExpandAll();
- }
- public void CollapseAll()
- {
- foreach (var node in Nodes)
- node.CollapseAll();
- }
- public TreeNode GetNodeAt(Drawing.Point point) => GetNodeAt(point.X, point.Y);
- public TreeNode GetNodeAt(int x, int y) => NodeHitTest(new Point(x / DpiScale, y / DpiScale));
- public void BeginUpdate() { Nodes.RaiseListChangedEvents = false; }
- public void EndUpdate() { Nodes.RaiseListChangedEvents = true; Nodes.ResetBindings(); }
- public TreeView()
- {
- Nodes = new(null);
- Nodes.SetOwner(this);
- control = new();
- control.ItemsSource = Nodes;
- VirtualizingPanel.SetIsVirtualizing(control, true);
- VirtualizingPanel.SetVirtualizationMode(control, VirtualizationMode.Recycling);
- WireEvents();
- // do this after wiring events. Some event handlers inside SetControl may set the Handled flag to true which prevents additional event handlers to fire up
- SetControl(control);
- PathSeparator = "\\";
- }
- }
- }
|