質問

I'm trying to enable spell checking in a WPF RichTextBox. MSDN writes that System.Windows.Controls.SpellCheck can be used to enable spell checking for TextBox and RichTextBox controls.

Unfortunately, the following code doesn't work for me:

<RichTextBox SpellCheck.IsEnabled="True" xml:lang="en-US"></RichTextBox>

Which is strange, because if I use a plain TextBox, it works perfectly fine (I can see the red lines if I miss-spell something).

Unfortunately, every answer I've found on SO so far mention only to set SpellCheck.IsEnabled to True and set the Language property to one of the supported languages, but I have no idea why does this method not work on my computer in case of built-in RichTextBoxes?

Update:

If I write that, the text in the run will be underlined:

<RichTextBox SpellCheck.IsEnabled="True">
    <FlowDocument Language="en">
         <Paragraph>
             <Run>asdfasdf</Run>
         </Paragraph>
    </FlowDocument>
</RichTextBox>

But unfortunately, if I try to enter some other text, it will be ignored. It looks like that the property Language is not setted to english on the edited content. I've tried to set even the Thread's CurrentCulture and CurrentUICulture with no result...

役に立ちましたか?

解決

Okay, finally I have figured out the solution for the issue. The problem can be easily seen if you dig into the WPF source: there is an internal class called TextEditorTyping which has a method called DoTextInput which takes care of inserting user input characters. This method sets the culture property for the inserted range through calling SetSelectedText on TextEditor (TextEditor is another internal class providing text editing services for various controls, such as RichTextBox). Here is that part of the DoTextInput method:

IDisposable disposable = This.Selection.DeclareChangeBlock();
using (disposable)
{
    ITextSelection selection = This.Selection;
    if (!This.AllowOvertype || !This._OvertypeMode)
    {
         flag = false;
    }
    else
    {
         flag = str != "\t";
    }
    ((ITextRange)selection).ApplyTypingHeuristics(flag);
    // SETTING THE CULTURE ->
    This.SetSelectedText(str, InputLanguageManager.Current.CurrentInputLanguage);
    ITextPointer textPointer = This.Selection.End.CreatePointer(LogicalDirection.Backward);
    This.Selection.SetCaretToPosition(textPointer, LogicalDirection.Backward, true, true);
    undoCloseAction = UndoCloseAction.Commit;
}

So the method is using the InputLanguageManager.Current.CurrentInputLanguage which corresponds to the current input language in Windows. If you use an input language which is different than English (which is the default value for FrameworkElement.LanguageProperty) then if you edit the text in your RichTextBox, the inserted element in the FlowDocument would have the current input language as its Language property. For example, if your input language is Hungarian (hu-hu), your FlowDocument would look like this:

<FlowDocument>
     <Paragraph>
         <Run xml:lang="hu-hu">asdfasdf</Run>
     </Paragraph>
</FlowDocument>

This site describes the same problem.

Fortunately, there is a workaround for that. We have already seen the source of the DoTextInput method, and there is a using block inside that:

IDisposable disposable = This.Selection.DeclareChangeBlock();
using (disposable)
{
    ...
    // SETTING THE CULTURE ->
    This.SetSelectedText(str, InputLanguageManager.Current.CurrentInputLanguage);
    ...
}

This is a change block which gets disposed at the last line - after it gets disposed, the TextContainerChanged event is fired which we can handle by overriding the OnTextChanged method of RichTextBox:

protected override void OnTextChanged(TextChangedEventArgs e)
{
    var changeList = e.Changes.ToList();
    if (changeList.Count > 0)
    {
        foreach (var change in changeList)
        {
            TextPointer start = null;
            TextPointer end = null;
            if (change.AddedLength > 0)
            {
                start = this.Document.ContentStart.GetPositionAtOffset(change.Offset);
                end = this.Document.ContentStart.GetPositionAtOffset(change.Offset + change.AddedLength);
            }
            else
            {
                int startOffset = Math.Max(change.Offset - change.RemovedLength, 0);
                start = this.Document.ContentStart.GetPositionAtOffset(startOffset);
                end = this.Document.ContentStart.GetPositionAtOffset(change.Offset);
            }

            if (start != null && end != null)
            {
                var range = new TextRange(start, end);
                range.ApplyPropertyValue(FrameworkElement.LanguageProperty, Document.Language);
            }
        }
    }
    base.OnTextChanged(e);
}

Here we are resetting the Language of the edited range to the proper value - to Document.Language. After this workaround, you can use WPF spellchecking - for example, in French:

<My:CultureIndependentRichTextBox xml:lang="fr-FR" SpellCheck.IsEnabled="True">
     <FlowDocument>
     </FlowDocument>
</My:CultureIndependentRichTextBox>

And it will magically work. :)

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top