Как переместить курсор вставки на текстовое поле при принятии броска - PullRequest
2 голосов
/ 27 мая 2009

У меня есть TreeView и Multiline Textbox вместе в одной форме в форме Windows. У меня есть функция перетаскивания, чтобы я мог перетащить узел из TreeView в текстовое поле и вставить текст в текстовое поле (это работает).

Я бы хотел улучшить это, чтобы при перетаскивании мышью над текстовым полем какой-то индикатор перемещался по тексту, показывая пользователю, куда будет вставлен текст, а при его отбрасывании он вставляется в эту позицию. В настоящее время я просто помещаю текст в SelectionStart, но операция перетаскивания не обновляет SelectionStart, поэтому он всегда там, где у пользователя последний раз был курсор.

Вот мой текущий код:

    private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)
    {
        if (e.Button != MouseButtons.Left)
            return;

        object item = e.Item;
        treeView1.DoDragDrop(((TreeNode)item).Tag.ToString(), DragDropEffects.Copy | DragDropEffects.Scroll);
    }

    private void textBox1_DragEnter(object sender, DragEventArgs e)
    {
        if (e.Data.GetDataPresent(DataFormats.StringFormat))
        {
            e.Effect = DragDropEffects.Copy | DragDropEffects.Scroll;
        }
        else
        {
            e.Effect = DragDropEffects.None;
        }
    }

    private void textBox1_DragDrop(object sender, DragEventArgs e)
    {
        if (e.Data.GetDataPresent(DataFormats.StringFormat))
        {
            textBox1.SelectionLength = 0;
            textBox1.SelectedText = (string)e.Data.GetData(DataFormats.StringFormat);
        }
    }

Ответы [ 5 ]

8 голосов
/ 06 октября 2010

То, что я упустил во всех этих советах о перетаскивании, делает текстовую карету видимой. В конце концов, я обнаружил, что вам просто нужно сосредоточиться на контроле! Таким образом, окончательный код для обработчика события textBox1.DragOver будет выглядеть следующим образом. Я включил функцию GetCaretIndexFromPoint из предыдущего ответа:

/// <summary>
/// Gives visual feedback where the dragged text will be dropped.
/// </summary>
private void textBox1_DragOver(Object sender, System.Windows.Forms.DragEventArgs e)
{
    // fake moving the text caret
    textBox1.SelectionStart = GetCaretIndexFromPoint(textBox1, e.X, e.Y);
    textBox1.SelectionLength = 0;
    // don't forget to set focus to the text box to make the caret visible!
    textBox1.Focus();
}

/// <remarks>
/// GetCharIndexFromPosition is missing one caret position, as there is one extra caret
/// position than there are characters (an extra one at the end).
/// </remarks>
private int GetCaretIndexFromPoint(System.Windows.Forms.TextBox box, int x, int y)
{
    Point realPoint = box.PointToClient(newPoint(x, y));
    int index = box.GetCharIndexFromPosition(realPoint);
    if (index == box.Text.Length - 1)
    {
        Point caretPoint = box.GetPositionFromCharIndex(index);
        if (realPoint.X > caretPoint.X)
        {
            index += 1;
        }
    }
    return index;
}
3 голосов
/ 30 сентября 2009

Это действительно очень раздражает, так как в GetCharIndexFromPosition (очевидно) отсутствует одна позиция каретки, поскольку есть одна дополнительная позиция каретки, чем символы (дополнительная в конце). Я использую это, чтобы установить SelectionStart на DragOver и Вставить на DragDrop.

    private int GetCaretIndexFromPoint(TextBox tb, int x, int y)
    {
        Point p = tb.PointToClient(new Point(x, y));
        int i = tb.GetCharIndexFromPosition(p);
        if (i == tb.Text.Length - 1)
        {
            Point c = tb.GetPositionFromCharIndex(i);
            if (p.X > c.X)
                i++;
        }
        return i;
    }

Не совсем идеально, но это делает работу. Если кто-нибудь найдет родную версию, пожалуйста, дайте мне знать:)

3 голосов
/ 27 мая 2009

Я думаю, вы захотите посмотреть на обработку события textBox1_DragOver. Передайте позицию мыши, содержащуюся в аргументах события DragOver, в textBox1.GetCharIndexFromPosition () '

