Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drag and drop an to TextBox in a specific mouse position - Show caret or position indicator

I am pasting an item from a TreeView to a TextBox, but I want to paste that item in the mouse's current position and also show a caret like the image below. Image with caret: example

Here is my code:

private void tvOperador_ItemDrag(object sender, ItemDragEventArgs e)
{
    var node = (TreeNode)e.Item;
    if (node.Level > 0)
    {
        DoDragDrop(node.Text, DragDropEffects.Copy);
    }
}
private void txtExpresion_DragEnter(object sender, DragEventArgs e)
{
    if (e.Data.GetDataPresent(typeof(string))) e.Effect = DragDropEffects.Copy;
}
private void txtExpresion_DragDrop(object sender, DragEventArgs e)
{
    if (e.Data.GetDataPresent(typeof(System.String)))
    {
        string Item = (System.String)e.Data.GetData(typeof(System.String));
        string[] split = Item.Split(':');

        txtExpresion.Text += split[1];
    }
}
like image 834
EGSL Avatar asked Mar 12 '23 10:03

EGSL


2 Answers

This is tricky as the Drag&Drop operation keeps the mouse captured, so you can't use the mouse events..

One way is to set up a Timer to do the work..:

    Timer cursTimer = new Timer();

    void cursTimer_Tick(object sender, EventArgs e)
    {
        int cp = txtExpresion.GetCharIndexFromPosition(
                 txtExpresion.PointToClient(Control.MousePosition));
        txtExpresion.SelectionStart = cp;
        txtExpresion.SelectionLength = 0; 
        txtExpresion.Refresh();
    }

The Timer uses the Control.MousePosition function to determined the cursor position every 25ms or so, sets the caret and updates the TextBox.

In your events you initialize it and make sure the TextBox has focus; finally you add the string at the current selection:

    private void txtExpresion_DragEnter(object sender, DragEventArgs e)
    {
        if (e.Data.GetDataPresent(typeof(string)))
        {
            e.Effect = DragDropEffects.Copy;
            txtExpresion.Focus();
            cursTimer = new Timer();
            cursTimer.Interval = 25;
            cursTimer.Tick += cursTimer_Tick;
            cursTimer.Start();
        }
    }

    private void txtExpresion_DragDrop(object sender, DragEventArgs e)
    {
        if (e.Data.GetDataPresent(typeof(System.String)))
        {
            cursTimer.Stop();

            string Item = (System.String)e.Data.GetData(typeof(System.String));
            string[] split = Item.Split(':');

            txtExpresion.SelectedText = split[1]
        }
    }

A different way to solve it would be to not use normal Drag&Drop and only code the mouse events but this one worked ok a my first tests.

Update

While the above solution does work, using a Timer seems not exactly elegant. Much better to use the DragOver event, as seen in Reza's answer. But instead of painting a cursor, why not do the real thing, i.e. take control of the actual I-beam..?

The DragOver event is called all the time during the move so it works pretty much like MousMove would: So here is a merger of the two solutions, which I believe is the best way to do it:

private void txtExpresion_DragDrop(object sender, DragEventArgs e)
{
    if (e.Data.GetDataPresent(typeof(System.String)))
    {
        string Item = (System.String)e.Data.GetData(typeof(System.String));
        string[] split = Item.Split(':');
        txtExpresion.SelectionLength = 0;
        txtExpresion.SelectedText = split[1];
    }
}

private void txtExpresion_DragEnter(object sender, DragEventArgs e)
{
    if (e.Data.GetDataPresent(typeof(string)))
    {
        e.Effect = DragDropEffects.Copy;
        txtExpresion.Focus();
    }
}

private void txtExpresion_DragOver(object sender, DragEventArgs e)
{
    int cp = txtExpresion.GetCharIndexFromPosition(
                          txtExpresion.PointToClient(Control.MousePosition));
    txtExpresion.SelectionStart = cp;
    txtExpresion.Refresh();

}
like image 127
TaW Avatar answered Mar 30 '23 00:03

TaW


You can draw a caret over TextBox in DragOver event. Also set the SelectionStart to the char index you get from mouse position. Then in DragDrop event, just set SelectedText.

enter image description here

private void textBox1_DragOver(object sender, DragEventArgs e)
{
    if (e.Data.GetDataPresent(typeof(System.String)))
    {
        var position = textBox1.PointToClient(Cursor.Position);
        var index = textBox1.GetCharIndexFromPosition(position);
        textBox1.SelectionStart = index;
        textBox1.SelectionLength = 0;
        textBox1.Refresh();
        using (var g = textBox1.CreateGraphics())
        {
            var p = textBox1.GetPositionFromCharIndex(index);
            g.DrawLine(Pens.Black, p.X, 0, p.X, textBox1.Height);
        }
    }
}

private void textBox1_DragDrop(object sender, DragEventArgs e)
{
    if (e.Data.GetDataPresent(typeof(System.String)))
    {
        string txt = (System.String)e.Data.GetData(typeof(System.String));
        textBox1.SelectedText = txt;
    }
}
like image 20
Reza Aghaei Avatar answered Mar 30 '23 01:03

Reza Aghaei