首页 > 解决方案 > Richtextbox 中的文本在选择之外应用文本装饰

问题描述

我有一个使用 MVVM 架构的 WPF 桌面应用程序。我是按照 Tim Buchalka 在 Udemy 上的“Windows Presentation Foundation Masterclass”开始制作的(付费课程,仅在有人已经知道它的内容时才提及),特别是制作简单的 Evernote 克隆的部分。

我有一个RichTextBox和几个按钮,允许用户更改当前选定文本的格式 - 粗体、斜体、下划线和删除线。粗体和斜体可以很好地工作,问题在于下划线和删除线。我可以很好地应用任何一个属性,只要它在整个选择中都是相同的(全部带下划线,全部删除线,两者都或两者都没有)。但是,当我尝试将样式应用于选择时,其中一个部分是 udnerlined/删除线而另一部分不是,格式会应用于与选择本身相比偏移的文本范围(更不用说样式不是' t 应用了我想要的方式,但偏移量目前是更大的问题)。我会尝试添加图片以更好地说明我的意思。

选择均匀时的造型正常工作:

选择时造型正常

选中文本的非同质部分,即将点击下划线按钮:

选中文本的非同质部分,即将点击下划线按钮

单击按钮后如何应用样式:

单击按钮后如何应用样式

我试图弄清楚使用断点发生了什么,但该方法似乎只按预期迭代选择。我将不胜感激任何建议和/或建设性的批评。无论如何,这是我的代码:

粗体按钮的方法:

private void btnUnderline_Click(object sender, RoutedEventArgs e)
{
    bool isButtonChecked = (sender as ToggleButton).IsChecked ?? false;
    TextDecorationCollection textDecorations = new TextDecorationCollection();
    try
    {
        textDecorations.Add( contentRichTextBox.Selection.GetPropertyValue( 
            Inline.TextDecorationsProperty) as TextDecorationCollection);
        if (isButtonChecked)
        { textDecorations.Add(TextDecorations.Underline); }
        else { textDecorations.TryRemove(TextDecorations.Underline, out textDecorations);}
        contentRichTextBox.Selection.ApplyPropertyValue(Inline.TextDecorationsProperty, 
            textDecorations);
    }
    catch (Exception)
    {
        textDecorations = new TextDecorationCollection();
        if (!contentRichTextBox.Selection.IsEmpty)
        {
            var tpFirst = contentRichTextBox.Selection.Start;
            var tpLast = contentRichTextBox.Selection.End;
            var textRange = new TextRange(tpFirst, tpFirst.GetPositionAtOffset(1));
            var underlined = (textRange.GetPropertyValue(Inline.TextDecorationsProperty) 
                as TextDecorationCollection).Contains(TextDecorations.Underline[0]);
            for (TextPointer t = tpFirst; 
                 t.CompareTo(tpLast) <= 0; 
                 t = t.GetPositionAtOffset(1))
            {
                textDecorations.Clear();
                textRange = new TextRange(t, t.GetPositionAtOffset(1));
                textDecorations = textRange.GetPropertyValue( 
                    Inline.TextDecorationsProperty) as TextDecorationCollection;
                if (!underlined)
                { textDecorations.Add(TextDecorations.Underline[0]); }
                else { textDecorations.TryRemove(TextDecorations.Underline, 
                       out textDecorations); }
                textRange.ApplyPropertyValue(Inline.TextDecorationsProperty, 
                     textDecorations);
            }
        }
        else
        {
            NewParagraphWhenSelectionIsEmpty();
        }
    }
}

删除线按钮的方法:

private void btnStrikethrough_Click(object sender, RoutedEventArgs e)
{
    bool isButtonChecked = (sender as ToggleButton).IsChecked ?? false;
    TextDecorationCollection textDecorations = new TextDecorationCollection();
    try
    {
        textDecorations.Add(contentRichTextBox.Selection.GetPropertyValue( 
            Inline.TextDecorationsProperty) as TextDecorationCollection);
        if (isButtonChecked)
        { textDecorations.Add(TextDecorations.Strikethrough); }
        else { textDecorations.TryRemove(TextDecorations.Strikethrough, 
               out textDecorations); }
        contentRichTextBox.Selection.ApplyPropertyValue(Inline.TextDecorationsProperty, 
             textDecorations);
    }
    catch (Exception)
    {
        textDecorations = new TextDecorationCollection();
        if (!contentRichTextBox.Selection.IsEmpty)
        {
            var tpFirst = contentRichTextBox.Selection.Start;
            var tpLast = contentRichTextBox.Selection.End;
            var textRange = new TextRange(tpFirst, tpFirst.GetPositionAtOffset(1));
            var strikedthrough = (textRange.GetPropertyValue( 
                  Inline.TextDecorationsProperty) as 
                  TextDecorationCollection).Contains(TextDecorations.Strikethrough[0]);
            for (TextPointer t = tpFirst; 
                 t.CompareTo(tpLast) <= 0;
                 t = t.GetPositionAtOffset(1))
            {
                textDecorations.Clear();
                textRange = new TextRange(t, t.GetPositionAtOffset(1));
                textDecorations = textRange.GetPropertyValue( 
                    Inline.TextDecorationsProperty) as TextDecorationCollection;
                if (!strikedthrough)
                { textDecorations.Add(TextDecorations.Strikethrough[0]); }
                else { textDecorations.TryRemove(TextDecorations.Strikethrough,
                       out textDecorations); }
                textRange.ApplyPropertyValue(Inline.TextDecorationsProperty, 
                    textDecorations);
            }
        }
        else
        {
            NewParagraphWhenSelectionIsEmpty();
        }
    }
}

富文本框中的选择发生变化时使用的方法:

private void contentRichTextBox_SelectionChanged(object sender, RoutedEventArgs e)
{
    var selectedWeight = contentRichTextBox.Selection.GetPropertyValue( 
        FontWeightProperty);
    btnBold.IsChecked = (selectedWeight != DependencyProperty.UnsetValue) 
                         && (selectedWeight.Equals(FontWeights.Bold));

    var selectedStyle = contentRichTextBox.Selection.GetPropertyValue(FontStyleProperty);
    btnItalics.IsChecked = (selectedStyle != DependencyProperty.UnsetValue) 
                         && (selectedStyle.Equals(FontStyles.Italic));

    var selectedDecoration = contentRichTextBox.Selection.GetPropertyValue( 
             Inline.TextDecorationsProperty) as TextDecorationCollection;

    btnUnderline.IsChecked = (selectedDecoration != null)
        && (selectedDecoration != DependencyProperty.UnsetValue)
        && selectedDecoration.Contains(TextDecorations.Underline[0]);

    btnStrikethrough.IsChecked = (selectedDecoration != null)
        && (selectedDecoration != DependencyProperty.UnsetValue)
        && selectedDecoration.Contains(TextDecorations.Strikethrough[0]);

    cbEditorFontFamily.SelectedItem = contentRichTextBox.Selection.GetPropertyValue( 
                                      Inline.FontFamilyProperty);

    if (contentRichTextBox.Selection.GetPropertyValue(Inline.FontSizeProperty)
        != DependencyProperty.UnsetValue)
    { cbEditorFontSize.Text = (contentRichTextBox.Selection.GetPropertyValue( 
                              Inline.FontSizeProperty)).ToString(); }
}

插入新段落时使用的方法:

private void NewParagraphWhenSelectionIsEmpty()
{
    var fontFamily = new FontFamily(cbEditorFontFamily.SelectedItem.ToString());
    var fontSize = Convert.ToDouble(cbEditorFontSize.Text);
    var fontStyle = (btnItalics.IsChecked ?? false) ? FontStyles.Italic : 
                    FontStyles.Normal;
    var fontWeight = (btnItalics.IsChecked ?? false) ? FontWeights.Bold : 
        FontWeights.Normal;

    var textDecorations = new TextDecorationCollection();
    if (btnUnderline.IsChecked ?? false)
    { textDecorations.Add(TextDecorations.Underline[0]); }
    if (btnStrikethrough.IsChecked ?? false)
    { textDecorations.Add(TextDecorations.Strikethrough[0]); }

    // Check to see if we are at the start of the textbox and nothing has been added yet
    if (contentRichTextBox.Selection.Start.Paragraph == null)
    {
        // Add a new paragraph object to the richtextbox with the fontsize
        Paragraph p = new Paragraph();
        p.FontFamily = fontFamily;
        p.FontSize = fontSize;
        p.FontStyle = fontStyle;
        p.FontWeight = fontWeight;
        p.TextDecorations = textDecorations;
        contentRichTextBox.Document.Blocks.Add(p);
    }
    else
    {
        // Get current position of cursor
        TextPointer curCaret = contentRichTextBox.CaretPosition;
        // Get the current block object that the cursor is in
        Block curBlock = contentRichTextBox.Document.Blocks.Where
            (x => x.ContentStart.CompareTo(curCaret) == -1 
            && x.ContentEnd.CompareTo(curCaret) == 1).FirstOrDefault();
        if (curBlock != null)
        {
            Paragraph curParagraph = curBlock as Paragraph;
            // Create a new run object with the fontsize, and add it to the current block
            Run newRun = new Run();
            newRun.FontFamily = fontFamily;
            newRun.FontSize = fontSize;
            newRun.FontStyle = fontStyle;
            newRun.FontWeight = fontWeight;
            newRun.Foreground = new 
                   SolidColorBrush((Color)wpfcpEditorFontColour.SelectedColor);    
            newRun.TextDecorations = textDecorations;
            curParagraph.Inlines.Add(newRun);
            // Reset the cursor into the new block. 
            // If we don't do this, the font size will default again when you start 
            // typing.
            contentRichTextBox.CaretPosition = newRun.ElementStart;
        }
    }
}

标签: c#wpfrichtextbox

解决方案


推荐阅读