Вы должны иметь возможность использовать положение символа для установки положения каретки.

Вот документация для GetCharIndexFromPosition

1 голос
/ 08 января 2014

Чтобы эмулировать поведение обычных инструментов (таких как Блокнот), функция GetCaretIndexFromPoint () должна проверять координаты X и Y.

/// <summary>
/// Gives visual feedback where the dragged text will be dropped.
/// </summary>
private void textBox1_DragOver(Object sender, System.Windows.Forms.DragEventArgs e)
{
    // fake moving the text caret
    textBox1.SelectionStart = GetCaretIndexFromPoint(textBox1, e.X, e.Y);
    textBox1.SelectionLength = 0;
    // don't forget to set focus to the text box to make the caret visible!
    textBox1.Focus();
}

/// <remarks>
/// GetCharIndexFromPosition is missing one caret position, as there is one extra caret
/// position than there are characters (an extra one at the end).
/// </remarks>
private int GetCaretIndexFromPoint(System.Windows.Forms.TextBox box, int x, int y)
{
    Point realPoint = box.PointToClient(new Point(x, y));
    int index = box.GetCharIndexFromPosition(realPoint);
    if (index == box.Text.Length - 1)
    {
        Point caretPoint = box.GetPositionFromCharIndex(index);
        if ((realPoint.X > caretPoint.X) || (realPoint.Y > caretPoint.y))
        {
            index += 1;
        }
    }
    return index;
}

Вы можете увидеть разницу, если последний символ в текстовом поле - это новая строка после строки текста, и вы перетащите текст ниже и слева от элемента caretPoint по сравнению с нижним и справа от элемента caretPoint.

Спасибо user468106 за отличный ответ!

1 голос
/ 06 апреля 2013

Я искал реализацию этого решения в WPF и не смог ее найти. Так реализовано следующим образом (Авторы оригинального решения автора). Надеюсь, это поможет кому-то, кто ищет версию WPF.

    /// <summary>
    /// Handles the Preview DragOver event to set the textbox selection at the precise place where the user dropped the dragged text
    /// </summary>
    private static void element_PreviewDragOver(object sender, DragEventArgs dragEventArgs)
    {
        TextBox textBox = sender as TextBox;
        if (textBox != null && dragEventArgs != null)
        {
            // Set the caret at the position where user ended the drag-drop operation
            Point dropPosition = dragEventArgs.GetPosition(textBox);

            textBox.SelectionStart = GetCaretIndexFromPoint(textBox, dropPosition);
            textBox.SelectionLength = 0;

            // don't forget to set focus to the text box to make the caret visible!
            textBox.Focus();
            dragEventArgs.Handled = true;
        }
    }

    /// <summary>
    /// Gets the caret index of a given point in the given textbox
    /// </summary>
    /// <param name="textBox"></param>
    /// <param name="point"></param>
    /// <returns></returns>
    private static int GetCaretIndexFromPoint(TextBox textBox, Point point)
    {
        int index = textBox.GetCharacterIndexFromPoint(point, true);

        // GetCharacterIndexFromPoint is missing one caret position, as there is one extra caret position than there are characters (an extra one at the end).
        //  We have to add that caret index if the given point is at the end of the textbox
        if (index == textBox.Text.Length - 1)
        {
            // Get the position of the character index using the bounding rectangle
            Rect caretRect = textBox.GetRectFromCharacterIndex(index);
            Point caretPoint = new Point(caretRect.X, caretRect.Y);

            if (point.X > caretPoint.X)
            {
                index += 1;
            }
        }
        return index;
    }

    /// <summary>
    /// Handler for preview drag event in a textbox
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="dragEventArgs"></param>
    private static void element_PreviewDrop(object sender, DragEventArgs dragEventArgs)
    {
        TextBox textBox = sender as TextBox;
        if (textBox != null && dragEventArgs != null && dragEventArgs.Data != null && dragEventArgs.Data.GetDataPresent(DataFormats.StringFormat))
        {
            string newText = dragEventArgs.Data.GetData(DataFormats.StringFormat) as string;
            if (!string.IsNullOrEmpty(newText))
            {
                // do your custom logic here
                textBox.Focus();
            }
            dragEventArgs.Handled = true;
        }
    }
...