65.9K
CodeProject 正在变化。 阅读更多。
Home

NHunspellToolTip - 使用 Hunspell for .NET 的拼写检查 ToolTip

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (13投票s)

2010 年 2 月 17 日

CPOL

5分钟阅读

viewsIcon

27937

downloadIcon

1020

此 ToolTip 允许您在看不到全部文本时,对项目或控件的文本进行拼写检查。

toolTipScreenShot.JPG

背景

作为程序员,我们经常被要求开发一个界面来方便地访问数据库。在我的例子中,我处理的是一个使用雪花模型设计的数据库。某些表包含与其他包含数据的表相关的索引。例如,有三个级别的“操作”:“目标”、“恢复操作”和“操作步骤”。其中任何一个都可以包含优先级、成本、持续时间、恢复合作伙伴和评论的信息,以及操作文本。在人眼看来,数据库是无法阅读的。

在开发用户访问的 GUI 时,用户无需从一个屏幕修改所有信息。相反,我想向用户提供数据的快照,并使用 ToolTip 提供更详细的信息,使用 ContextMenu 编辑这些信息。提供信息的控件是一个自定义的 ListView(如下所示)。

ScreenshotNoToolTip.jpg

这效果很好,用户发现使用 GUI 浏览数据库很容易。然而,我很快意识到我们的用户在输入时不够小心,产生了大量的拼写错误。因此,我想提供一种方法,让用户获得一个视觉提示,表明他们犯了拼写错误。

为此,我开发了一个 IExtenderProvider,它将扩展 TextBox 以提供类似 Microsoft Word 的即时拼写检查功能。我使用了 Hunspell for .NET 并创建了 NHunspellTextBoxExtender。我可以在用户编辑文本时使用 TextBox 扩展程序,但这仍然没有为主窗体上的信息(由 ListView 提供)提供任何拼写检查功能。

那么,问题来了,我如何提供这个视觉提示,表明信息中有拼写错误呢?我已经在使用 ToolTip 提供信息了,为什么不创建一个可以拼写检查其文本并以这种方式提供视觉提示的 ToolTip 呢?这样,用户就可以看到拼写错误,并进去修复它。这个自定义 ToolTip 提供了如下所示的功能:

toolTipScreenShot.JPG

拼写检查 ToolTip

与我之前的控件(NHunspellTextBoxExtender)不同,对于这个控件,我不需要使用 IExtenderProvider,因为我只改变了一个 ToolTip。相反,我选择创建一个继承自 ToolTip 的新控件。拼写检查与 TextBox 版本非常相似。但因为我将其限制为仅显示,并且因为控件的文本只会设置,而不会追加或截断,所以我可以精简 SpellCheckControl

SpellCheckControl

SpellCheckControl 负责所有拼写检查。当 ToolTip 收到 Draw 命令时,它会调用 SpellCheckControlSetText 方法。SetText 方法会遍历所有文本,查找任何拼写错误的单词,并使用 CharacterRange 来识别它们。

类结构如下所示

Imports System.Drawing
Imports System.IO
Imports System.Windows.Forms
Imports System.Reflection

Public Class SpellCheckControl
    Private FullText As String
    Private _Text(,) As String
    Public myNHunspell As Object = Nothing
    Private _spellingErrors() As String
    Private _spellingErrorRanges() As CharacterRange

    Public Sub New(ByRef HunspellObject As Object) 

    Public Sub SetText(ByVal Input As String)

    Private Function FindFirstLetterOrDigitFromPosition(_
                     ByVal SelectionStart As Long) As Long

    Private Function FindLastLetterOrDigitFromPosition(_
                     ByVal SelectionStart As Long) As Long

    Public Function GetSpellingErrorRanges() As CharacterRange()

    Public Function HasSpellingErrors() As Boolean

    Public Sub SetSpellingErrorRanges()

End Class

ToolTip Draw

一旦识别出拼写错误(由于 NHunspell,速度非常快),ToolTip 就需要被绘制。要做到这一点,我只需要处理 ToolTip_Draw 方法。此时,我已经使用 e.DrawBackground() 命令绘制了背景。下一步是绘制红色的波浪线。要做到这一点,我们首先必须识别出拼写错误的位置,并确定红色线条应该绘制在哪里。这有点棘手,因为我们必须跟踪当前文本所在的行。

但是,一旦我们确定了绘制线条的位置,我们就必须实际绘制它。我使用了一些可以在这个 博客 上找到的代码。Draw 事件的完整代码如下所示:

