【问题标题】:Place RichTextBox caret within a Run在 Run 中放置 RichTextBox 插入符号
【发布时间】:2017-06-29 06:11:25
【问题描述】:

我试图将插入符号放在 Run 内的 RichTextBox 中,以使用户能够继续使用相同的格式输入。在这个 Run 之后,还有另一个不同格式的 Run。

这里有一些代码可以告诉你我的意思:

private void Button_Click(object sender, RoutedEventArgs e)
{
    Paragraph p = new Paragraph();

    // Add the new Paragraph to the RichTextBox flowdocument.
    rtb1.Document.Blocks.Add(p);

    // Create the first Run.
    Run r1 = new Run("This is a ");

    // Create the second Run.
    Run r2 = new Run("test");
    r2.TextDecorations = TextDecorations.Underline;

    // Add the new Runs to the Paragraph.
    p.Inlines.Add(r1);
    p.Inlines.Add(r2);

    // Place the caret at the end of the first Run, and give focus to the RichTextBox.
    rtb1.CaretPosition = r1.ContentEnd;
    rtb1.Focus();
}

如果我继续输入,新的输入将得到与第二次运行相同的格式。

我也尝试将 CaretPosition 设置为

new TextRange(r1.ElementEnd, r1.ElementEnd).Start

希望因为它的逻辑方向设置为向后它会起作用,但无济于事。

有没有办法在第一个 Run 中设置 CaretPosition?

【问题讨论】:

    标签: c# wpf richtextbox caret flowdocument


    【解决方案1】:

    当我选择“a”和“test”之间的空格并开始输入时,我观察到发生了什么,并看到创建了一个新的运行。我尝试创建一个新的 Run 并将其放置在 r1 和 r2 之间,并将 CaretPosition 设置为 newRun.ContentStart,但这不起作用。

    我发现如果我...

    1. 创建了一个包含空格的新 Run,
    2. 将 CaretPosition 放置在新 Run (newRun.ContentStart) 的开头,
    3. 从新的 Run 中删除了空间

    ...我将能够以与新 Run 相同的格式继续输入。

    我不喜欢这种方法,因为我需要创建一个新的 Run 而不是只使用它前面的那个。我会把它留在这里以防万一没有人知道更好的方法,如果没有人能提供更好的解决方案,我会在一周内接受它作为最佳答案。

    完整代码如下:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Paragraph p = new Paragraph();
    
        // Add the new Paragraph to the RichTextBox flowdocument.
        rtb1.Document.Blocks.Add(p);
    
        // Create the first Run.
        Run r1 = new Run("This is a ");
    
        // Create the second Run.
        Run r2 = new Run("test");
        r2.TextDecorations = TextDecorations.Underline;
    
        // Add the new Runs to the Paragraph.
        p.Inlines.Add(r1);
        p.Inlines.Add(r2);
    
        // Create a new Run. This will be placed between r1 and r2.
        Run newRun = new Run();
    
        // Copy the formatting from r1 to our new Run.
        CopyInlineFormattingToRun(r1, ref newRun);
    
        // Set the text of the new Run to be a space.
        newRun.Text = " ";
    
        // Place our new Run after r1.
        CreateNewRunAfterExistingRun(r1, newRun);
    
        // Put the caret at the start of newRun.
        rtb1.CaretPosition = newRun.ContentStart;
    
        // Remove the space at the end of newRun.
        newRun.ContentEnd.DeleteTextInRun(-1);
    
        // Give focus to the RichTextBox.
        rtb1.Focus();
    }
    
    /// <summary>
    /// Copies some of the properties in <paramref name="mainInline"/> to <paramref name="receiverRun"/>.
    /// </summary>
    /// <param name="mainInline">The <see cref="Inline"/> containing the desired formatting.</param>
    /// <param name="receiverRun">The <see cref="Run"/> which should receive the formattinh from <paramref name="mainInline"/>.</param>
    internal static void CopyInlineFormattingToRun(Inline mainInline, ref Run receiverRun)
    {
        receiverRun.FontFamily = mainInline.FontFamily;
        receiverRun.FontSize = mainInline.FontSize;
        receiverRun.FontStretch = mainInline.FontStretch;
        receiverRun.FontStyle = mainInline.FontStyle;
        receiverRun.FontWeight = mainInline.FontWeight;
        receiverRun.Foreground = mainInline.Foreground;
        receiverRun.Background = mainInline.Background;
        receiverRun.TextDecorations = mainInline.TextDecorations;
    }
    
    /// <summary>
    /// Places a <see cref="Run"/> after an existing <see cref="Run"/>.
    /// </summary>
    /// <param name="existingRun">The existing <see cref="Run"/> already present in the <see cref="FlowDocument"/>.</param>
    /// <param name="newRun">The new <see cref="Run"/> which will be placed after the <paramref name="existingRun"/> in the <see cref="FlowDocument"/>.</param>
    private void CreateNewRunAfterExistingRun(Run existingRun, Run newRun)
    {
        Paragraph p = (Paragraph)existingRun.Parent;
        p.Inlines.InsertAfter(existingRun, newRun);
    }
    

    如果您想在 Run 的另一侧执行此操作,则需要更改 CreateNewRunAfterExistingRun() 以使用 InsertAfter 而不是 ContentStart 您将不得不使用

    // Put the caret at the start of newRun.
    rtb1.CaretPosition = newRun.ContentEnd;
    

    编辑: 您可能可以订阅 LostFocusLostKeyboardFocus 事件,并根据需要合并两个 Run。

    【讨论】:

      【解决方案2】:

      诀窍在于运行有两个“结束”点。

      而不是rtb1.CaretPosition = newRun.ContentEnd;(它似乎位于运行结束的“外部”),而是使用rtb1.CaretPosition = newRun.ElementEnd;(它似乎位于它的“内部”),然后在结束时使用任何样式run 将应用于随后键入的所有文本。

      通过对已接受答案的此调整,无需添加空格,然后添加 DeleteTextInRun(-1) 来欺骗控件正常工作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-08-15
        • 1970-01-01
        • 1970-01-01
        • 2010-10-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多