问题
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...
回答1:
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. :)
来源:https://stackoverflow.com/questions/18982057/spell-checking-doesnt-work-with-wpf-richtextbox