Private Sub NHunspellToolTip_Draw(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.DrawToolTipEventArgs) _
        Handles Me.Draw
    e.DrawBackground()

    mySpellCheckControl.SetText(e.ToolTipText)

    'Now we do the custom drawing
    myBitmap = New Bitmap(e.Bounds.Width, e.Bounds.Height)
    bufferGraphics = Graphics.FromImage(myBitmap)
    bufferGraphics.Clip = New Region(e.Bounds)

    Dim currentRange As CharacterRange

    For Each currentRange In mySpellCheckControl.GetSpellingErrorRanges
        Dim startPoint, endPoint As Point
        Dim bottom, left, right As Integer

        'Determine which line the current word is on
        Dim lastNewline As Integer = 1
        For i = 1 To currentRange.First
            If Mid(e.ToolTipText, i, 1) = vbLf Then
                lastNewline = i + 1
            End If
        Next

        'Get the text of the line to the end of the word
        Dim lineToEndofWord As String = Mid(e.ToolTipText, lastNewline, _
                                            ((currentRange.First - lastNewline) + _
                                              currentRange.Length + 1))

        'Figure out how wide and tall the text before this is word is
        'Measure the text starting from the beginning 
        'to the end of the word to get the borrom coordinates
        Dim newSize As SizeF = _
          TextRenderer.MeasureText(Microsoft.VisualBasic.Strings.Left(e.ToolTipText, _
                                   (currentRange.First + currentRange.Length)), _
                                    e.Font, e.Bounds.Size, TextFormatFlags.Left)
        bottom = newSize.Height - 2

        'Now measure the text from the beginning of the current line 
        'to the end of the word to get the right coordinates
        newSize = TextRenderer.MeasureText(lineToEndofWord, e.Font, _
                  e.Bounds.Size, TextFormatFlags.Left)
        right = newSize.Width
        endPoint = New Point(right, bottom)

        'Now we can backtrack and find out how wide that text is
        newSize = TextRenderer.MeasureText(Mid(e.ToolTipText, _
                        currentRange.First + 1, currentRange.Length), _
                        e.Font, e.Bounds.Size, TextFormatFlags.Left)
        left = right - newSize.Width
        startPoint = New Point(left, bottom)

        startPoint.X += 2
        endPoint.X -= 4

        DrawWave(startPoint, endPoint)
    Next

    toolTipGraphics = e.Graphics

    toolTipGraphics.DrawImageUnscaled(myBitmap, 0, 0)

    e.DrawBorder()
    Dim stringFlags As New StringFormat()
    stringFlags.Alignment = StringAlignment.Near
    stringFlags.LineAlignment = StringAlignment.Near

    TextRenderer.DrawText(e.Graphics, e.ToolTipText, e.Font, _
                          e.Bounds, Color.Black, flags:=TextFormatFlags.Left)
End Sub

Using the Code

就是这样!这个工具非常简单易用……只需从上面的链接下载 DLL,并将其添加到您的工具箱之一。拼写检查会自动完成,并且这个 ToolTip 可以像标准的 ToolTip 一样使用。

关注点

我能够用这个 ToolTip 实现一些不同的东西。我不确定为什么我无法在 NHunspellTextBoxExtender 中让它工作,但我可以在没有 NHunspell.dll 文件的情况下加载这个 ToolTip。这意味着当我打包这个工具时,我不需要包含 NHunspell.dll。为了做到这一点,我将 NHunspell.dll 文件嵌入到项目中。然后,当我创建 Hunspell 对象时,我使用原始程序集来创建它。

这需要一些工作,而且你必须预先知道参数。首先,我必须加载程序集。这只需要一行代码

Dim a As Assembly = Assembly.Load(My.Resources.NHunspell)

然后我必须获取我试图创建的对象的 Type,并创建一个 ConstructorInfo 类。Hunspell 对象的构造函数接受两个字符串。执行此操作的代码需要更多几行代码,但仍然相对简单:

Dim type_l As Type = a.GetType("NHunspell.Hunspell")
Dim types(1) As Type
types(0) = GetType(String)
types(1) = GetType(String)
Dim ctor As ConstructorInfo = type_l.GetConstructor(types)

最后要做的是 Invoke 构造函数,它将返回一个 Object。要做到这一点,我必须设置将要传递的参数。Invoke 方法接受一个 Objects 数组。Hunspell 的第一个参数是 .aff 文件,第二个参数是 .dic 文件。不进行任何错误检查的实现如下所示:

Dim params(1) As Object
params(0) = USaff
params(1) = USdic

Dim result As Object = Nothing
result = ctor.Invoke(params)

当我实际执行此操作时,我会将 result = ctor.Invoke(params) 放入一个 Try/Catch 块中,因为我必须确保几个非托管 DLL 可用。我已在我的 NHunspellTextBoxExtender 文章 中提供了更多相关信息。

这种方法唯一的问题是 Visual Studio 的 IntelliSense 将无法按此方式工作。这就是为什么这个步骤要到最后才实现。在此之前,我包含对 NHunspell DLL 的引用,这为我提供了 IntelliSense。然后,在最终确定之前,我只需注释掉那个 Import 语句,并将 Hunspell 对象更改为 Object

历史

  • 2010 年 2 月 17 日:创建文章。
© . All rights reserved.