适用于 Visual Studio 的 NHunspell 组件






4.90/5 (30投票s)
适用于 WindowsForms 的 NHunspell 组件示例

目录
引言
本文介绍了如何基于 NHunspell 创建可重用且可扩展的拼写检查组件。
背景
故事始于大约一年前,当时我决定编写一个带拼写检查功能的应用程序。我发现了许多.NET 组件可以实现我的需求,但除了 NetSpell 之外,它们都是专有的。在研究其他解决方案的工作原理时,我查阅了大量的 C++ 代码。但后来我意识到工作还处于起步阶段,于是从 NetSpell 开始,得到一个大致的解决方案。
NetSpell
NetSpell 是一个免费的拼写检查引擎,于 2004 年为 .NET 开发。
第一个目标是将拼写检查功能添加到 RichTextBox
控件。当我实现它时,我对自己非常自豪。它奏效了!但后来,我需要将此功能添加到 MaskedTextBox
控件。我意识到我的实现太固定了,集成到任何新控件都需要花费几乎相同的时间。此类实现的绑定应尽可能松散,并且应独立于任何控件或项目。它必须是自给自足的。它应该是一个可以随时使用的附加组件,就像一本检查拼写然后放回书架的书一样。
然后我阅读了一些设计模式,开始编写适合任何控件使用的实现。我为 Instance 属性使用了 Singleton,并从任何窗体中使用它来通知拼写检查引擎它应该检查拼写的控件。当然,然后我想在文本上添加一些波浪线,并在此处找到了 WINAPI 解决方案。当我实现它时,我对自己非常自豪。我的拼写检查引擎适用于任何文本控件,还可以绘制波浪线。但随后我发现 WINAPI 下划线在不同的字体下并不总是正常工作。这是一个崩溃。我极不情愿自己绘制这些线条。但然后另一个解决方案从天而降在此。它描述了如何通过覆盖控件的 WndProc
方法并添加一些自定义绘制功能来绘制这些下划线。
NHunspell
Hunspell 是一个常用 Hunspell 引擎的包装器。Hunspell 是一个 C++ 库,用于诸如 OpenOffice、Mozilla Firefox/Thunderbird、Opera 10、The Bat! 等应用程序,以及许多其他应用程序。
几个月后,我发现有一个 Hunspell 的 .NET 包装器。我查看了我之前的解决方案,发现如果我想将拼写检查添加到其他项目,我仍然需要手动复制大量代码。这是一个崩溃。我意识到此类事物不仅不应绑定到控件,而且不应绑定到初始解决方案。代码必须是可重用的。如果您编写代码时能考虑到这一点,您的代码的价值将远不止一次实现。我需要编写一个组件,可以简单地包含在我的解决方案中使用。该组件应自行完成所有工作,并公开尽可能多的事件来满足每个程序员的需求(我在使用 NetSpell 时遇到了一些问题,因为没有我迫切需要的事件,我不得不重写现有库中的一些代码并重新编译它)。
我明白有些控件应该处理下划线,有些则不需要。所以我描述了基本的 ISpellingControl
接口,然后用 IUnderlineable
接口对其进行了扩展。现在我有了 IUnderlineableSpellingControl
接口,用于 richTextBox
等控件,以及用于所有其他控件的 ISpellingControl
。
概念

正如您在第一张图中看到的,NHunspellWrapper
独立运行,并向外部公开 Instance
属性。我们每个应用程序只需要一个实例。我们可以拥有任意数量的 SpellingWorkers
。SpellingWorker
类链接到它正在处理的编辑器。
注意:在不同的库中,属性可以具有相同的名称甚至相同的声明,但在运行时,如果您尝试将一种类型转换为另一种类型,属性将返回 null
。
例如:DevExpress.XtraEditors.BaseEdit
有一个 Text
属性。在您的代码中,您编写了一个处理 TextBoxBase
的类,该类也有一个这样的属性。如果您尝试将一种类型转换为另一种类型并使用其 Text
属性,它将返回 null
值。所以要小心。
下一张图显示了如何解决此问题。

正如我之前指出的,我们在这里使用依赖注入。因此,现在我们的组件可以通过此接口处理具有相同定义的类。但是,如果我们要使用的 ClassA
没有必要的定义,我们应该为组件提供这些定义,以便它能正确处理 ClassA
。
Using the Code
要使用 SpellingWorker
组件,您需要在项目中添加对 NHunspellComponent
的引用。还要添加兼容的字典到您的项目以及Hunspellx86/x64.dll,并设置您项目的属性以使生成后操作如下所示。
xcopy "$(ProjectDir)Dictionaries" "$(TargetDir)" /ICRY
xcopy "$(ProjectDir)Hunspell" "$(TargetDir)" /ICRY
将组件拖放到窗体上,然后选择适当的编辑器(实现 ISpellingControl
或 IUnderlinableSpellingControl
接口)。就是这样。
拼写控件
实现 ISpellingControl
通常不需要太多工作,因为文本编辑器控件已经存在所有属性和方法。因此,正如您在示例应用程序中看到的,我们需要将此接口添加到 CustomMaskedTextBox
并仅实现 ISpellingControl
的独特属性(例如 IsSpellingEnabled
、IsSpellingAutoEnabled
、IsPassWordProtected
)。您可以阅读有关接口注入的内容,控制反转和依赖注入的文章。
拼写窗口

拼写窗口应通知 NHunspellWrapper
其所做的更改。它还应公开允许其他类更改其视图的属性。
每个控件的选项都存储在拼写工作器中。因此,您可以设置单独的选项。
波浪线
拼写检查过程中的主要问题之一是通知用户刚刚犯的错误。您可以使用任何您想要的动作。您可以在 UnderliningChanged
事件中为每个拼写错误的单词发射烟花。但我更喜欢用红色的波浪线来标记它们。所以我描述了在示例应用程序中是如何完成的。
- 实现
IUnderlinable
的控件的WndProc
方法被重写以调用CustomPaint
方法。 CustomPaint
方法用于处理编辑器区域的位图。它在UnderlinedSections
属性的每个项目下绘制波浪线。DrawWave
方法用于从点到点绘制锯齿线。
关注点
我学会了如何编写组件,最令人恼火的是,您必须做一些繁琐的工作才能绘制我们用户习惯的那些波浪线。它们不是组件工作的必要条件,并且在 TestApplication
项目的 CustomPaintRichText
类中显示。您可以将其视为一个示例,但如果您需要真正起作用的东西,它需要经过充分的测试。
此外,我在将 SpellingWorker
组件显示在 Visual Studio 工具箱中时遇到了麻烦。
编写任何库最聪明的方法是将其实现为一个可重用库,这样您就不需要在未来重写代码。设计模式和实践将有助于您实现这一点,当然,反复解决问题以获得经验。除非您具有超感官知觉,否则您将无法从仅阅读文章中获得任何东西。
历史
这是文章的初稿。
更新了源代码