При использовании перетаскивания я могу заставить Treeview развернуть узел, над которым пользователь парит?

вкратце:

есть ли встроенная функция в .Net 2.0 для расширения TreeNodes, когда завис над в то время как операция перетаскивания выполняется?

Я использую 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);
    }
}