简体   繁体   中英

WPF Richtextbox FontFace/FontSize

I am currently trying to create some basic word processor features in a WPF project. I am using a RichTextBox and am aware of all of the EditingCommands (ToggleBold, ToggleItalic...ect.). The thing I am stuck on is allowing the user to change the fontsize and font face like in MS Office where the value changes for only the selected text and if there is no selected text then the value will change for the current caret position. I have come up with a decent amount of code to get this to work, but am having problems with the no selected text thing. Here is what I am doing for the RichTextBox.Selection.

TextSelection text = richTextBox.Selection;
if (text.IsEmpty)
{
    //doing this will change the entire word that the current caret position
    //is on which is not the desire/expected result.
    text.ApplyPropertyValue(RichTextBox.FontSizeProperty, value);
}
else
    //This works as expected.
    text.ApplyPropertyValue(RichTextBox.FontSizeProperty, value);

So my question is how should I go about doing this? Is there a better/more convenient way to do this? One thought I had was that I would need to insert a new Inline into the Paragraph but I couldn't figure out how to do that. Any help is appreciated. Thank you.

I have solved this as follows:

TextRange r = new TextRange(richtextbox.Selection.Start, richtextbox.Selection.End);
r.ApplyPropertyValue(TextElement.FontSizeProperty, value);

I just had the exact same problem. I was able to use TextElement.FontSizeProperty as bendewey said. However, it still wasn't working quite right. After looking through the project at the link below, I found out I was still doing something wrong. I wasn't setting the focus back to the RichTextBox... which the author of the project below didn't need to do because they were using a ribbon combobox. With a regular combobox, after you select a font, it does actually change the font of the selection in the RichTextBox, but it takes the focus away from the RTB. When you click back on the RTB to get the focus and start typing, you have a new selection object, in which case the font is back to the default font of the RTB itself.

http://www.codeplex.com/WPFRichEditorLibrary

@sub-jp is right, you need to set the focus back to the RichTextBox , otherwise you'll change the properties of one selection, but when you click back to the text box, you'll get a new selection with the existing font. Try changing your code to be like this:

TextSelection text = richTextBox.Selection;

richTextBox.Focus();

text.ApplyPropertyValue(RichTextBox.FontSizeProperty, value);

...and then it should work correctly both when there is text highlighted, and when there is not.

Alternatively, as suggested here , you can set the Focusable property of the ComboBox to False , to avoid this issue entirely.

Try this

var range = new TextRange( richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd );
range.ApplyPropertyValue( TextElement.FontSizeProperty, value );

The issue is that you must set focus back to the Rich Textbox after selecting the new FontFamily or Font Size:

//When Font Size is changed
private void rbngFontSize_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        rtbMain.Focus(); // This part is what fixes the issue, just make sure it is set before ApplyPropertyValue method.

        try
        {
            ApplyPropertyValueToSelectedText(TextElement.FontSizeProperty, e.AddedItems[0]);
        }
        catch { };
    }

//When FontFamily is changed
private void rbngFontFamily_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        FontFamily editValue = (FontFamily)e.AddedItems[0];

        rtbMain.Focus(); // This part is what fixes the issue, just make sure it is set before ApplyPropertyValue method.

        ApplyPropertyValueToSelectedText(TextElement.FontFamilyProperty, editValue);            

    }

I had a similar problem, if i am understanding you correctly. I tried searching a ton of various answers that were close but didn't quite work for me. My problem was that the font change worked fine for text that was explicitly selected, but if there was no selected text, and the font size was changed, the following text typed in would revert to the default fontsize. Here is what i finally figured out, give it a shot, and let me know if it worked for anyone else:

    public static void SetFontSize(RichTextBox target, double value)
    {
        // Make sure we have a richtextbox.
        if (target == null)
            return;

        // Make sure we have a selection. Should have one even if there is no text selected.
        if (target.Selection != null)
        {
            // Check whether there is text selected or just sitting at cursor
            if (target.Selection.IsEmpty)
            {
                // Check to see if we are at the start of the textbox and nothing has been added yet
                if (target.Selection.Start.Paragraph == null)
                {
                    // Add a new paragraph object to the richtextbox with the fontsize
                    Paragraph p = new Paragraph();
                    p.FontSize = value;
                    target.Document.Blocks.Add(p);
                }
                else
                {
                    // Get current position of cursor
                    TextPointer curCaret = target.CaretPosition;
                    // Get the current block object that the cursor is in
                    Block curBlock = target.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.FontSize = value;
                        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.
                        target.CaretPosition = newRun.ElementStart;
                    }
                }
            }
            else // There is selected text, so change the fontsize of the selection
            {
                TextRange selectionTextRange = new TextRange(target.Selection.Start, target.Selection.End);
                selectionTextRange.ApplyPropertyValue(TextElement.FontSizeProperty, value);
            }
        }
        // Reset the focus onto the richtextbox after selecting the font in a toolbar etc
        target.Focus();
    }

It's easy to solve if you're handling OnTextInput .

protected override void OnTextInput(TextCompositionEventArgs e)
{
    TextRange range = new TextRange(this.Selection.Start, this.Selection.End);
    // Doesn't matter whether the selection is empty or not, it should be 
    // replaced with something new, and with the right formatting
    range.Text = e.Text;

    // Now nothing else would get affected...
    range.ApplyPropertyValue(TextElement.FontFamilyProperty, value);
    this.CaretPosition = range.End;
    e.Handled = true; // You might not need this line :)
}

EDIT: Setting the CaretPosition to the end of the TextRange , might make the Caret align wrongly sometimes. My RichTextBox has a lot of BaselineAlignment tweaks so that might have been what caused mine. But, if you ever experience any funny Caret jumps or sinks, try to check if the Caret is at the end of the Paragraph each time before setting the CaretPosition correctly. Something like this would work:

TextRange test = new TextRange(range.End, range.End.Paragraph.ContentEnd);
bool IsAtEnd = test.IsEmpty || String.IsNullOrEmpty(test.Text) || test.Text == "\r\n";

// Now we know whether the Caret is still in the middle of the text or not
// This part corrects the Caret glitch! :)
if (!IsAtEnd)
    this.CaretPosition = range.End;
else
    this.CaretPosition = range.Start.GetNextContextPosition(LogicalDirection.Forward);

I know my answer is late and all, but hope I've helped someone else. Cheers!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM