При использовании перетаскивания я могу заставить Treeview развернуть node, над которым пользователь зависает?
Вкратце:
Есть ли встроенная функция в .Net 2.0 для расширения TreeNode
при зависании, пока выполняется операция перетаскивания?
Я использую С# в Visual Studio 2005.
Подробнее:
Я заполнил элемент управления Treeview
с многоуровневым многострочным деревом (подумайте об организационной диаграмме или диалоге файлов/папок), и я хочу использовать перетаскивание для перемещения узлов внутри дерева.
Код перетаскивания переходов работает хорошо, и я могу перейти на любой видимый node, однако я бы хотел, чтобы мой элемент управления вел себя как проводник Windows при перетаскивании файлов по панели папок. В частности, я хотел бы, чтобы каждая папка открывалась, если она зависала в течение 1/2 секунды или около того.
Я начал разработку решения с использованием методов Threading
и Sleep
, но я столкнулся с проблемами и задался вопросом, есть ли что-то уже на месте, если я не скачусь и не научусь использовать потоки ( это о времени, но я надеялся быстро получить это приложение)
Нужно ли писать собственный код для обработки Развернуть TreeNode
при зависании в режиме перетаскивания?
Ответы
Ответ 1
Вы можете использовать событие DragOver; он многократно срабатывает при перетаскивании объекта
Открытие после задержки может быть сделано очень легко с двумя дополнительными переменными, которые отмечают последний объект под мышью и время. Не требуется нить или другие трюки (lastDragDestination и lastDragDestinationTime в моем примере)
Из моего собственного кода:
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(e.X, e.Y);
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();
}
}
}
}
Ответ 2
ИЗМЕНИТЬ
У меня есть новое решение, немного надуманное, но оно работает... Он использует класс DelayedAction
для обработки замедленного выполнения действия в основном потоке:
DelayedAction<T>
public class DelayedAction<T>
{
private SynchronizationContext _syncContext;
private Action<T> _action;
private int _delay;
private Thread _thread;
public DelayedAction(Action<T> action)
: this(action, 0)
{
}
public DelayedAction(Action<T> 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<TreeNode> _expandNode;
public AutoExpandTreeView()
{
_expandNode = new DelayedAction<TreeNode>((node) => node.Expand(), 500);
}
private TreeNode _prevNode;
protected override void OnDragOver(DragEventArgs e)
{
Point clientPos = PointToClient(new Point(e.X, e.Y));
TreeViewHitTestInfo hti = HitTest(clientPos);
if (hti.Node != null && hti.Node != _prevNode)
{
_prevNode = hti.Node;
_expandNode.RunAfterDelay(hti.Node);
}
base.OnDragOver(e);
}
}