You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C# Windows Forms中实现垂直文本选择与多行同步编辑?

在C# Windows Forms中实现垂直文本选择与多行同步输入

要实现类似Notepad++的垂直选择和多行同步输入功能,WinForms默认的TextBox无法满足需求,我们可以基于RichTextBox控件自定义逻辑来实现。以下是具体步骤和代码示例:

1. 准备基础控件

在Form中添加一个RichTextBox控件(命名为rtbEditor),并设置以下属性:

  • Multiline = true
  • ScrollBars = RichTextBoxScrollBars.Both
  • Font = new Font("Consolas", 10f)(可选,等宽字体更适配垂直选择场景)

2. 实现Ctrl+Shift方向键的垂直选择

首先定义两个私有变量记录选择状态:

private Point _verticalSelectStart;
private bool _isVerticalSelecting = false;

为rtbEditor绑定KeyDown事件,处理Ctrl+Shift+方向键的组合操作:

private void rtbEditor_KeyDown(object sender, KeyEventArgs e)
{
    if (e.Control && e.Shift)
    {
        if (!_isVerticalSelecting)
        {
            // 记录垂直选择的起始位置(字符索引+行号)
            int startCharIndex = rtbEditor.SelectionStart;
            int startLine = rtbEditor.GetLineFromCharIndex(startCharIndex);
            _verticalSelectStart = new Point(startCharIndex, startLine);
            _isVerticalSelecting = true;
        }

        // 获取当前光标所在的行和列
        int currentCharIndex = rtbEditor.SelectionStart;
        int currentLine = rtbEditor.GetLineFromCharIndex(currentCharIndex);
        int currentCol = currentCharIndex - rtbEditor.GetFirstCharIndexFromLine(currentLine);

        // 根据方向键计算目标行/列
        int targetLine = currentLine;
        int targetCol = currentCol;
        switch (e.KeyCode)
        {
            case Keys.Up:
                targetLine = Math.Max(0, currentLine - 1);
                break;
            case Keys.Down:
                targetLine = Math.Min(rtbEditor.Lines.Length - 1, currentLine + 1);
                break;
            case Keys.Left:
                targetCol = Math.Max(0, currentCol - 1);
                break;
            case Keys.Right:
                targetCol = Math.Min(rtbEditor.Lines[currentLine].Length, currentCol + 1);
                break;
        }

        // 计算垂直选择的范围:起始行/列到目标行/列
        int startLine = Math.Min(_verticalSelectStart.Y, targetLine);
        int endLine = Math.Max(_verticalSelectStart.Y, targetLine);
        int startCol = Math.Min(_verticalSelectStart.X - rtbEditor.GetFirstCharIndexFromLine(_verticalSelectStart.Y), targetCol);
        int endCol = Math.Max(_verticalSelectStart.X - rtbEditor.GetFirstCharIndexFromLine(_verticalSelectStart.Y), targetCol);

        // 逐行选中对应区域
        rtbEditor.SelectionStart = 0;
        rtbEditor.SelectionLength = 0;
        rtbEditor.SelectionBackColor = SystemColors.Window; // 清除之前的选择
        for (int line = startLine; line <= endLine; line++)
        {
            int lineStartChar = rtbEditor.GetFirstCharIndexFromLine(line);
            int selectStart = lineStartChar + startCol;
            int selectLength = endCol - startCol;

            // 处理行长度不足的情况,避免越界
            if (selectStart + selectLength > lineStartChar + rtbEditor.Lines[line].Length)
            {
                selectLength = (lineStartChar + rtbEditor.Lines[line].Length) - selectStart;
            }

            if (selectLength > 0)
            {
                rtbEditor.Select(selectStart, selectLength);
                rtbEditor.SelectionBackColor = SystemColors.Highlight;
                rtbEditor.SelectionColor = SystemColors.HighlightText;
            }
        }

        e.Handled = true; // 阻止默认事件
    }
    else
    {
        _isVerticalSelecting = false; // 结束垂直选择模式
    }
}

3. 实现多行同步输入

为rtbEditor绑定KeyPress事件,处理垂直选择状态下的输入逻辑:

private void rtbEditor_KeyPress(object sender, KeyPressEventArgs e)
{
    // 判断是否处于垂直选择状态
    bool isVerticalSelection = false;
    int selStart = rtbEditor.SelectionStart;
    int selLength = rtbEditor.SelectionLength;
    int startLine = rtbEditor.GetLineFromCharIndex(selStart);
    int endLine = rtbEditor.GetLineFromCharIndex(selStart + selLength - 1);

    if (startLine != endLine)
    {
        // 验证每行选中的长度是否一致(垂直选择的特征)
        int lineCount = endLine - startLine + 1;
        int singleLineSelLength = selLength / lineCount;
        isVerticalSelection = true;
        for (int line = startLine; line <= endLine; line++)
        {
            int lineStartChar = rtbEditor.GetFirstCharIndexFromLine(line);
            int lineSelStart = lineStartChar + (selStart - rtbEditor.GetFirstCharIndexFromLine(startLine));
            int actualLineSelLength = (line == endLine) ? selStart + selLength - lineSelStart : rtbEditor.GetFirstCharIndexFromLine(line + 1) - lineSelStart;
            if (actualLineSelLength != singleLineSelLength)
            {
                isVerticalSelection = false;
                break;
            }
        }
    }

    if (isVerticalSelection && !char.IsControl(e.KeyChar))
    {
        int lineCount = endLine - startLine + 1;
        int singleLineSelLength = selLength / lineCount;
        int colStart = selStart - rtbEditor.GetFirstCharIndexFromLine(startLine);

        // 批量修改文本,避免频繁刷新
        rtbEditor.BeginUpdate();
        rtbEditor.SelectionLength = 0;

        for (int line = startLine; line <= endLine; line++)
        {
            int lineStartChar = rtbEditor.GetFirstCharIndexFromLine(line);
            int targetPos = lineStartChar + colStart;

            // 删除当前行的选中内容
            if (singleLineSelLength > 0)
            {
                rtbEditor.Select(targetPos, singleLineSelLength);
                rtbEditor.SelectedText = "";
            }
            // 插入新输入的字符
            rtbEditor.Select(targetPos, 0);
            rtbEditor.SelectedText = e.KeyChar.ToString();
        }

        // 重新设置垂直选择范围
        rtbEditor.Select(selStart, lineCount * 1);
        rtbEditor.EndUpdate();

        e.Handled = true; // 阻止默认输入逻辑
    }
}

4. 扩展优化(可选)

  • 添加Backspace和Delete键的处理逻辑,实现垂直选择内容的批量删除
  • 支持鼠标垂直选择(按住Alt键拖动鼠标),需处理MouseDown、MouseMove事件计算选择区域

内容的提问来源于stack exchange,提问作者Moraban

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 04:15:08