При использовании перетаскивания я могу заставить 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);
    }
}