【问题标题】:WPF .NET 4.0 as-you-type spell check control?WPF .NET 4.0 as-you-type 拼写检查控件?
【发布时间】:2011-06-16 08:04:58
【问题描述】:

我正在寻找 WPF 拼写检查控件(类似于内置功能,但支持更多字典)。我需要它具有即时输入功能(红色下划线)。它还应该支持超过 4 种语言,而不是内置的 .NET 4.0 拼写检查(例如,英语、西班牙语、德语、意大利语和俄语语言支持会很棒)。

我希望控件具有可在商业 Windows 应用程序中使用的 MIT 或 BSD 许可证。源代码会很棒,因为我想将拼写建议集成到我的自定义右键单击上下文菜单中。

【问题讨论】:

    标签: .net wpf spell-checking


    【解决方案1】:

    我通过添加自己的 SpellCheckerBehavior 将 AvalonEditNHunspell 结合起来。可以在github 找到一个示例项目。它是这样实现的:

    <avalonEdit:TextEditor
        xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"
        Name="editor">
        <avalonEdit:TextEditor.ContextMenu>
            <ContextMenu>
                <MenuItem Command="Undo" />
                <MenuItem Command="Redo" />
                <Separator/>
                <MenuItem Command="Cut" />
                <MenuItem Command="Copy" />
                <MenuItem Command="Paste" />
            </ContextMenu>
        </avalonEdit:TextEditor.ContextMenu>
        <i:Interaction.Behaviors>
            <local:SpellCheckerBehavior />
        </i:Interaction.Behaviors>
    </avalonEdit:TextEditor>
    

    ...像这样修改我的 MainWindow 的构造函数:

    public MainWindow()
    {
        SpellChecker.Default.HunspellInstance = new Hunspell("German.aff", "German.dic");
        editor.TextArea.TextView.LineTransformers.Add(new SpellCheckerColorizer());
    }
    

    ...并将以下类添加到我的项目中以集成它们:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text.RegularExpressions;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Interactivity;
    using System.Windows.Media;
    using ICSharpCode.AvalonEdit;
    using ICSharpCode.AvalonEdit.Document;
    using ICSharpCode.AvalonEdit.Rendering;
    using NHunspell;
    
    namespace Martin
    {
        public class SpellChecker
        {
            static Lazy<SpellChecker> defaultInstance = new Lazy<SpellChecker>(() => new SpellChecker());
            public static SpellChecker Default { get { return defaultInstance.Value; } }
    
            public Hunspell HunspellInstance { get; set; }
    
            public class Word
            {
                public int Index { get; set; }
                public string Value { get; set; }
            }
    
            static IEnumerable<Word> FindWords(string text)
            {
                foreach (Match m in new Regex(@"\w+").Matches(text))
                {
                    yield return new Word() { Index = m.Index, Value = m.Value };
                }
            }
    
            public IEnumerable<Word> FindSpellingErrors(string text)
            {
                foreach (var word in FindWords(text))
                {
                    if (!Spell(word.Value))
                    {
                        yield return word;
                    }
                }
            }
    
            public bool Spell(string word)
            {
                return HunspellInstance.Spell(word);
            }
    
            public List<string> Suggest(string word)
            {
                return HunspellInstance.Suggest(word);
            }
        }
    
        public class SpellCheckerBehavior : Behavior<TextEditor>
        {
            TextEditor textEditor;
            List<Control> originalItems;
    
            protected override void OnAttached()
            {
                textEditor = AssociatedObject;
                if (textEditor != null)
                {
                    textEditor.ContextMenuOpening += new ContextMenuEventHandler(TextEditorContextMenuOpening);
                    textEditor.TextArea.MouseRightButtonDown += new System.Windows.Input.MouseButtonEventHandler(TextAreaMouseRightButtonDown);
                    originalItems = textEditor.ContextMenu.Items.OfType<Control>().ToList();
                }
                base.OnAttached();
            }
    
            protected override void OnDetaching()
            {
                if (textEditor != null)
                {
                    textEditor.ContextMenuOpening -= new ContextMenuEventHandler(TextEditorContextMenuOpening);
                    textEditor.TextArea.MouseRightButtonDown -= new System.Windows.Input.MouseButtonEventHandler(TextAreaMouseRightButtonDown);
                    originalItems = null;
                    textEditor = null;
                }
                base.OnDetaching();
            }
    
            void TextAreaMouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
            {
                var position = textEditor.GetPositionFromPoint(e.GetPosition(textEditor));
                if (position.HasValue)
                {
                    textEditor.TextArea.Caret.Position = position.Value;
                }
            }
    
            void TextEditorContextMenuOpening(object sender, ContextMenuEventArgs e)
            {
                foreach (Control item in textEditor.ContextMenu.Items.OfType<Control>().ToList())
                {
                    if (originalItems.Contains(item)) { continue; }
                    textEditor.ContextMenu.Items.Remove(item);
                }
                var position = textEditor.TextArea.Caret.Position;
                Match word = null;
                Regex r = new Regex(@"\w+");
                var line = textEditor.Document.GetText(textEditor.Document.GetLineByNumber(position.Line));
                foreach (Match m in r.Matches(line))
                {
                    if (m.Index >= position.VisualColumn) { break; }
                    word = m;
                }
                if (null == word ||
                    position.Column > word.Index + word.Value.Length ||
                    SpellChecker.Default.Spell(word.Value))
                {
                    return;
                }
                textEditor.ContextMenu.Items.Insert(0, new Separator());
                var suggestions = SpellChecker.Default.Suggest(word.Value);
                if (0 == suggestions.Count)
                {
                    textEditor.ContextMenu.Items.Insert(0,
                        new MenuItem() { Header = "<No suggestions found>", IsEnabled = false });
                    return;
                }
                foreach (string suggestion in suggestions)
                {
                    var item = new MenuItem { Header = suggestion, FontWeight = FontWeights.Bold };
                    item.Tag =
                        new Tuple<int, int>(
                            textEditor.Document.GetOffset(position.Line, word.Index + 1),
                            word.Value.Length);
                    item.Click += ItemClick;
                    textEditor.ContextMenu.Items.Insert(0, item);
                }
            }
    
            private void ItemClick(object sender, RoutedEventArgs e)
            {
                var item = sender as MenuItem;
                if (null == item) { return; }
                var segment = item.Tag as Tuple<int, int>;
                if (null == segment) { return; }
                textEditor.Document.Replace(segment.Item1, segment.Item2, item.Header.ToString());
            }
        }
    
        public class SpellCheckerColorizer : DocumentColorizingTransformer
        {
            private readonly TextDecorationCollection textDecorationCollection;
    
            public SpellCheckerColorizer()
            {
                textDecorationCollection = new TextDecorationCollection();
                textDecorationCollection.Add(new TextDecoration()
                {
                    Pen = new Pen { Thickness = 1, DashStyle = DashStyles.Dot, Brush = new SolidColorBrush(Colors.Red) },
                    PenThicknessUnit = TextDecorationUnit.FontRecommended
                });
            }
    
            protected override void ColorizeLine(DocumentLine line)
            {
                var lineText = CurrentContext.Document.Text
                    .Substring(line.Offset, line.Length);
                foreach (var error in SpellChecker.Default.FindSpellingErrors(lineText))
                {
                    base.ChangeLinePart(line.Offset + error.Index, line.Offset + error.Index + error.Value.Length,
                                (VisualLineElement element) => element.TextRunProperties.SetTextDecorations(textDecorationCollection));
                }
            }
        }
    }
    

    【讨论】:

    • 我尝试了你的方法,但是当我创建一个名为 SpellChecker.cs 的类并将代码放入时,我收到错误 Behvaiour, OnDetching, OnAttached and AssociatedObject
    • @F4z 您需要添加对 System.Windows.Interactivity 程序集的引用。如果有时间,我会尽量提供一个示例项目作为模板。
    • @F4z 我按照承诺添加了link to an example project
    • @Marin 谢谢!帮助很大:)
    • 还有一件事,这真的准确吗?从某种意义上说,没有任何错误或任何正确的东西?
    【解决方案2】:

    AvalonEdit (http://www.codeproject.com/KB/edit/AvalonEdit.aspx) 是在 WPF 中为 SharpDevelop 从头开始​​编写的。哦,快速 Google 的奇迹。

    如果您一次可以使用一种语言(但可以切换),您可以使用默认的 WPF 功能 (http://joshsmithonwpf.wordpress.com/2007/02/17/spellchecking-and-suggested-spellings-in-a-textbox/)

    【讨论】:

    • 谢谢。默认的 WPF 拼写检查功能不能提供额外的语言,如俄语等。AvalonEdit 看起来是完整的文本编辑器,我基本上只想要在 WPF TextBox 控件之上添加拼写检查的东西,以及可选的字典。
    • 这是我写的一篇文章,用于在 AvalonEdit 中进行快速拼写检查 - codeproject.com/Tips/560292/AvalonEdit-and-Spell-check
    猜你喜欢
    • 2010-09-20
    • 1970-01-01
    • 2011-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-20
    • 1970-01-01
    • 2011-07-08
    相关资源
    最近更新 更多