При использовании перетаскивания я могу заставить Treeview развернуть узел, над которым пользователь парит?
вкратце:
есть ли встроенная функция в .Net 2.0 для расширения TreeNode
s, когда завис над в то время как операция перетаскивания выполняется?
Я использую C# в Visual Studio 2005.
подробнее:
Я заселил Treeview
управление с многоуровневым, многозначным деревом (думаю, организационная диаграмма или диалоговое окно файл/папка), и я хочу использовать перетаскивание для перемещения узлов в дереве.
код перетаскивания работает хорошо, и я могу перейти на любой видимый узел, однако я хотел бы, чтобы мой элемент управления вел себя как проводник Windows при перетаскивании файлов через панель папок. В частности, я хотел бы, чтобы каждая папка открывалась, если зависала в течение 1/2 секунды или около того.
Я начал разрабатывать решение, используя Threading
и Sleep
метод, но я сталкиваюсь с проблемами и задавался вопросом, есть ли что-то на месте уже, если нет, я буду сжимать кулак и учиться использовать резьбу (это о времени, но я надеялся получить это приложение быстро)
мне нужно написать свой собственный код для обработки расширения TreeNode
когда завис в режиме перетаскивания?
2 ответов
вы можете использовать событие 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();
}
}
}
}
редактировать
у меня есть новое решение, немного надуманное, но оно работает... Он использует 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);
}
}