Existe-t-il une fonction intégrée dans .Net 2.0 pour développer TreeNode
s en survolant une opération de glisser-déposer?
J’utilise C # dans Visual Studio 2005.
J’ai Treeview
un contrôle Treeview
avec une arborescence multi-niveaux et multinodée (pensez à un organigramme Treeview
à une boîte de dialog de fichier / dossier) et je souhaite utiliser le glisser-déposer pour déplacer les nœuds dans l’arborescence.
Le code de glisser-déposer fonctionne bien et je peux déposer sur n’importe quel nœud visible, mais j’aimerais que mon contrôle se comporte comme l’explorateur Windows lors du déplacement de fichiers sur le volet de navigation. Plus précisément, j’aimerais que chaque dossier s’ouvre s’il est survolé pendant environ une seconde ou deux.
J’ai commencé à développer une solution utilisant Threading
et une méthode Sleep
, mais je rencontre des problèmes et je me demandais s’il y avait déjà quelque chose en place. Sinon, je vais m’attarder et apprendre à utiliser le threading (il est grand temps, mais j’étais en espérant faire sortir cette application rapidement)
Dois-je écrire mon propre code pour gérer le développement d’un TreeNode
lorsqu’il est survolé en mode glisser-déposer?
Vous pouvez utiliser l’événement DragOver. il se déclenche de manière répétée pendant que vous faites glisser un object L’ouverture après un délai peut se faire très facilement avec deux variables supplémentaires qui notent le dernier object sous la souris et l’heure. Aucun thread ou autre astuce nécessaire (lastDragDestination et lastDragDestinationTime dans mon exemple)
De mon propre code:
TreeNode lastDragDestination = null; DateTime lastDragDestinationTime; private void tvManager_DragOver(object sender, DragEventArgs e) { IconObject dragDropObject = null; TreeNode dragDropNode = null; //always disallow by default e.Effect = DragDropEffects.None; //make sure we have data to transfer if (e.Data.GetDataPresent(typeof(TreeNode))) { dragDropNode = (TreeNode)e.Data.GetData(typeof(TreeNode)); dragDropObject = (IconObject)dragDropNode.Tag; } else if (e.Data.GetDataPresent(typeof(ListViewItem))) { ListViewItem temp (ListViewItem)e.Data.GetData(typeof(ListViewItem)); dragDropObject = (IconObject)temp.Tag; } if (dragDropObject != null) { TreeNode destinationNode = null; //get current location Point pt = new Point(eX, eY); pt = tvManager.PointToClient(pt); destinationNode = tvManager.GetNodeAt(pt); if (destinationNode == null) { return; } //if we are on a new object, reset our timer //otherwise check to see if enough time has passed and expand the destination node if (destinationNode != lastDragDestination) { lastDragDestination = destinationNode; lastDragDestinationTime = DateTime.Now; } else { TimeSpan hoverTime = DateTime.Now.Subtract(lastDragDestinationTime); if (hoverTime.TotalSeconds > 2) { destinationNode.Expand(); } } } }
MODIFIER
J’ai une nouvelle solution, un peu tirée par les cheveux, mais cela fonctionne … Elle utilise une classe DelayedAction
pour gérer l’exécution différée d’une action sur le thread principal:
DelayedAction
public class DelayedAction { private SynchronizationContext _syncContext; private Action _action; private int _delay; private Thread _thread; public DelayedAction(Action action) : this(action, 0) { } public DelayedAction(Action action, int delay) { _action = action; _delay = delay; _syncContext = SynchronizationContext.Current; } public void RunAfterDelay() { RunAfterDelay(_delay, default(T)); } public void RunAfterDelay(T param) { RunAfterDelay(_delay, param); } public void RunAfterDelay(int delay) { RunAfterDelay(delay, default(T)); } public void RunAfterDelay(int delay, T param) { Cancel(); InitThread(delay, param); _thread.Start(); } public void Cancel() { if (_thread != null && _thread.IsAlive) { _thread.Abort(); } _thread = null; } private void InitThread(int delay, T param) { ThreadStart ts = () => { Thread.Sleep(delay); _syncContext.Send( (state) => { _action((T)state); }, param); }; _thread = new Thread(ts); } }
AutoExpandTreeView
public class AutoExpandTreeView : TreeView { DelayedAction _expandNode; public AutoExpandTreeView() { _expandNode = new DelayedAction ((node) => node.Expand(), 500); } private TreeNode _prevNode; protected override void OnDragOver(DragEventArgs e) { Point clientPos = PointToClient(new Point(eX, eY)); TreeViewHitTestInfo hti = HitTest(clientPos); if (hti.Node != null && hti.Node != _prevNode) { _prevNode = hti.Node; _expandNode.RunAfterDelay(hti.Node); } base.OnDragOver(e); } }