在特定的鼠标位置拖放一个项目到文本框中 - 显示插入符号或位置指示器

6

我正在将一个来自 TreeView 的项目粘贴到一个 TextBox 中,但我想要在鼠标当前位置粘贴该项,并且显示一个像下面图片中的光标。

图片中的光标: example

这是我的代码:

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];
    }
}

请查看更新后的答案,它融合了我原来的最佳解决方案和@Reza的方案! - TaW
2个回答

5

这很棘手,因为拖放(Drag&Drop)操作会保持鼠标的焦点,所以您不能使用鼠标事件来完成操作。

一种方法是设置一个计时器(Timer)来完成工作...:

    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();
    }
Timer 使用 Control.MousePosition 函数每 25ms 左右确定光标位置,设置插入符并更新 TextBox
在您的事件中,您需要初始化它并确保 TextBox 具有焦点;最后,在当前选择位置添加字符串。
    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]
        }
    }

另一种解决方法是不使用普通的拖放,只编写鼠标事件,但在我的第一次测试中,这个方法可行。
更新:虽然上面的解决方案确实可行,但使用计时器似乎不是很优雅。最好使用DragOver事件,就像Reza的回答中所示。但是,为什么不做真正的事情,也就是控制实际的I-beam呢? DragOver事件在移动过程中一直调用,因此它的工作方式几乎与MouseMove相同。因此,以下是两个解决方案的合并,我认为这是最好的方法:
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();

}

我喜欢这个解决方案。我注意到我无法将光标移动到最后一个字符之后(如Reza的答案中的gif所示)。我在网上找到了一个解决此行为的解决方案,它对我有效:C# .NET问题:在文本框插入符号处拖放文本 - Paul π

3
DragOver事件中可以在TextBox上绘制插入符号,并将SelectionStart设置为从鼠标位置获取的字符索引。然后在DragDrop事件中,只需设置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;
    }
}

那是一个非常好的选择!@Reza:你能告诉我你用什么制作了那个动画吗? - TaW
@TaW 感谢您的反馈 :) 我使用了 ScreenToGif。这个工具可以让您录制屏幕上的选定区域并将其保存为 Gif。 - Reza Aghaei
我在 gif 中看到了放置指示器的黑暗阴影,但在执行时在 TextBox 中并不存在。这只是 gif 的质量或采样率问题,在运行时完全清晰可见。 - Reza Aghaei
嗯,我没有看到阴影。但是我正在睡觉;-) - TaW

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接