|
Another deficiency I've noticed with i00SpellCheck is its inability to properly handle text within a rich-text box that is protected or hidden (invisible). Fortunately, I've figured out a fix for that--which, unfortunately, requires making strategic changes in several code files. First, I'll explain the change in functionality that my modifications will do:
1. It causes the SpellCheckDialog to skip over any and all cases of a word whose text protected or semi-protected, and, unless otherwise directed, any and all cases of a word whose text is hidden or semi-hidden.
2. It prevents context-menu options from being added when the user right-clicks on a word in a rich-text box which is protected/semi-protected.
3. It still draws a wavy line, when that feature is enabled, under "misspelled" words that are wholly or partially protected/hidden. One can use the extension methods below to disable that line-drawing in such cases, but I've found that that slows down the rendering of rich text. If you want to keep wavy lines from appearing under protected/hidden text, you should modify the Drawing.vb file's CustomPaint procedure to invoke parentTextBox.SetRedraw(False) and Dim ScrollPosition As Point = parentTextBox.GetScrollPosition() before it goes through the text, check the value of parentTextBox.GetTextType(LetterIndex, words(iWord).Length) as well as (the already-checked-for) e.Default before drawing a line, then invoke parentTextBox.SetScrollPosition(ScrollPosition) and parentTextBox.SetRedraw(True) after the text is done being parsed. (Better yet, use a Try block with after redrawing is turned off and the scroll position is saved, with the resetting of these attributes occurring the Finally clause in order to ensure that the text box can redraw and is in the correct scroll position after CustomPaint finishes even if an exception occurs during the word-parsing.)
First of changes:
In the SpellCheck\Engine\Extension.vb file, create a new module, named ExtendedTextReading, and move the ExtendedText and FullSelectionLength extension methods thereto. Also, add the SetRedraw, GetScrollPosition, SetScrollPosition, HasFlag, (only included because this method doesn't come automatically with pre-4.0 versions of .NET!), and GetTextType extension methods, and the various Private components--as follows:
#Region "Extended Text Reading"
Public Module ExtendedTextReading
Private Const WM_USER As Int32 = &H400&
Private Const EM_GETSCROLLPOS As Integer = WM_USER + 221, _
EM_SETSCROLLPOS As Integer = WM_USER + 222
Private Const WM_SETREDRAW As Integer = &HB
Private Const HiddenTextSyntax As String = "(^|[^\\])(\\\\)*\\v[^0]", _
ProtectedTextSyntax As String = "(^|[^\\])(\\\\)*\\protect[^0]"
Private HiddenTextChecker As Regex = _
New Regex(HiddenTextSyntax, _
RegexOptions.IgnoreCase Or RegexOptions.Compiled Or RegexOptions.ExplicitCapture), _
ProtectedTextChecker As Regex = _
New Regex(ProtectedTextSyntax, _
RegexOptions.IgnoreCase Or RegexOptions.Compiled Or RegexOptions.ExplicitCapture)
<DllImport("user32.dll")> _
Private Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Int32, _
ByVal wParam As Int32, ByRef lParam As Point) As Int32
End Function
<DllImport("user32.dll")> _
Private Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Int32, _
ByVal wParam As Integer, ByVal lParam As Integer) As Int32
End Function
<Flags()> _
Public Enum TypeOfText
Normal = 0
Hidden = 1
[Protected] = 2
End Enum
<System.Runtime.CompilerServices.Extension()> _
Public Function HasFlag(ByVal TextType As TypeOfText, _
ByVal TypeFlag As TypeOfText) As Boolean
Return (TextType And TypeFlag) = TypeFlag
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function GetScrollPosition(TextBox As TextBoxBase) As Point
Dim RTBScrollPoint As Point = Nothing
SendMessage(TextBox.Handle, EM_GETSCROLLPOS, 0, RTBScrollPoint)
Return RTBScrollPoint
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Sub SetScrollPosition(TextBox As TextBoxBase, _
ByVal RTBScrollPoint As Point)
SendMessage(TextBox.Handle, EM_SETSCROLLPOS, 0, RTBScrollPoint)
End Sub
<System.Runtime.CompilerServices.Extension()> _
Public Sub SetRedrawMode(TextBox As TextBoxBase, _
ByVal OnOrOff As Boolean)
SendMessage(TextBox.Handle, WM_SETREDRAW, CType(OnOrOff, Integer) And 1, 1)
If OnOrOff Then
TextBox.Refresh()
End If
End Sub
<System.Runtime.CompilerServices.Extension()> _
Public Function FullSelectionLength(TextBox As TextBoxBase) As Integer
If TypeOf TextBox IsNot RichTextBox Then
Return TextBox.SelectionLength
End If
Dim RichTextBox As RichTextBox = DirectCast(TextBox, RichTextBox)
Dim TypeRTB As Type = RichTextBox.GetType
Dim fi As FieldInfo = _
TypeRTB.GetField("curSelStart", _
BindingFlags.Instance Or BindingFlags.NonPublic)
Dim nCurSelStart As Integer = CType(fi.GetValue(RichTextBox), Integer)
fi = TypeRTB.GetField("curSelEnd", _
BindingFlags.Instance Or BindingFlags.NonPublic)
Dim nCurSelEnd As Integer = _
Math.Min(CType(fi.GetValue(RichTextBox), Integer), RichTextBox.TextLength)
If nCurSelStart = -1 AndAlso nCurSelEnd = -1 Then
Return 0
Else
Return _
nCurSelEnd - nCurSelStart
End If
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function ExtendedText(TextBox As TextBoxBase, _
Optional SelectionOnly As Boolean = False) As String
If TypeOf TextBox IsNot RichTextBox Then
If SelectionOnly Then
Return TextBox.SelectedText
Else
Return TextBox.Text
End If
End If
Dim RichTextBox As RichTextBox = DirectCast(TextBox, RichTextBox)
Dim TypeRTB As Type = RichTextBox.GetType
Dim prop As PropertyInfo = _
TypeRTB.GetProperty("WindowText", _
BindingFlags.Instance Or BindingFlags.NonPublic)
Dim Text As String = _
CType(prop.GetValue(RichTextBox, {}), String).Replace(ControlChars.Cr, "")
If SelectionOnly Then
Dim fi As FieldInfo = _
TypeRTB.GetField("curSelStart", _
BindingFlags.Instance Or BindingFlags.NonPublic)
Dim nCurSelStart As Integer = CType(fi.GetValue(RichTextBox), Integer)
fi = TypeRTB.GetField("curSelEnd", _
BindingFlags.Instance Or BindingFlags.NonPublic)
Dim nCurSelEnd As Integer = _
Math.Min(CType(fi.GetValue(RichTextBox), Integer), Text.Length)
If (nCurSelStart = -1 AndAlso nCurSelEnd = -1) _
OrElse nCurSelStart = nCurSelEnd Then
Text = ""
Else
Text = Text.Substring(nCurSelStart, nCurSelEnd - nCurSelStart)
End If
End If
Return Text
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function GetTextType(TextBox As TextBoxBase, _
ByVal StartIndex As Integer, ByVal WordLength As Integer) As TypeOfText
Dim TextType As TypeOfText = TypeOfText.Normal
If TypeOf TextBox Is RichTextBox Then
Dim RichTextBox As RichTextBox = DirectCast(TextBox, RichTextBox)
With RichTextBox
Dim CurrentStart As Integer = .SelectionStart, CurrentLength As Integer = .SelectionLength
.Select(StartIndex, WordLength)
If HiddenTextChecker.IsMatch(.SelectedRtf) _
OrElse .SelectionLength < WordLength Then
TextType = TextType Or TypeOfText.Hidden
End If
If .SelectionProtected _
OrElse ProtectedTextChecker.IsMatch(.SelectedRtf) Then
TextType = TextType Or TypeOfText.Protected
End If
.Select(CurrentStart, CurrentLength)
End With
End If
Return TextType
End Function
#End Region
Next, make the following additions and modifications to the file HTMLSpellCheck.vb, within class HTMLSpellCheck:
Private Function CanChangeWord(ByVal StartIndex As Integer, ByVal Length As Integer) As Boolean
If Length = 0 Then
Return False
ElseIf Me.TextBox Is Nothing OrElse TypeOf Me.TextBox Is TextBox Then
Return True
End If
With Me.TextBox
Dim TextType As TypeOfText = .GetTextType(StartIndex, Length)
Select Case True
Case TextType.HasFlag(TypeOfText.Protected)
Return False
Case TextType.HasFlag(TypeOfText.Hidden)
Return SpellCheckHiddenText
Case Else
Return True
End Select
End With
End Function
Private Sub UpdateDocument()
Dim DocumentText As New System.Text.StringBuilder
Dim UpTo = 0
Dim UsesTextBox As Boolean = Me.TextBox IsNot Nothing
For Each item In mc_Words
RemoveHandler item.UpdateClassEvent, AddressOf UpdateClassEvent
RemoveHandler item.SelectionChangedEvent, AddressOf WordSelectionChangedEvent
RemoveHandler item.UpdateWordEvent, AddressOf UpdateWordEvent
Next
mc_Words.Clear()
Dim ScrollPosition As Point
If UsesTextBox Then
Me.TextBox.SetRedrawMode(False)
ScrollPosition = Me.TextBox.GetScrollPosition()
End If
Try
For Each item In _
Dictionary.Formatting.RemoveWordBreaks(mc_Text).Split(" "c)
Dim ThisWordItem = New SpellCheckDialogWords(item, UpTo, CanChangeWord(UpTo, item.Length))
AddHandler ThisWordItem.UpdateClassEvent, AddressOf UpdateClassEvent
AddHandler ThisWordItem.SelectionChangedEvent, AddressOf WordSelectionChangedEvent
AddHandler ThisWordItem.UpdateWordEvent, AddressOf UpdateWordEvent
mc_Words.Add(ThisWordItem)
If ThisWordItem.OrigWord <> "" Then
DocumentText.Append("<a href='" & mc_Words.Count - 1 & "' class='Pending' id='" _
& mc_Words.Count - 1 & "'>" & ThisWordItem.OrigWord & "</a>")
End If
UpTo += item.Length + 1
If UpTo <= mc_Text.Length Then
DocumentText.Append(mc_Text.Substring(UpTo - 1, 1).Replace("&", "&"))
End If
Next item
Finally
If UsesTextBox Then
Me.TextBox.SetScrollPosition(ScrollPosition) : Me.TextBox.SetRedrawMode(True)
End If
End Try
mc_AllowNavigation = True
MyBase.DocumentText = HTMLStyle() & "<BODY onselectstart='return false;'>" & System.Text.RegularExpressions.Regex.Replace(DocumentText.ToString, "\r\n|\n\r|\r|\n", "<BR>")
mc_AllowNavigation = False
End Sub
#End Region
Public CanChangeWord As Boolean
Public Sub New(ByVal OrigWord As String, _
Optional ByVal StartIndex As Integer = 0, _
Optional ByVal CanChangeText As Boolean = True)
Me.OrigWord = OrigWord : mc_NewWord = OrigWord
Me.CanChangeWord = CanChangeText : Me.StartIndex = StartIndex
End Sub
Public OrigWord As String
Dim mc_NewWord As String
Public Property NewWord(Optional ByVal Autoupdate As Boolean = True) As String
Get
Return mc_NewWord
End Get
Set(ByVal value As String)
If Me.CanChangeWord AndAlso value <> mc_NewWord Then
mc_NewWord = value : Me.Changed = True
If Autoupdate Then
UpdateWord()
End If
End If
End Set
End Property
#Region "Constructors"
Public TextBox As TextBoxBase = Nothing
Public SpellCheckHiddenText As Boolean = False
Public Sub New(Optional TextBox As TextBoxBase = Nothing, _
Optional ByVal SpellCheckHiddenText As Boolean = False)
MyBase.AllowWebBrowserDrop = False
MyBase.IsWebBrowserContextMenuEnabled = False
Me.TextBox = TextBox : Me.SpellCheckHiddenText = SpellCheckHiddenText
End Sub
#End Region
#Region "Other Public Subs"
Dim mc_Text As String
Private Settings As New SpellCheckSettings
Public Sub SetText(Optional ByVal Text As String = "", Optional ByVal Settings As SpellCheckSettings = Nothing)
If Settings IsNot Nothing Then Me.Settings = Settings
If String.IsNullOrEmpty(Text) AndAlso Me.TextBox IsNot Nothing Then
mc_Text = Me.TextBox.ExtendedText()
Else
mc_Text = Text
End If
UpdateDocument()
End Sub
Now change the extTextBoxContextMenu_Opening procedure of SpellCheck\Controls\Menu.vb as follows:
Private Sub extTextBoxContextMenu_MenuOpening(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles extTextBoxContextMenu.MenuOpening
If OKToSpellCheck Then
SpellMenuItems.ContextMenuStrip = extTextBoxContextMenu.ContextMenuStrip
If Settings.IgnoreWordOverride(extTextBoxContextMenu.MenuSpellClickReturn.Word) Then
Else
With extTextBoxContextMenu.MenuSpellClickReturn
Beep
parentTextBox.SetRedrawMode(False)
Dim ScrollPosition As Point = parentRichTextBox.GetScrollPosition()
Dim TextType As TypeOfText = parentTextBox.GetTextType(.WordStart, .Word.Length)
If TextType = TypeOfText.Normal Then
SpellMenuItems.AddItems(.Word, CurrentDictionary, CurrentDefinitions, CurrentSynonyms, Settings)
End If
parentRichTextBox.SetScrollPosition(ScrollPosition)
parentTextBox.SetRedrawMode(True)
End With
End If
End If
End Sub
Next, go to the SpellCheckDialog.vb file, and make the following additions and modifications:
Private SourceTextBox As TextBoxBase, SpellCheckHiddenText As Boolean, ShowingSuggestions As Boolean
Private Function EnablingWordChange(Word As HTMLSpellCheck.SpellCheckDialogWords) As Boolean
Return ShowingSuggestions AndAlso Word.CanChangeWord
End Function
Public Overloads Function ShowDialog(ByVal owner As TextBoxBase, _
ByVal SpellCheckControlBase As SpellCheckControlBase, Optional ByVal Text As String = "", _
Optional ByVal SpellCheckHiddenText As Boolean = False) As List(Of HTMLSpellCheck.SpellCheckDialogWords)
Me.SourceTextBox = owner : Me.SpellCheckHiddenText = SpellCheckHiddenText
Me.SpellCheckControlBase = SpellCheckControlBase
If String.IsNullOrEmpty(Text) Then
Text = owner.ExtendedText()
End If
With HtmlSpellCheck1
.TextBox = owner : .SpellCheckHiddenText = SpellCheckHiddenText
.Dictionary = SpellCheckControlBase.CurrentDictionary
.SetText(Text)
End With
pbChangeAll.Maximum = HtmlSpellCheck1.Words.Count - 1
If HtmlSpellCheck1.Words.Count > 0 AndAlso Not EnablingWordChange(HtmlSpellCheck1.Words(0)) Then
btnChange.Enabled = False : btnSkip_Click(btnSkip, EventArgs.Empty)
End If
MyBase.StartPosition = FormStartPosition.CenterParent
MyBase.ShowDialog(owner)
Return HtmlSpellCheck1.Words
End Function
Private Function SelectedWord() As HTMLSpellCheck.SpellCheckDialogWords
Dim sw As HTMLSpellCheck.SpellCheckDialogWords = _
(From xItem In HtmlSpellCheck1.Words Where xItem.Selected).FirstOrDefault
btnChange.Enabled = _
sw IsNot Nothing AndAlso EnablingWordChange(sw)
Return sw
End Function
Private Sub HtmlSpellCheck1_SelectionChanged(ByVal sender As Object, ByVal e As HTMLSpellCheck.HTMLWordEventArgs) _
Handles HtmlSpellCheck1.SelectionChanged
If pnlSuggestions.InvokeRequired Then
Dim HtmlSpellCheck1_SelectionChanged_cb As New HtmlSpellCheck1_SelectionChanged_cb(AddressOf HtmlSpellCheck1_SelectionChanged)
pnlSuggestions.Invoke(HtmlSpellCheck1_SelectionChanged_cb, sender, e)
Else
FillSuggestions()
ShowHideSuggestions(True)
txtChangeTo.Focus()
btnChange.Enabled = _
e.Word IsNot Nothing AndAlso EnablingWordChange(e.Word)
ChangeToUseOldWord = True
txtChangeTo_TextChanged(txtChangeTo, EventArgs.Empty)
ChangeToChanged = False
If e.Word.Selected AndAlso Not btnChange.Enabled Then
MoveToNextWordError()
End If
End If
End Sub
Private Sub ShowHideSuggestions(ByVal Show As Boolean)
ShowingSuggestions = Show
If pnlSuggestions.InvokeRequired Then
Dim ShowHideSuggestions_cb As New ShowHideSuggestions_cb(AddressOf ShowHideSuggestions)
pnlSuggestions.Invoke(ShowHideSuggestions_cb, Show)
Else
Dim theSelectedWord = SelectedWord()
pnlSuggestions.Visible = Show
btnIgnore.Enabled = Show AndAlso theSelectedWord IsNot Nothing AndAlso theSelectedWord.Changed = False AndAlso theSelectedWord.SpellCheckState <> HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK
btnSkip.Enabled = Show AndAlso (From xItem In HtmlSpellCheck1.Words Where (xItem.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error OrElse xItem.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case) AndAlso xItem IsNot theSelectedWord).Count > 0
tstbChangeTo.Enabled = _
MenuCurrentWord IsNot Nothing AndAlso EnablingWordChange(MenuCurrentWord)
btnRevert.Enabled = Show AndAlso theSelectedWord IsNot Nothing AndAlso theSelectedWord.Changed = True
btnChangeAll.Enabled = Not ChangeingAll AndAlso (From xItem In HtmlSpellCheck1.Words Where (xItem.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case OrElse xItem.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error)).Count > 0
btnRevertAll.Enabled = (From xItem In HtmlSpellCheck1.Words Where xItem.Changed = True).Count > 0
End If
End Sub
Private Sub txtChangeTo_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles txtChangeTo.KeyUp
Select Case e.KeyCode
Case Keys.Apps
e.Handled = True
If txtChangeTo.ContextMenu Is Nothing Then
txtChangeTo.ContextMenu = New ContextMenu
End If
Dim theSelectedWord = SelectedWord()
If theSelectedWord IsNot Nothing Then
MenuCurrentWord = theSelectedWord
tstbChangeTo.Enabled = _
MenuCurrentWord IsNot Nothing AndAlso EnablingWordChange(MenuCurrentWord)
UpdateMenuItems()
OpenedByClick = False
cmsHTMLSpellCheck.Show(HtmlSpellCheck1, Point.Empty)
End If
End Select
End Sub
Private Sub SpellMenuItems_WordChanged(ByVal sender As Object, ByVal e As Menu.AddSpellItemsToMenu.SpellItemEventArgs) Handles SpellMenuItems.WordChanged
If MenuCurrentWord.CanChangeWord Then
MenuCurrentWord.NewWord = e.Word
MenuCurrentWord.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK
End If
WordUpdatedFromMenu()
End Sub
Private Sub tstbChangeTo_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles tstbChangeTo.KeyPress
If e.KeyChar = vbCr Then
If MenuCurrentWord.CanChangeWord Then
MenuCurrentWord.NewWord = tstbChangeTo.Text
MenuCurrentWord.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK
Else
Beep() : MessageBox.Show("This word is protected and/or hidden!")
End If
UpdateMenuItems()
WordUpdatedFromMenu()
End If
End Sub
Private Sub UpdateMenuItems()
If Replace(Dictionary.Formatting.RemoveWordBreaks(MenuCurrentWord.NewWord), " ", "") = MenuCurrentWord.NewWord Then
SpellMenuItems.ContextMenuStrip = cmsHTMLSpellCheck
SpellMenuItems.RemoveSpellMenuItems()
If MenuCurrentWord.CanChangeWord Then
SpellMenuItems.AddItems(MenuCurrentWord.NewWord, SpellCheckControlBase.CurrentDictionary, SpellCheckControlBase.CurrentDefinitions, SpellCheckControlBase.CurrentSynonyms, SpellCheckControlBase.Settings)
End If
End If
mtsChangeTo.Text = "Change " & MenuCurrentWord.NewWord & " to:"
tstbChangeTo.Text = MenuCurrentWord.NewWord
tsiRevertTo.Text = "Revert to " & MenuCurrentWord.OrigWord
tsiRevertTo.Visible = MenuCurrentWord.Changed
cmsHTMLSpellCheck_LocationChanged(cmsHTMLSpellCheck, EventArgs.Empty)
End Sub
Private Sub HtmlSpellCheck1_WordSpellChecked(ByVal sender As Object, ByVal e As HTMLSpellCheck.HTMLWordEventArgs) Handles HtmlSpellCheck1.WordSpellChecked
If pbChangeAll.Visible Then
Dim Progress As Integer = HtmlSpellCheck1.Words.IndexOf(e.Word)
pbChangeAll.SafeInvoke(Function(x As ProgressBar) InlineAssignHelper(x.Value, Progress))
End If
pnlPleaseWait.SafeInvoke(Function(x As Panel) InlineAssignHelper(x.Visible, True))
If e.Word.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error OrElse e.Word.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case Then
If ChangeingAll Then
Dim Suggestions = GetSuggestions(e.Word.NewWord)
If Suggestions.Count > 0 AndAlso e.Word.CanChangeWord Then
e.Word.NewWord = Suggestions.First
e.Word.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK
If SelectedWord() Is e.Word Then
HtmlSpellCheck1_SelectionChanged(HtmlSpellCheck1, New HTMLSpellCheck.HTMLWordEventArgs() With {.Word = e.Word})
End If
End If
Exit Sub
End If
If pnlSuggestions.Visible = False Then
e.Word.Selected = True
End If
ShowHideSuggestions(pnlSuggestions.Visible)
End If
End Sub
Private Function MoveToNextWordError(Optional ByVal DoRecheck As Boolean = True) As Boolean
With HtmlSpellCheck1
MoveToNextWordError = False
Dim StartIndex = 0
Dim theSelectedWord = SelectedWord()
If theSelectedWord IsNot Nothing Then
theSelectedWord.Selected = False
StartIndex = .Words.IndexOf(theSelectedWord) + 1
End If
ReCheck:
For i = StartIndex To .Words.Count - 1
If .Words(i) IsNot Nothing Then
Select Case .Words(i).SpellCheckState
Case HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error, _
HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case
If .Words(i).CanChangeWord Then
.Words(i).Selected = True
MoveToNextWordError = True : Exit Function
End If
End Select
If .Words(i).Selected Then
.Words(i).Selected = False
End If
End If
Next
If DoRecheck Then
If StartIndex = 0 Then
CompleteSpellCheck() : Exit Function
End If
StartIndex = 0
GoTo ReCheck
End If
End With
End Function
Private Sub btnChange_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnChange.Click
Dim theSelectedWord = SelectedWord()
If theSelectedWord IsNot Nothing Then
If theSelectedWord.CanChangeWord Then
theSelectedWord.NewWord = txtChangeTo.Text
theSelectedWord.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK
Else
Beep() : MessageBox.Show("This word is protected and/or hidden!")
End If
End If
btnSkip_Click(btnSkip, EventArgs.Empty)
ShowHideSuggestions(pnlSuggestions.Visible)
End Sub
Private Sub StartChangeAll()
btnAdd.Enabled = False
ChangeingAll = True
If mt_ChangeAll IsNot Nothing AndAlso mt_ChangeAll.IsAlive Then
mt_ChangeAll.Abort()
End If
Dim SelectedWord = Me.SelectedWord
NewTextForChangeAll = txtChangeTo.Text
OldTextForChangeAll = SelectedWord.OrigWord
If SelectedWord IsNot Nothing Then
SelectedWord.Selected = False
End If
ShowHideSuggestions(False)
btnChangeAll.Enabled = False
mt_ChangeAll = New System.Threading.Thread(AddressOf ChangeAll)
mt_ChangeAll.Name = "Spell Check - Change all"
mt_ChangeAll.IsBackground = True
mt_ChangeAll.Start()
End Sub
Private Sub ChangeAll()
Dim WordErrors = _
(From xItem In HtmlSpellCheck1.Words _
Where String.Compare(xItem.OrigWord, OldTextForChangeAll, True) = 0 _
AndAlso (xItem.SpellCheckState = _
HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case _
OrElse xItem.SpellCheckState = _
HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error))
For Each item In WordErrors.ToArray
If item.CanChangeWord Then
Dim Suggestions = GetSuggestions(item.NewWord)
If Suggestions.Count > 0 Then
If String.IsNullOrEmpty(NewTextForChangeAll) Then
item.NewWord = Suggestions.First
Else
item.NewWord = NewTextForChangeAll
End If
item.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK
If SelectedWord() Is item Then
HtmlSpellCheck1_SelectionChanged(HtmlSpellCheck1, _
New HTMLSpellCheck.HTMLWordEventArgs() With {.Word = item})
End If
End If
Else
Beep()
End If
Next
btnRevertAll.SafeInvoke(Function(x As Button) InlineAssignHelper(x.Enabled, True))
If HtmlSpellCheck1.mt_SpellCheck IsNot Nothing _
AndAlso HtmlSpellCheck1.mt_SpellCheck.IsAlive Then
Else
Dim btnChangeAllEnabled = _
(From xItem In HtmlSpellCheck1.Words _
Where (xItem.SpellCheckState = _
HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case _
OrElse xItem.SpellCheckState = _
HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error)).Count > 0
ChangeingAll = False
If btnChangeAllEnabled = True Then
btnChangeAll.SafeInvoke(Function(x As Button) _
InlineAssignHelper(x.Enabled, btnChangeAllEnabled))
btnSkip_Click(btnSkip, EventArgs.Empty)
Else
CompleteSpellCheck()
End If
End If
End Sub
Private Sub AddWordButton(ByVal CaseSensitive As Boolean)
Dim theSelectedWord = SelectedWord()
If theSelectedWord IsNot Nothing Then
Dim WordCanBeChanged As Boolean = theSelectedWord.CanChangeWord
Dim Word As String = txtChangeTo.Text
If ChangeToChanged = False Then
Word = theSelectedWord.NewWord
Else
theSelectedWord.NewWord = Word
End If
Try
If SpellCheckControlBase.CurrentDictionary.SpellCheckWord(Word) <> i00SpellCheck.Dictionary.SpellCheckWordError.OK Then
If CaseSensitive = False Then
Word = LCase(Word)
End If
AddWordToDict(Word, theSelectedWord)
End If
Finally
If Not WordCanBeChanged Then
theSelectedWord.NewWord = theSelectedWord.OrigWord
theSelectedWord.Changed = False
End If
End Try
End If
btnSkip_Click(btnSkip, EventArgs.Empty)
End Sub
Private Sub cmsHTMLSpellCheck_Opening(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles cmsHTMLSpellCheck.Opening
If OpenedByClick Then
Dim pCursor = Cursor.Position
Dim pHtml = HtmlSpellCheck1.PointToScreen(New Point(0, 0))
pCursor.X = pCursor.X - pHtml.X
pCursor.Y = pCursor.Y - pHtml.Y
Dim element = HtmlSpellCheck1.Document.GetElementFromPoint(pCursor)
If element IsNot Nothing AndAlso element.Id <> "" Then
Dim ElementNo As Integer
If IsNumeric(Integer.TryParse(element.Id, ElementNo)) AndAlso HtmlSpellCheck1.Words.Count > ElementNo Then
MenuCurrentWord = HtmlSpellCheck1.Words.Item(ElementNo)
tstbChangeTo.Enabled = _
MenuCurrentWord IsNot Nothing AndAlso EnablingWordChange(MenuCurrentWord)
UpdateMenuItems()
Else
e.Cancel = True
End If
Else
e.Cancel = True
End If
Else
OpenedByClick = True
End If
End Sub
Finally, go to file Misc.vb, and change the ShowDialog procedure as follows:
#Region "Show spellcheck dialog"
Public Sub ShowDialog(Optional ByVal SpellCheckHiddenText As Boolean = False) Implements iSpellCheckDialog.ShowDialog
If CurrentDictionary IsNot Nothing AndAlso CurrentDictionary.Loading = False Then
Using SpellCheckDialog As New SpellCheckDialog
Dim SpellCheckResults = _
SpellCheckDialog.ShowDialog(parentTextBox, Me, _
parentTextBox.ExtendedText, SpellCheckHiddenText)
DrawSpellingErrors = False
extTextBoxCommon.LockWindowUpdate(Control.Handle)
Control.SuspendLayout()
Dim SelStart = parentTextBox.SelectionStart
Dim SellLength = parentTextBox.FullSelectionLength
If parentRichTextBox IsNot Nothing Then
Dim Offset As Integer = 0
For Each word In (From xItem In SpellCheckResults Where xItem.Changed = True AndAlso xItem.NewWord <> xItem.OrigWord).ToArray.Reverse
parentRichTextBox.Select(word.StartIndex - Offset, word.OrigWord.Length)
parentRichTextBox.SelectedText = word.NewWord
Next
CType(parentRichTextBox, TextBoxBase).ClearUndo()
Else
Dim OldVertPos = extTextBoxCommon.GetScrollBarLocation(parentTextBox)
Dim NewText As String = parentTextBox.ExtendedText
For Each word In (From xItem In SpellCheckResults Where xItem.Changed = True AndAlso xItem.NewWord <> xItem.OrigWord).ToArray.Reverse
NewText = Strings.Left(NewText, word.StartIndex) & _
word.NewWord & _
Strings.Right(NewText, Len(NewText) - (word.StartIndex + word.OrigWord.Length))
Next
parentTextBox.Text = NewText
extTextBoxCommon.SetScrollBarLocation(parentTextBox, OldVertPos)
End If
parentTextBox.SelectionStart = SelStart
parentTextBox.SelectionLength = SellLength
DrawSpellingErrors = True
Control.ResumeLayout()
extTextBoxCommon.LockWindowUpdate(IntPtr.Zero)
End Using
End If
End Sub
#End Region
Notice that when one invokes ShowDialog(True), the spell-checker spell-checks hidden (invisible) text, but not protected text. If one invokes ShowDialog(False) or simply ShowDialog(), then neither hidden nor protected text is spell-checked.
|
|
|
|
|
EVEN MORE RECOMMENDED CHANGES:
One thing I find irritating is that when this spell-checker is in force, pressing F3 always does a case-cycling on the selected word. In order to be able to turn on and off this feature--say, if F3 is being used for another purpose (like for a menu option)--do the following:
In the i00SpellCheck\ControlExtension\Extension.vb file, add the following code:
Friend ChangeCaseWithF3 As Boolean = True
<System.Runtime.CompilerServices.Extension()> _
Public Sub SetF3CasingOption(ByVal sender As Control, _
Optional ByVal ChangeCase As Boolean = True)
ChangeCaseWithF3 = ChangeCase
End Sub
Now, in the extTextBoxChangeCase.vb file, make the following change:
Private Sub parentTextBox_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles mc_TextBox.KeyUp
Select Case e.KeyCode
Case Keys.F3
If ChangeCaseWithF3 Then
CycleCase()
End If
End Select
End Sub
To enable using F3 to cycle through case, call textbox.SetF3CasingOption(True) (True is the default value) before enabling spell-check--say, right where you would call IncludeOrExcludeStandardMenu. To disable the feature, call textBox.SetF3CasingOption(False) instead; this latter version will free up F3 to be used for another purpose.
==================================
Also, in the file i00SpellCheck\SpellCheck\Engine\Extension.vb, you should make the following change in order to ensure that GetTextType always catches when a selected word in a RichTextBox contains text that is hidden and semi-protected:
<System.Runtime.CompilerServices.Extension()> _
Public Function GetTextType(TextBox As TextBoxBase, _
ByVal StartIndex As Integer, ByVal WordLength As Integer) As TypeOfText
Dim TextType As TypeOfText = TypeOfText.Normal
If TypeOf TextBox Is RichTextBox Then
Dim RichTextBox As RichTextBox = DirectCast(TextBox, RichTextBox)
With RichTextBox
Dim ScrollPosition As Point = .GetScrollPosition()
Dim CurrentStart As Integer = .SelectionStart, _
CurrentLength As Integer = .FullSelectionLength
.Select(StartIndex, WordLength)
If .SelectionProtected _
OrElse ProtectedTextChecker.IsMatch(.SelectedRtf) Then
TextType = TextType Or TypeOfText.Protected
End If
If HiddenTextChecker.IsMatch(.SelectedRtf) Then
TextType = TextType Or TypeOfText.Hidden
ElseIf .SelectionLength < WordLength _
AndAlso Not TextType.HasFlag(TypeOfText.Protected) Then
TextType = TextType Or TypeOfText.Hidden
For position As Integer = StartIndex To StartIndex + WordLength - 1
.Select(position, 1)
If .SelectionProtected Then
TextType = TextType Or TypeOfText.Protected : Exit For
End If
Next position
End If
.Select(CurrentStart, CurrentLength) : .SetScrollPosition(ScrollPosition)
End With
End If
Return TextType
End Function
|
|
|
|
|
The best way to make a self-contained incorporation of i00SpellCheck functionality without the need for the host project to reference it as either a solution project or an exe file (not a big issue when the host project is a program proper, but something of an issue when the host project is a custom control or other class library--in which case the ultimate host project using that library will otherwise have to reference both the library and i00SpellCheck) is to take approach #3, and bring all of the *.vb files from the "Spell Check" folder directly into the project.
The problem is, the VB environment doesn't provide a way to add an entire existing folder into a project just like that. One can only use the file dialog to add actual *.vb files, not folders containing them, and the *.vb files are scattered over numerous subfolders and sub-subfolders (of many levels). Also, it's not clear that the "Spell Check" folder alone has all the files needed for the proper functioning provided by the i00SpellCheck project (not to be confused with its larger solution). (For instance, the context-menu setup logic is in a *.vb file outside the "Spell Check" folder, although it's in the overall project.)
Is there any way to simplify the direct-*.vb-file approach, one that doesn't require traversing the entire directory tree of "Spell Check" to get the *.vb files?
modified 18-Nov-19 7:51am.
|
|
|
|
|
The best way to bring the *.vb files directly into a project is to first add the i00SpellCheck project to the project's solution, then, in the Solution Explorer, highlight all the files and folders from the i00SpellCheck project, copy and paste them into the main project, highlight/copy/paste (once again, from the Solution Explorer) all the references of the i00SpellCheck project into the main project, then remove the i00SpellCheck project from the solution.
Before doing these steps, you might want to, using Windows Explorer, copy i00SpellCheck.vbproj file and the i00SpellCheck subfolder that's in the same main folder into the main folder of your own project, then add the i00SpellCheck project from there, so that the spell-check support is self-contained within your project's folder.
Also note that, in any case, you must copy the entire set of *.vb files and folders of the i00SpellCheck project--not just the contents of the "Spell Check" folder--into your project (!!); you do not need to copy the contents of the entire "SpellCheck" solution with all of its other projects.
One side benefit to copying the support files/folders of the i00SpellCheck project directly into host projects--besides not having to reference the project/.exe and Import its functionality--is that one can then have the spell-check code customized to the nuances of each individual host project. (That's allowed, i00 people, provided use-credit is given on this page, right?)
modified 30-Nov-19 2:07am.
|
|
|
|
|
That's allowed, i00 people, provided use-credit is given on this page, right?
Yep ... I have had some people complain about giving credit here ... but I am entitled to my own licence agreement ... and although it may seem strange I think that it is:
A. Better than having to give credit in the application itself (although if you want to advertise i00 Spell Check in your project that would be great )
B. Giving me the feedback that you are using it makes me more motivated to develop it further... it had been a little quiet on here when I last checked and the extra recent enthusiasm means that I am going to revisit this and release an update in the next week or so!
Kris
|
|
|
|
|
I am using i00 spell check in my project
|
|
|
|
|
I am using i00 spell check in my project
|
|
|
|
|
If a custom control (UserControl or extended/inherited control) invokes EnableControlExtensions() or other related extension methods before its ultimate container form (Me.FindForm) exists, then the wrinkly lines will be displayed not under the misspelled text over the form window, but instead over the Visual Studio environment window or even the desktop! Therefore, you must check to see if the form exists yet (Me.FindForm IsNot Nothing) before invoking such methods. Add this code, and call the SetUpSpellCheck() method below inside the procedures of any key event of the control where Me.FindForm might first come into existence--particularly HandleCreated, ControlAdded, Enter, VisibleChanged, and GotFocus:
Private ControlExtensionsEnabled As Boolean
Private Sub SetUpSpellCheck(ByVal SpellCheckOn As Boolean, _
Optional ByVal TextControl As Control = Nothing)
If TextControl Is Nothing Then
TextControl = Me 'if not for a constituent control
End If
If Me.FindForm IsNot Nothing Then
' set things up
If Not ControlExtensionsEnabled Then
Me.EnableControlExtensions() 'set up extensions for this particular control
ControlExtensionsEnabled = True
If Not SpellCheckOn Then
' make sure spell-check dialog actually displays when invoked first time
TextControl.EnableSpellCheck()
End If
End If
' set spell-check status
If SpellCheckOn Then
If Not TextControl.IsSpellCheckEnabled Then
TextControl.EnableSpellCheck()
End If
Else
If TextControl.IsSpellCheckEnabled Then
TextControl.DisableSpellCheck()
End If
End If
End If
Also include the follow code inside the procedure of the control's Disposed event (This is important!):
' use Me instead of Textcontrol if not working with a constituent control
If TextControl.IsSpellCheckEnabled() Then
TextControl.DisableSpellCheck()
End If
If you use my recommended additional method Me.IncludeOrExcludeStandardMenu (see earlier post, "Some issues about text-changing", including recommended modifications, regarding this tool), then call it before calling any of the other extension methods--for instance, inside the control's New constructor procedure, right after Initializecomponent:
Me.IncludeOrExcludeStandardMenu(TrueOrFalse) 'use Me, not Me.FindForm!
modified 27-Oct-19 1:36am.
|
|
|
|
|
The most important events to use to watch for the container form's existence inside a custom control's code, when using i00SpellCheck in it are Load, HandleCreated, HandleDestroyed, and Disposed--particularly Load and Disposed.
Private Sub MyControl_Load(sender As Object, e As EventArgs) Handles Me.Load
If Me.FindForm IsNot Nothing AndAlso Me.FindForm.IsHandleCreated Then
' form present
DoThisOnLoad()
Else
' form not present
DoThatOnLoad()
End If
End Sub
Private Sub MyControl_HandleCreated(sender As Object, e As EventArgs) Handles Me.HandleCreated
If Me.FindForm IsNot Nothing AndAlso Me.FindForm.IsHandleCreated Then
' form present
DoThisOnHandleCreated()
Else
' form not present
DoThatOnHandleCreated()
End If
End Sub
Private Sub MyControl_HandleDestroyed(sender As Object, e As EventArgs) Handles Me.HandleDestroyed
If Me.FindForm IsNot Nothing AndAlso Me.FindForm.IsHandleCreated Then
' form present
DoThisOnHandleDestroyed()
Else
' form not present
DoThatOnHandleDestroyed()
End If
End Sub
Private Sub MyControl_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
If Me.FindForm IsNot Nothing AndAlso Me.FindForm.IsHandleCreated Then
' form present
DoThisOnDisposed()
Else
' form not present
DoThatOnDisposed()
End If
End Sub
|
|
|
|
|
The version of i00 Spell Check I'm using in my project is built from only the spell-checker's main project, and incorporates a fix for a small bug in regards to the "Change All" option of the spell-check dialog. Hopefully, this is not a problem for you.
|
|
|
|
|
RECOMMENDED CHANGES:
Here are some modifications that I'm recommending for the i00SpellCheck project inside the SpellCheck solution:
First, not a change to the project itself, but a useful tip: The spell-checker checks the entire text box or rich text box. There's no way to restrict the spell-check to a highlighted (selected) region. To do so, you need to create a second text-box/rich-text-box--say, one unattached to any form--transfer the selected text of the first text box into it (using SelectedText/SelectedRtf of the first text-box/rich-text-box and Text/Rtf of the second), spell-check on the second one, then paste the result back into the first text-box. For instance:
Dim rtb As RichTextBox = New RichTextBox()
rtb.SelectedRtf = RichTextBox1.SelectedRtf
Dim iSpellCheckDialog = _
TryCast(rtb.SpellCheck, i00SpellCheck.SpellCheckControlBase.iSpellCheckDialog)
If iSpellCheckDialog IsNot Nothing Then
iSpellCheckDialog.ShowDialog()
RichTextBox1.SelectionProtected = False
rtb.SelectAll() : RichTextBox1.SelectedRtf = rtb.SelectedRtf
End If
Now for the changes:
1. With selection "Change All" in the spell-check dialog, all subsequent unrecognized words are replaced, wily-nilly, with the first item in each word's suggestion list, even if arbitrary replacement text or another list entry is specified in "Change To:", even if only all occurrences of 1 word is desired for change. To make it so that subsequent occurrences only of the current misspelled word are replaced, and with the contents of the txtChangeTo text (if it isn't empty), I suggest making the following changes to the following changes to SpellCheckDialog.vb:
Dim NewTextForChangeAll, OldTextForChangeAll As String
Private Sub StartChangeAll()
btnAdd.Enabled = False
ChangeingAll = True
If mt_ChangeAll IsNot Nothing AndAlso mt_ChangeAll.IsAlive Then
mt_ChangeAll.Abort()
End If
Dim SelectedWord = Me.SelectedWord
NewTextForChangeAll = txtChangeTo.Text
OldTextForChangeAll = SelectedWord.OrigWord
If SelectedWord IsNot Nothing Then
SelectedWord.Selected = False
End If
ShowHideSuggestions(False)
btnChangeAll.Enabled = False
mt_ChangeAll = New System.Threading.Thread(AddressOf ChangeAll)
mt_ChangeAll.Name = "Spell Check - Change all"
mt_ChangeAll.IsBackground = True
mt_ChangeAll.Start()
End Sub
Private Sub ChangeAll()
Dim WordErrors = (From xItem In HtmlSpellCheck1.Words Where (xItem.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case OrElse xItem.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error))
For Each item In WordErrors.ToArray
If String.Compare(item.NewWord, OldTextForChangeAll, True) = 0 Then
Dim Suggestions = GetSuggestions(item.NewWord)
If Suggestions.Count > 0 Then
If String.IsNullOrEmpty(NewTextForChangeAll) Then
item.NewWord = Suggestions.First
Else
item.NewWord = NewTextForChangeAll
End If
item.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK
If SelectedWord() Is item Then
HtmlSpellCheck1_SelectionChanged(HtmlSpellCheck1, New HTMLSpellCheck.HTMLWordEventArgs() With {.Word = item})
End If
End If
End If
Next
btnRevertAll.SafeInvoke(Function(x As Button) InlineAssignHelper(x.Enabled, True))
If HtmlSpellCheck1.mt_SpellCheck IsNot Nothing AndAlso HtmlSpellCheck1.mt_SpellCheck.IsAlive Then
Else
Dim btnChangeAllEnabled = (From xItem In HtmlSpellCheck1.Words Where (xItem.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case OrElse xItem.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error)).Count > 0
ChangeingAll = False
If btnChangeAllEnabled = True Then
btnChangeAll.SafeInvoke(Function(x As Button) InlineAssignHelper(x.Enabled, btnChangeAllEnabled))
btnSkip_Click(btnSkip, EventArgs.Empty)
Else
CompleteSpellCheck()
End If
End If
End Sub
2. To allow the programmer to keep the standard menu from being automatically added to a text-box's/rich-text-box's context menu, add the following to Extension.vb:
Friend IncludeStandardItems As Boolean = True
<System.Runtime.CompilerServices.Extension()> _
Public Sub IncludeOrExcludeStandardMenu(ByVal sender As Control, _
Optional ByVal Include As Boolean = True)
IncludeStandardItems = Include
End Sub
Then change extTextBoxContextMenu.vb as follows:
Public Sub AddStandardItems(ByVal ContextMenuStrip As ContextMenuStrip)
If Not IncludeStandardItems Then
Exit Sub
End If
RaiseEvent PreAddingMenuItems(Me, EventArgs.Empty)
If ContextMenuStrip.Items.Count > 0 Then
ContextMenuStrip.Items.Add(New StandardToolStripSeparator)
End If
Undo = New StandardToolStripMenuItem("&Undo", My.Resources.Undo)
ContextMenuStrip.Items.Add(Undo)
Dim RichTextBox = TryCast(TextBox, RichTextBox)
If RichTextBox IsNot Nothing Then
Redo = New StandardToolStripMenuItem("&Redo", My.Resources.Redo)
ContextMenuStrip.Items.Add(Redo)
Redo.Enabled = RichTextBox.CanRedo
End If
ContextMenuStrip.Items.Add(New StandardToolStripSeparator)
If System.Threading.Thread.CurrentThread.GetApartmentState = Threading.ApartmentState.STA Then
Cut = New StandardToolStripMenuItem("Cu&t", My.Resources.Cut)
ContextMenuStrip.Items.Add(Cut)
Copy = New StandardToolStripMenuItem("&Copy", My.Resources.Copy)
ContextMenuStrip.Items.Add(Copy)
Paste = New StandardToolStripMenuItem("&Paste", My.Resources.Paste)
ContextMenuStrip.Items.Add(Paste)
Cut.Enabled = TextBox.SelectionLength > 0
Copy.Enabled = TextBox.SelectionLength > 0
Paste.Enabled = Clipboard.GetText <> ""
End If
Delete = New StandardToolStripMenuItem("&Delete", My.Resources.Delete)
ContextMenuStrip.Items.Add(Delete)
ContextMenuStrip.Items.Add(New StandardToolStripSeparator)
SelectAll = New StandardToolStripMenuItem("Select &All", My.Resources.SelectAll)
ContextMenuStrip.Items.Add(SelectAll)
Undo.Enabled = TextBox.CanUndo
Delete.Enabled = TextBox.SelectionLength > 0
SelectAll.Enabled = TextBox.SelectionLength <> Len(TextBox.Text)
RaiseEvent PostAddingMenuItems(Me, EventArgs.Empty)
End Sub
Now, calling Me.IncludeOrExcludeStandardMenu(False) before Me.EnableControlExtensions() will suppress the auto-addition of the standard menu. (The default parameter value for the new procedure is True to allow auto-addition.)
3. The spell check won't recognize valid words which are broken by hidden (syllable) hyphens (ChrW(173)). To rectify that, I would make the following changes:
First, add the following to iDictionary.vb:
Public Shared Function RemoveHyphens(ByVal Word As String) As String
Return Word.Replace(ChrW(173), "")
End Function
Then add the following statement
Word = Dictionary.RemoveHyphens(Word)
in the following places:
a. in front of the 1st statement of the following procedures: SpellCheckWord and SpellCheckSuggestions (both in iDictionary.vb); AddItems (in Menu.vb); DictionaryIgnoreWord, DictionaryUnIgnoreWord, DictionaryAddWord, and DictionaryRemoveWord (all in SpellCheckControlBase.vb); and, finally,
b. in the following place in SpellCheckDialog.vb:
Private Sub btnAdd_ClickDropdown(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAdd.ClickDropdown
Dim theSelectedWord = SelectedWord()
If theSelectedWord IsNot Nothing Then
Dim Word As String = txtChangeTo.Text
If ChangeToChanged = False Then
Word = theSelectedWord.NewWord
End If
Word = Dictionary.RemoveHyphens(Word)
tsiAddCaseSensitive.Text = "Case sensitive: " & Word
tsiAddCaseInsensitive.Text = "Case insensitive: " & LCase(Word)
tsiAddCaseSensitive.Visible = Word <> LCase(Word)
cmsAdd.Show(btnAdd, New Point(0, btnAdd.Height))
End If
End Sub
4. Also, you might want to set StartPosition for the SpellCheckDialog form to CenterScreen.
5. I also recommend the following changes in order to ensure that i00SpellCheck's recognition of numbers and works breaks is more similar to (although not identical to) Microsoft Word's. First make the following changes in the "Word Breaks" and "For words with 's" regions of Formatting.vb--changing RemoveWordBreaks and RemoveApoS as follows (and removing the WordBreakChrs array and associated "escaping" procedure):
#Region "Word Breaks"
Private Const FastOption As RegexOptions = _
RegexOptions.Compiled Or RegexOptions.ExplicitCapture Or RegexOptions.IgnoreCase
Private Const HyphenChar As String = ChrW(173), _
SingleQuoteChars As String = "'`" & ChrW(1370) & ChrW(1371) & ChrW(1372) _
& ChrW(8216) & ChrW(8217) & ChrW(8219) & ChrW(8242) _
& ChrW(11788) & ChrW(11789) & ChrW(65040) & ChrW(65287), _
SingleQuoteTest As String = "[" & SingleQuoteChars & "]", _
AmpersandChars As String = "&﹠&&", _
AmpersandTest As String = "[" & AmpersandChars & "]", _
NumberSignChars As String = "#﹟#♯", _
NumberSignTest As String = "[" & NumberSignChars & "]", _
PercentPermilleChars As String = "%‰﹪‱%٪؉؊", _
PercentPermilleTest As String = "[" & PercentPermilleChars & "]"
Private Const ApoSTest As String = SingleQuoteTest & "s(?![\p{L}\d])"
Private Const WordBreakCharTest As String = "(?<![\p{L}\d])'|'(?!\p{L})" _
& "|(?<![\p{L}\d\p{Sc}])&(?![\p{L}\d\p{Sc}])" _
& "|(?<![\p{L}\d])[#™℠]|[#™℠](?![\p{L}\d])" _
& "|(?<![\p{Sc}%\p{L}\d])[\p{Sc}%](?![\p{Sc}%\p{L}\d])" _
& "|(?<![\p{L}\d])[©®℗]|[\s\r\n\t\v\f]" _
& "|(?<!(^|[^\p{L}\d])\d+E)[+\-]|[+\-](?!\d+($|[^\p{L}\d]))" _
& "|[\p{P}\p{Sm}\^~≈-[" & HyphenChar & "'&#™℠\p{Sc}%©®℗+\-]]"
Private Shared ReadOnly reReplaceWordBreakChars As Regex = _
New Regex(WordBreakCharTest, FastOption), _
reReplaceSingleQuotes As Regex = New Regex(SingleQuoteTest, FastOption), _
reReplaceAmpersands As Regex = New Regex(AmpersandTest, FastOption), _
reReplaceNumberSigns As Regex = New Regex(NumberSignTest, FastOption), _
reReplacePercentPermille As Regex = New Regex(PercentPermilleTest, FastOption), _
reRemoveTrailingApoS As Regex = New Regex(ApoSTest, FastOption)
Public Shared Function RemoveWordBreaks(ByVal Text As String) As String
If Text <> "" Then
Text = reReplaceSingleQuotes.Replace(Text, "'")
Text = reReplaceAmpersands.Replace(Text, "&")
Text = reReplaceNumberSigns.Replace(Text, "#")
Text = reReplacePercentPermille.Replace(Text, "%")
Text = reReplaceWordBreakChars.Replace(Text, " ")
End If
Return Text
End Function
#End Region
#Region "For words with 's"
Friend Shared Function RemoveApoS(ByVal text As String) As String
Return _
reRemoveTrailingApoS.Replace(text, "")
End Function
#End Region
Then change the code in the SpellCheckWordNonUser procedure of FlatFileSpellCheck.vb as follows:
#Region "Check that Word is in the Dictionary"
Private Const FastOption As RegexOptions = _
RegexOptions.Compiled Or RegexOptions.ExplicitCapture Or RegexOptions.IgnoreCase
Private Const PercentPermilleChars As String = "%‰﹪‱%٪؉؊", _
PercentPermilleTest As String = "[" & PercentPermilleChars & "]", _
SpecialChars As String = "\p{Sc}" & PercentPermilleChars, _
SpecialCharsTest As String = "[" & SpecialChars & "]", _
NumericText As String = "[^" & SpecialChars & "]+", _
ValidNumberTest As String = "^(" & NumericText & "|\p{Sc}?" & NumericText & "\p{Sc}?" _
& "|" & NumericText & PercentPermilleTest & "?)$"
Private ReadOnly reCheckValidNumber As Regex = New Regex(ValidNumberTest, FastOption), _
reRemoveSpecialChars As Regex = New Regex(SpecialCharsTest, FastOption)
Public Overrides Function SpellCheckWordNonUser(ByVal Word As String) _
As Dictionary.SpellCheckWordError
If Word = "" Then
Return SpellCheckWordError.OK
End If
Dim theWord = Word
Dim OldWord = theWord
theWord = Dictionary.Formatting.RemoveApoS(theWord)
Dim NumericWord = theWord
If reCheckValidNumber.IsMatch(NumericWord) Then
NumericWord = reRemoveSpecialChars.Replace(NumericWord, "")
If IsNumeric(NumericWord) Then
Return SpellCheckWordError.OK
End If
End If
Dim DicWords = IndexedDictionary.Item(Word)
If DicWords Is Nothing Then
Return SpellCheckWordError.SpellError
End If
DicWords = (From xItem In DicWords Where LCase(xItem) = LCase(theWord)).ToList
If DicWords.Count = 0 Then
Return SpellCheckWordError.SpellError
End If
For Each iDicWord In DicWords
Dim WordCaseOK = Formatting.CaseOK(Word, iDicWord)
If WordCaseOK Then
Return SpellCheckWordError.OK
End If
Next
Return SpellCheckWordError.CaseError
End Function
#End Region
6.There are also issues with RichTextBox's which contain hidden text (text marked as "invisible" using RTF code "\v text\v0") whenever the platform of the host project of this DLL is .NET Framework 4.7 or higher: The Text, SelectedText, and SelectionLength properties return different values than they do for earlier .NET platforms, ignoring the invisible text. (There are also issues with proper function of methods that convert between point-on-control and character-index when the end of a document isn't padded with enough "visible" but undisplayed characters--like optional "syllable" hyphens"--to compensate for the earlier marked-as-invisible text. Weird!) In order to ensure that the spell-check dialog properly locates and (when asked) replaces misspelled text in a rich-text box containing invisible text, regardless of target platform, do the following:
Add the following extension methods to the Extension.vb file:
#Region "Extended Text Reading"
<System.Runtime.CompilerServices.Extension()> _
Public Function FullSelectionLength(TextBox As TextBoxBase) As Integer
If TypeOf TextBox IsNot RichTextBox Then
Return TextBox.SelectionLength
End If
Dim RichTextBox As RichTextBox = DirectCast(TextBox, RichTextBox)
Dim TypeRTB As Type = RichTextBox.GetType
Dim fi As FieldInfo = _
TypeRTB.GetField("curSelStart", _
BindingFlags.Instance Or BindingFlags.NonPublic)
Dim nCurSelStart As Integer = CType(fi.GetValue(RichTextBox), Integer)
fi = TypeRTB.GetField("curSelEnd", _
BindingFlags.Instance Or BindingFlags.NonPublic)
Dim nCurSelEnd As Integer = _
Math.Min(CType(fi.GetValue(RichTextBox), Integer), RichTextBox.TextLength)
If nCurSelStart = -1 AndAlso nCurSelEnd = -1 Then
Return 0
Else
Return _
nCurSelEnd - nCurSelStart
End If
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function ExtendedText(TextBox As TextBoxBase, _
Optional SelectionOnly As Boolean = False) As String
If TypeOf TextBox IsNot RichTextBox Then
If SelectionOnly Then
Return TextBox.SelectedText
Else
Return TextBox.Text
End If
End If
Dim RichTextBox As RichTextBox = DirectCast(TextBox, RichTextBox)
Dim TypeRTB As Type = RichTextBox.GetType
Dim prop As PropertyInfo = _
TypeRTB.GetProperty("WindowText", _
BindingFlags.Instance Or BindingFlags.NonPublic)
Dim Text As String = _
CType(prop.GetValue(RichTextBox, {}), String).Replace(ControlChars.Cr, "")
If SelectionOnly Then
Dim fi As FieldInfo = _
TypeRTB.GetField("curSelStart", _
BindingFlags.Instance Or BindingFlags.NonPublic)
Dim nCurSelStart As Integer = CType(fi.GetValue(RichTextBox), Integer)
fi = TypeRTB.GetField("curSelEnd", _
BindingFlags.Instance Or BindingFlags.NonPublic)
Dim nCurSelEnd As Integer = _
Math.Min(CType(fi.GetValue(RichTextBox), Integer), Text.Length)
If (nCurSelStart = -1 AndAlso nCurSelEnd = -1) _
OrElse nCurSelStart = nCurSelEnd Then
Text = ""
Else
Text = Text.Substring(nCurSelStart, nCurSelEnd - nCurSelStart)
End If
End If
Return Text
End Function
#End Region
Then, in every other ".vb" code file in the "i00SpellCheck" project, replace all "reading" occurrences (those inside an expression) of Text, SelectedText, and SelectionLength properties that are attached to TextBoxBase-type variables--specifically, to TextBox and parentTextBox--with ExtendedText (aka. all text), ExtendedText(True) (aka. selected text), and FullSelectionLength, respectively. Do not replace occurrences of these properties that are "writing" (on the left-hand-side of an assignment statement) or attached to variables of other objects--particularly those that don't derive from TextBoxBase! These changes should affect the following files: clsRTBHighlight.vb, Drawing.vb, extTextBoxChangeCase.vb, extTextBoxCommon.vb, extTextBoxContextMenu.vb, Menu.vb, and Misc.vb.
(Note that these fixes don't necessarily guarantee that spell-check via squiggly lines and a context menu for a misspelled word will display and function correctly when there's invisible text. More changes will be needed to finesse that.)
7. You might also want to make the following change to SetUpControl within TextBox.vb, in order to ensure that i00SpellCheck words with controls that derive from TextBox and RichTextBox:
Region "Test Harness"
Public Function SetupControl(ByVal Control As System.Windows.Forms.Control) As Control Implements iTestHarness.SetupControl
If TypeOf Control Is TextBox Then
Dim TextBox = DirectCast(Control, TextBox)
TextBox.Font = New System.Drawing.Font("Microsoft Sans Serif", 12)
TextBox.Multiline = True
TextBox.ScrollBars = ScrollBars.Vertical
TextBox.AppendText(If(TextBox.Text = "", "", vbCrLf & vbCrLf) & "Ths is a standrd text field that uses a dictionary to spel check the its contents ... as you can se errors are underlnied in red!")
TextBox.SelectionStart = 0
TextBox.SelectionLength = 0
Return TextBox
ElseIf TypeOf Control Is RichTextBox Then
Dim RichTextBox = DirectCast(Control, RichTextBox)
RichTextBox.Font = New System.Drawing.Font("Microsoft Sans Serif", 15.75!)
Dim StartIndex = RichTextBox.TextLength
RichTextBox.AppendText(If(RichTextBox.Text = "", "", vbCrLf & vbCrLf) & "i00SpellCheck has built in support for RichTextBoxes!" & vbCrLf & _
"The quic brown fox junped ovr the lazy dog!" & vbCrLf & _
"You can right click to see spelling suggestions for words and to add/ignore/remove words from the dictionary." & vbCrLf & _
"If you ignre a word you can hold ctrl down to underlne all ignored words!" & vbCrLf & _
"The initial dictionary may take a little while to load ... it holds more than 150 000 words!")
Dim HighlightKeyWordFormat As New extTextBoxCommon.HighlightKeyWordFormat
HighlightKeyWordFormat.Color = Color.Red
extTextBoxCommon.HighlightKeyWord(RichTextBox, "Rich", HighlightKeyWordFormat, RichTextBoxFinds.None, StartIndex)
HighlightKeyWordFormat.Color = Color.Green
extTextBoxCommon.HighlightKeyWord(RichTextBox, "Text", HighlightKeyWordFormat, RichTextBoxFinds.None, StartIndex)
HighlightKeyWordFormat.Color = Color.Blue
extTextBoxCommon.HighlightKeyWord(RichTextBox, "Boxes", HighlightKeyWordFormat, RichTextBoxFinds.None, StartIndex)
HighlightKeyWordFormat.Color = Color.FromKnownColor(KnownColor.HotTrack)
extTextBoxCommon.HighlightKeyWord(RichTextBox, "i00SpellCheck", HighlightKeyWordFormat, RichTextBoxFinds.None, StartIndex)
HighlightKeyWordFormat.Color = Color.Empty
HighlightKeyWordFormat.Font = New Font(RichTextBox.Font.Name, CSng(RichTextBox.Font.Size * 1.5), FontStyle.Bold)
extTextBoxCommon.HighlightKeyWord(RichTextBox, "RichTextBoxes!", HighlightKeyWordFormat, RichTextBoxFinds.None, StartIndex)
RichTextBox.Select(0, 0)
RichTextBox.ClearUndo()
Return RichTextBox
Else
Return Nothing
End If
End Function
#End Region
8. Finally, here's a bug (!!) in the Drawing.vb source file which can cause an application to crash (!!) when the "character index" at the upper-left or lower-right of a TextBox/RichTextBox is -1. The error is in the CustomPaint procedure, and is corrected as follows:
#Region "Painting"
Private Sub CustomPaint()
If OKToDraw = False OrElse parentTextBox.ClientSize.Width = 0 Then Exit Sub
Dim TextHeight As Integer = System.Windows.Forms.TextRenderer.MeasureText("Ag", parentTextBox.Font).Height
Dim BufferWidth As Integer = System.Windows.Forms.TextRenderer.MeasureText("--", parentTextBox.Font).Width
'for drawing underlines below the textbox drawing bounds when on a single line text box
Dim bHeight = parentTextBox.ClientSize.Height
If DrawOverlayForm IsNot Nothing Then
bHeight = DrawOverlayForm.Height
End If
Using b As New Bitmap(parentTextBox.ClientSize.Width, bHeight)
Using g = Graphics.FromImage(b)
g.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
'Using sb As New SolidBrush(Color.FromArgb(127, Color.Blue))
' g.FillRectangle(sb, New RectangleF(0, 0, parentTextBox.Width, parentTextBox.Height))
'End Using
Dim FromChar = parentTextBox.GetCharIndexFromPosition(New Point(0, 0))
If FromChar < 0 Then
FromChar = 0
End If
Dim ToChar = parentTextBox.GetCharIndexFromPosition(New Point(parentTextBox.ClientRectangle.Width - 1, parentTextBox.ClientRectangle.Height - 1))
If ToChar < 0 Then
ToChar = 0
End If
Dim theText As String = Dictionary.Formatting.RemoveWordBreaks(parentTextBox.Text)
Dim LetterIndex = FromChar
Dim LeftSide = Left(theText, FromChar)
LeftSide = LeftSide.Split(" "c).Last
Dim RightSide = Right(theText, Len(theText) - ToChar)
RightSide = RightSide.Split(" "c).First
FromChar -= Len(LeftSide) : ToChar += Len(RightSide)
Dim VisibleText As String = Mid(theText, FromChar + 1, ToChar - FromChar)
'If parentTextBox.Multiline = False Then
'g.TranslateTransform(-System.Windows.Forms.TextRenderer.MeasureText(LeftSide, parentTextBox.Font).Width, -5)
'End If
Dim NewWords As New Dictionary(Of String, Dictionary.SpellCheckWordError)
If Trim(VisibleText) <> "" Then
Dim words = Replace(Replace(VisibleText, vbCr, " "), vbLf, " ").Split(" "c)
For iWord = LBound(words) To UBound(words)
If words(iWord) <> "" Then
Dim P1 = parentTextBox.GetPositionFromCharIndex(LetterIndex)
Dim P1OffsetPlus As Integer = 0
If iWord = 0 AndAlso P1.X >= parentTextBox.ClientSize.Width Then
P1OffsetPlus = 1
P1.X = 0
End If
If P1.Y < parentTextBox.Height Then
Dim WordState As Dictionary.SpellCheckWordError = Dictionary.SpellCheckWordError.SpellError
If dictCache.ContainsKey(words(iWord)) Then
'load from cache
WordState = dictCache(words(iWord))
Else
''item is not in the dict cache
If NewWords.ContainsKey(words(iWord)) = False Then
NewWords.Add(words(iWord), Dictionary.SpellCheckWordError.OK)
End If
WordState = Dictionary.SpellCheckWordError.OK
End If
If WordState = Dictionary.SpellCheckWordError.OK Then
Else
If WordState = Dictionary.SpellCheckWordError.Ignore Then
If DrawIgnored() = False Then GoTo ContinueFor
End If
Dim P2 = parentTextBox.GetPositionFromCharIndex(LetterIndex + Len(words(iWord)))
If LeftSide <> "" AndAlso iWord = 0 Then
Dim NormalStringWidth = g.MeasureString(Mid(words(iWord), Len(LeftSide) + 1 + P1OffsetPlus), parentTextBox.Font).Width
Dim XOffsetDiff = g.MeasureString("-" & Mid(words(iWord), Len(LeftSide) + 1 + P1OffsetPlus) & "-", parentTextBox.Font).Width - NormalStringWidth
P2.X = CInt(parentTextBox.GetPositionFromCharIndex(LetterIndex + P1OffsetPlus).X + (NormalStringWidth - XOffsetDiff))
End If
If P2.X = 0 Then
'we are the last char ... :(
P2 = parentTextBox.GetPositionFromCharIndex(LetterIndex + Len(words(iWord)) - 1)
P2.X += System.Windows.Forms.TextRenderer.MeasureText("-" & Right(words(iWord), 1) & "-", parentTextBox.Font).Width - BufferWidth
End If
Dim LineHeight As Integer = extTextBoxCommon.GetLineHeightFromCharPosition(parentTextBox, LetterIndex, RTBContents)
'P1.Y += LineHeight
P2.Y = P1.Y + LineHeight
'P2.Y = P1.Y
Dim e = New SpellCheckCustomPaintEventArgs With {.Graphics = g, .Word = words(iWord), .Bounds = New Rectangle(P1.X, P1.Y, P2.X - P1.X, P2.Y - P1.Y), .WordState = WordState}
OnSpellCheckErrorPaint(e)
If e.DrawDefault Then
Select Case WordState
Case Dictionary.SpellCheckWordError.Ignore
Using p As New Pen(Settings.IgnoreColor)
g.DrawLine(p, P1.X, P2.Y + 1, P2.X, P2.Y + 1)
End Using
Case Dictionary.SpellCheckWordError.CaseError
DrawingFunctions.DrawWave(g, P1, P2, Settings.CaseMistakeColor)
Case Dictionary.SpellCheckWordError.SpellError
DrawingFunctions.DrawWave(g, P1, P2, Settings.MistakeColor)
End Select
End If
End If
End If
End If
ContinueFor:
If LeftSide <> "" AndAlso iWord = 0 Then
LetterIndex -= Len(LeftSide)
End If
LetterIndex += 1 + Len(words(iWord))
Next
End If
Draw:
If DrawOverlayForm IsNot Nothing Then
DrawOverlayForm.SetBitmap(b, 255)
Else
Dim textBoxGraphics = Graphics.FromHwnd(parentTextBox.Handle)
textBoxGraphics.DrawImageUnscaled(b, 0, 0)
End If
If NewWords.Count > 0 Then
AddWordsToCache(NewWords)
End If
End Using
End Using
End Sub
#End Region
Now re-build the i00SpellCheck project.
PS. You might also want to create a separate, extra solution with only that project. Simply load in the SpellCheck.sln solution, exclude the 11 projects other than i00SpellCheck and save a new solution with a different name, say i00SpellCheck.sln. (Be sure when opening the project alone, though, to click on the new i00SpellCheck.sln file because clicking on the i00SpellCheck.vbproj file will open the entire SpellCheck.sln solution with all 12 projects!)
PS. As a fellow VB programmer--keep the faith!
modified 17-Oct-21 22:24pm.
|
|
|
|
|
Another deficiency I've noticed with i00SpellCheck is its inability to properly handle text within a rich-text box that is protected or hidden (invisible). Fortunately, I've figured out a fix for that--which, unfortunately, requires making strategic changes in several code files. First, I'll explain the change in functionality that my modifications will do:
1. It causes the SpellCheckDialog to skip over any and all cases of a word whose text protected or semi-protected, and, unless otherwise directed, any and all cases of a word whose text is hidden or semi-hidden.
2. It prevents context-menu options from being added when the user right-clicks on a word in a rich-text box which is protected/semi-protected.
3. It still draws a wavy line, when that feature is enabled, under "misspelled" words that are wholly or partially protected/hidden. One can use the extension methods below to disable that line-drawing in such cases, but I've found that that slows down the rendering of rich text. If you want to keep wavy lines from appearing under protected/hidden text, you should modify the Drawing.vb file's CustomPaint procedure to invoke parentTextBox.SetRedraw(False) and Dim ScrollPosition As Point = parentTextBox.GetScrollPosition() before it goes through the text, check the value of parentTextBox.GetTextType(LetterIndex, words(iWord).Length) as well as (the already-checked-for) e.Default before drawing a line, then invoke parentTextBox.SetScrollPosition(ScrollPosition) and parentTextBox.SetRedraw(True) after the text is done being parsed. (Better yet, use a Try block with after redrawing is turned off and the scroll position is saved, with the resetting of these attributes occurring the Finally clause in order to ensure that the text box can redraw and is in the correct scroll position after CustomPaint finishes even if an exception occurs during the word-parsing.)
First of changes:
In the SpellCheck\Engine\Extension.vb file, create a new module, named ExtendedTextReading, and move the ExtendedText and FullSelectionLength extension methods thereto. Also, add the SetRedraw, GetScrollPosition, SetScrollPosition, HasFlag, (only included because this method doesn't come automatically with pre-4.0 versions of .NET!), and GetTextType extension methods, and the various Private components--as follows:
#Region "Extended Text Reading"
Public Module ExtendedTextReading
Private Const WM_USER As Int32 = &H400&
Private Const EM_GETSCROLLPOS As Integer = WM_USER + 221, _
EM_SETSCROLLPOS As Integer = WM_USER + 222
Private Const WM_SETREDRAW As Integer = &HB
Private Const HiddenTextSyntax As String = "(^|[^\\])(\\\\)*\\v[^0]", _
ProtectedTextSyntax As String = "(^|[^\\])(\\\\)*\\protect[^0]"
Private HiddenTextChecker As Regex = _
New Regex(HiddenTextSyntax, _
RegexOptions.IgnoreCase Or RegexOptions.Compiled Or RegexOptions.ExplicitCapture), _
ProtectedTextChecker As Regex = _
New Regex(ProtectedTextSyntax, _
RegexOptions.IgnoreCase Or RegexOptions.Compiled Or RegexOptions.ExplicitCapture)
<DllImport("user32.dll")> _
Private Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Int32, _
ByVal wParam As Int32, ByRef lParam As Point) As Int32
End Function
<DllImport("user32.dll")> _
Private Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Int32, _
ByVal wParam As Integer, ByVal lParam As Integer) As Int32
End Function
<Flags()> _
Public Enum TypeOfText
Normal = 0
Hidden = 1
[Protected] = 2
End Enum
<System.Runtime.CompilerServices.Extension()> _
Public Function HasFlag(ByVal TextType As TypeOfText, _
ByVal TypeFlag As TypeOfText) As Boolean
Return (TextType And TypeFlag) = TypeFlag
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function GetScrollPosition(TextBox As TextBoxBase) As Point
Dim RTBScrollPoint As Point = Nothing
SendMessage(TextBox.Handle, EM_GETSCROLLPOS, 0, RTBScrollPoint)
Return RTBScrollPoint
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Sub SetScrollPosition(TextBox As TextBoxBase, _
ByVal RTBScrollPoint As Point)
SendMessage(TextBox.Handle, EM_SETSCROLLPOS, 0, RTBScrollPoint)
End Sub
<System.Runtime.CompilerServices.Extension()> _
Public Sub SetRedrawMode(TextBox As TextBoxBase, _
ByVal OnOrOff As Boolean)
SendMessage(TextBox.Handle, WM_SETREDRAW, CType(OnOrOff, Integer) And 1, 1)
If OnOrOff Then
TextBox.Refresh()
End If
End Sub
<System.Runtime.CompilerServices.Extension()> _
Public Function FullSelectionLength(TextBox As TextBoxBase) As Integer
If TypeOf TextBox IsNot RichTextBox Then
Return TextBox.SelectionLength
End If
Dim RichTextBox As RichTextBox = DirectCast(TextBox, RichTextBox)
Dim TypeRTB As Type = RichTextBox.GetType
Dim fi As FieldInfo = _
TypeRTB.GetField("curSelStart", _
BindingFlags.Instance Or BindingFlags.NonPublic)
Dim nCurSelStart As Integer = CType(fi.GetValue(RichTextBox), Integer)
fi = TypeRTB.GetField("curSelEnd", _
BindingFlags.Instance Or BindingFlags.NonPublic)
Dim nCurSelEnd As Integer = _
Math.Min(CType(fi.GetValue(RichTextBox), Integer), RichTextBox.TextLength)
If nCurSelStart = -1 AndAlso nCurSelEnd = -1 Then
Return 0
Else
Return _
nCurSelEnd - nCurSelStart
End If
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function ExtendedText(TextBox As TextBoxBase, _
Optional SelectionOnly As Boolean = False) As String
If TypeOf TextBox IsNot RichTextBox Then
If SelectionOnly Then
Return TextBox.SelectedText
Else
Return TextBox.Text
End If
End If
Dim RichTextBox As RichTextBox = DirectCast(TextBox, RichTextBox)
Dim TypeRTB As Type = RichTextBox.GetType
Dim prop As PropertyInfo = _
TypeRTB.GetProperty("WindowText", _
BindingFlags.Instance Or BindingFlags.NonPublic)
Dim Text As String = _
CType(prop.GetValue(RichTextBox, {}), String).Replace(ControlChars.Cr, "")
If SelectionOnly Then
Dim fi As FieldInfo = _
TypeRTB.GetField("curSelStart", _
BindingFlags.Instance Or BindingFlags.NonPublic)
Dim nCurSelStart As Integer = CType(fi.GetValue(RichTextBox), Integer)
fi = TypeRTB.GetField("curSelEnd", _
BindingFlags.Instance Or BindingFlags.NonPublic)
Dim nCurSelEnd As Integer = _
Math.Min(CType(fi.GetValue(RichTextBox), Integer), Text.Length)
If (nCurSelStart = -1 AndAlso nCurSelEnd = -1) _
OrElse nCurSelStart = nCurSelEnd Then
Text = ""
Else
Text = Text.Substring(nCurSelStart, nCurSelEnd - nCurSelStart)
End If
End If
Return Text
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function GetTextType(TextBox As TextBoxBase, _
ByVal StartIndex As Integer, ByVal WordLength As Integer) As TypeOfText
Dim TextType As TypeOfText = TypeOfText.Normal
If TypeOf TextBox Is RichTextBox Then
Dim RichTextBox As RichTextBox = DirectCast(TextBox, RichTextBox)
With RichTextBox
Dim CurrentStart As Integer = .SelectionStart, CurrentLength As Integer = .SelectionLength
.Select(StartIndex, WordLength)
If HiddenTextChecker.IsMatch(.SelectedRtf) _
OrElse .SelectionLength < WordLength Then
TextType = TextType Or TypeOfText.Hidden
End If
If .SelectionProtected _
OrElse ProtectedTextChecker.IsMatch(.SelectedRtf) Then
TextType = TextType Or TypeOfText.Protected
End If
.Select(CurrentStart, CurrentLength)
End With
End If
Return TextType
End Function
#End Region
Next, make the following additions and modifications to the file HTMLSpellCheck.vb, within class HTMLSpellCheck:
Private Function CanChangeWord(ByVal StartIndex As Integer, ByVal Length As Integer) As Boolean
If Length = 0 Then
Return False
ElseIf Me.TextBox Is Nothing OrElse TypeOf Me.TextBox Is TextBox Then
Return True
End If
With Me.TextBox
Dim TextType As TypeOfText = .GetTextType(StartIndex, Length)
Select Case True
Case TextType.HasFlag(TypeOfText.Protected)
Return False
Case TextType.HasFlag(TypeOfText.Hidden)
Return SpellCheckHiddenText
Case Else
Return True
End Select
End With
End Function
Private Sub UpdateDocument()
Dim DocumentText As New System.Text.StringBuilder
Dim UpTo = 0
Dim UsesTextBox As Boolean = Me.TextBox IsNot Nothing
For Each item In mc_Words
RemoveHandler item.UpdateClassEvent, AddressOf UpdateClassEvent
RemoveHandler item.SelectionChangedEvent, AddressOf WordSelectionChangedEvent
RemoveHandler item.UpdateWordEvent, AddressOf UpdateWordEvent
Next
mc_Words.Clear()
Dim ScrollPosition As Point
If UsesTextBox Then
Me.TextBox.SetRedrawMode(False)
ScrollPosition = Me.TextBox.GetScrollPosition()
End If
Try
For Each item In _
Dictionary.Formatting.RemoveWordBreaks(mc_Text).Split(" "c)
Dim ThisWordItem = New SpellCheckDialogWords(item, UpTo, CanChangeWord(UpTo, item.Length))
AddHandler ThisWordItem.UpdateClassEvent, AddressOf UpdateClassEvent
AddHandler ThisWordItem.SelectionChangedEvent, AddressOf WordSelectionChangedEvent
AddHandler ThisWordItem.UpdateWordEvent, AddressOf UpdateWordEvent
mc_Words.Add(ThisWordItem)
If ThisWordItem.OrigWord <> "" Then
DocumentText.Append("<a href='" & mc_Words.Count - 1 & "' class='Pending' id='" _
& mc_Words.Count - 1 & "'>" & ThisWordItem.OrigWord & "</a>")
End If
UpTo += item.Length + 1
If UpTo <= mc_Text.Length Then
DocumentText.Append(mc_Text.Substring(UpTo - 1, 1).Replace("&", "&"))
End If
Next item
Finally
If UsesTextBox Then
Me.TextBox.SetScrollPosition(ScrollPosition) : Me.TextBox.SetRedrawMode(True)
End If
End Try
mc_AllowNavigation = True
MyBase.DocumentText = HTMLStyle() & "<BODY onselectstart='return false;'>" & System.Text.RegularExpressions.Regex.Replace(DocumentText.ToString, "\r\n|\n\r|\r|\n", "<BR>")
mc_AllowNavigation = False
End Sub
#End Region
Public CanChangeWord As Boolean
Public Sub New(ByVal OrigWord As String, _
Optional ByVal StartIndex As Integer = 0, _
Optional ByVal CanChangeText As Boolean = True)
Me.OrigWord = OrigWord : mc_NewWord = OrigWord
Me.CanChangeWord = CanChangeText : Me.StartIndex = StartIndex
End Sub
Public OrigWord As String
Dim mc_NewWord As String
Public Property NewWord(Optional ByVal Autoupdate As Boolean = True) As String
Get
Return mc_NewWord
End Get
Set(ByVal value As String)
If Me.CanChangeWord AndAlso value <> mc_NewWord Then
mc_NewWord = value : Me.Changed = True
If Autoupdate Then
UpdateWord()
End If
End If
End Set
End Property
#Region "Constructors"
Public TextBox As TextBoxBase = Nothing
Public SpellCheckHiddenText As Boolean = False
Public Sub New(Optional TextBox As TextBoxBase = Nothing, _
Optional ByVal SpellCheckHiddenText As Boolean = False)
MyBase.AllowWebBrowserDrop = False
MyBase.IsWebBrowserContextMenuEnabled = False
Me.TextBox = TextBox : Me.SpellCheckHiddenText = SpellCheckHiddenText
End Sub
#End Region
#Region "Other Public Subs"
Dim mc_Text As String
Private Settings As New SpellCheckSettings
Public Sub SetText(Optional ByVal Text As String = "", Optional ByVal Settings As SpellCheckSettings = Nothing)
If Settings IsNot Nothing Then Me.Settings = Settings
If String.IsNullOrEmpty(Text) AndAlso Me.TextBox IsNot Nothing Then
mc_Text = Me.TextBox.ExtendedText()
Else
mc_Text = Text
End If
UpdateDocument()
End Sub
Now change the extTextBoxContextMenu_Opening procedure of SpellCheck\Controls\Menu.vb as follows:
Private Sub extTextBoxContextMenu_MenuOpening(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles extTextBoxContextMenu.MenuOpening
If OKToSpellCheck Then
SpellMenuItems.ContextMenuStrip = extTextBoxContextMenu.ContextMenuStrip
If Settings.IgnoreWordOverride(extTextBoxContextMenu.MenuSpellClickReturn.Word) Then
Else
With extTextBoxContextMenu.MenuSpellClickReturn
Beep
parentTextBox.SetRedrawMode(False)
Dim ScrollPosition As Point = parentRichTextBox.GetScrollPosition()
Dim TextType As TypeOfText = parentTextBox.GetTextType(.WordStart, .Word.Length)
If TextType = TypeOfText.Normal Then
SpellMenuItems.AddItems(.Word, CurrentDictionary, CurrentDefinitions, CurrentSynonyms, Settings)
End If
parentRichTextBox.SetScrollPosition(ScrollPosition)
parentTextBox.SetRedrawMode(True)
End With
End If
End If
End Sub
Next, go to the SpellCheckDialog.vb file, and make the following additions and modifications:
Private SourceTextBox As TextBoxBase, SpellCheckHiddenText As Boolean, ShowingSuggestions As Boolean
Private Function EnablingWordChange(Word As HTMLSpellCheck.SpellCheckDialogWords) As Boolean
Return ShowingSuggestions AndAlso Word.CanChangeWord
End Function
Public Overloads Function ShowDialog(ByVal owner As TextBoxBase, _
ByVal SpellCheckControlBase As SpellCheckControlBase, Optional ByVal Text As String = "", _
Optional ByVal SpellCheckHiddenText As Boolean = False) As List(Of HTMLSpellCheck.SpellCheckDialogWords)
Me.SourceTextBox = owner : Me.SpellCheckHiddenText = SpellCheckHiddenText
Me.SpellCheckControlBase = SpellCheckControlBase
If String.IsNullOrEmpty(Text) Then
Text = owner.ExtendedText()
End If
With HtmlSpellCheck1
.TextBox = owner : .SpellCheckHiddenText = SpellCheckHiddenText
.Dictionary = SpellCheckControlBase.CurrentDictionary
.SetText(Text)
End With
pbChangeAll.Maximum = HtmlSpellCheck1.Words.Count - 1
If HtmlSpellCheck1.Words.Count > 0 AndAlso Not EnablingWordChange(HtmlSpellCheck1.Words(0)) Then
btnChange.Enabled = False : btnSkip_Click(btnSkip, EventArgs.Empty)
End If
MyBase.StartPosition = FormStartPosition.CenterParent
MyBase.ShowDialog(owner)
Return HtmlSpellCheck1.Words
End Function
Private Function SelectedWord() As HTMLSpellCheck.SpellCheckDialogWords
Dim sw As HTMLSpellCheck.SpellCheckDialogWords = _
(From xItem In HtmlSpellCheck1.Words Where xItem.Selected).FirstOrDefault
btnChange.Enabled = _
sw IsNot Nothing AndAlso EnablingWordChange(sw)
Return sw
End Function
Private Sub HtmlSpellCheck1_SelectionChanged(ByVal sender As Object, ByVal e As HTMLSpellCheck.HTMLWordEventArgs) _
Handles HtmlSpellCheck1.SelectionChanged
If pnlSuggestions.InvokeRequired Then
Dim HtmlSpellCheck1_SelectionChanged_cb As New HtmlSpellCheck1_SelectionChanged_cb(AddressOf HtmlSpellCheck1_SelectionChanged)
pnlSuggestions.Invoke(HtmlSpellCheck1_SelectionChanged_cb, sender, e)
Else
FillSuggestions()
ShowHideSuggestions(True)
txtChangeTo.Focus()
btnChange.Enabled = _
e.Word IsNot Nothing AndAlso EnablingWordChange(e.Word)
ChangeToUseOldWord = True
txtChangeTo_TextChanged(txtChangeTo, EventArgs.Empty)
ChangeToChanged = False
If e.Word.Selected AndAlso Not btnChange.Enabled Then
MoveToNextWordError()
End If
End If
End Sub
Private Sub ShowHideSuggestions(ByVal Show As Boolean)
ShowingSuggestions = Show
If pnlSuggestions.InvokeRequired Then
Dim ShowHideSuggestions_cb As New ShowHideSuggestions_cb(AddressOf ShowHideSuggestions)
pnlSuggestions.Invoke(ShowHideSuggestions_cb, Show)
Else
Dim theSelectedWord = SelectedWord()
pnlSuggestions.Visible = Show
btnIgnore.Enabled = Show AndAlso theSelectedWord IsNot Nothing AndAlso theSelectedWord.Changed = False AndAlso theSelectedWord.SpellCheckState <> HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK
btnSkip.Enabled = Show AndAlso (From xItem In HtmlSpellCheck1.Words Where (xItem.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error OrElse xItem.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case) AndAlso xItem IsNot theSelectedWord).Count > 0
tstbChangeTo.Enabled = _
MenuCurrentWord IsNot Nothing AndAlso EnablingWordChange(MenuCurrentWord)
btnRevert.Enabled = Show AndAlso theSelectedWord IsNot Nothing AndAlso theSelectedWord.Changed = True
btnChangeAll.Enabled = Not ChangeingAll AndAlso (From xItem In HtmlSpellCheck1.Words Where (xItem.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case OrElse xItem.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error)).Count > 0
btnRevertAll.Enabled = (From xItem In HtmlSpellCheck1.Words Where xItem.Changed = True).Count > 0
End If
End Sub
Private Sub txtChangeTo_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles txtChangeTo.KeyUp
Select Case e.KeyCode
Case Keys.Apps
e.Handled = True
If txtChangeTo.ContextMenu Is Nothing Then
txtChangeTo.ContextMenu = New ContextMenu
End If
Dim theSelectedWord = SelectedWord()
If theSelectedWord IsNot Nothing Then
MenuCurrentWord = theSelectedWord
tstbChangeTo.Enabled = _
MenuCurrentWord IsNot Nothing AndAlso EnablingWordChange(MenuCurrentWord)
UpdateMenuItems()
OpenedByClick = False
cmsHTMLSpellCheck.Show(HtmlSpellCheck1, Point.Empty)
End If
End Select
End Sub
Private Sub SpellMenuItems_WordChanged(ByVal sender As Object, ByVal e As Menu.AddSpellItemsToMenu.SpellItemEventArgs) Handles SpellMenuItems.WordChanged
If MenuCurrentWord.CanChangeWord Then
MenuCurrentWord.NewWord = e.Word
MenuCurrentWord.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK
End If
WordUpdatedFromMenu()
End Sub
Private Sub tstbChangeTo_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles tstbChangeTo.KeyPress
If e.KeyChar = vbCr Then
If MenuCurrentWord.CanChangeWord Then
MenuCurrentWord.NewWord = tstbChangeTo.Text
MenuCurrentWord.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK
Else
Beep() : MessageBox.Show("This word is protected and/or hidden!")
End If
UpdateMenuItems()
WordUpdatedFromMenu()
End If
End Sub
Private Sub UpdateMenuItems()
If Replace(Dictionary.Formatting.RemoveWordBreaks(MenuCurrentWord.NewWord), " ", "") = MenuCurrentWord.NewWord Then
SpellMenuItems.ContextMenuStrip = cmsHTMLSpellCheck
SpellMenuItems.RemoveSpellMenuItems()
If MenuCurrentWord.CanChangeWord Then
SpellMenuItems.AddItems(MenuCurrentWord.NewWord, SpellCheckControlBase.CurrentDictionary, SpellCheckControlBase.CurrentDefinitions, SpellCheckControlBase.CurrentSynonyms, SpellCheckControlBase.Settings)
End If
End If
mtsChangeTo.Text = "Change " & MenuCurrentWord.NewWord & " to:"
tstbChangeTo.Text = MenuCurrentWord.NewWord
tsiRevertTo.Text = "Revert to " & MenuCurrentWord.OrigWord
tsiRevertTo.Visible = MenuCurrentWord.Changed
cmsHTMLSpellCheck_LocationChanged(cmsHTMLSpellCheck, EventArgs.Empty)
End Sub
Private Sub HtmlSpellCheck1_WordSpellChecked(ByVal sender As Object, ByVal e As HTMLSpellCheck.HTMLWordEventArgs) Handles HtmlSpellCheck1.WordSpellChecked
If pbChangeAll.Visible Then
Dim Progress As Integer = HtmlSpellCheck1.Words.IndexOf(e.Word)
pbChangeAll.SafeInvoke(Function(x As ProgressBar) InlineAssignHelper(x.Value, Progress))
End If
pnlPleaseWait.SafeInvoke(Function(x As Panel) InlineAssignHelper(x.Visible, True))
If e.Word.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error OrElse e.Word.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case Then
If ChangeingAll Then
Dim Suggestions = GetSuggestions(e.Word.NewWord)
If Suggestions.Count > 0 AndAlso e.Word.CanChangeWord Then
e.Word.NewWord = Suggestions.First
e.Word.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK
If SelectedWord() Is e.Word Then
HtmlSpellCheck1_SelectionChanged(HtmlSpellCheck1, New HTMLSpellCheck.HTMLWordEventArgs() With {.Word = e.Word})
End If
End If
Exit Sub
End If
If pnlSuggestions.Visible = False Then
e.Word.Selected = True
End If
ShowHideSuggestions(pnlSuggestions.Visible)
End If
End Sub
Private Function MoveToNextWordError(Optional ByVal DoRecheck As Boolean = True) As Boolean
With HtmlSpellCheck1
MoveToNextWordError = False
Dim StartIndex = 0
Dim theSelectedWord = SelectedWord()
If theSelectedWord IsNot Nothing Then
theSelectedWord.Selected = False
StartIndex = .Words.IndexOf(theSelectedWord) + 1
End If
ReCheck:
For i = StartIndex To .Words.Count - 1
If .Words(i) IsNot Nothing Then
Select Case .Words(i).SpellCheckState
Case HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error, _
HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case
If .Words(i).CanChangeWord Then
.Words(i).Selected = True
MoveToNextWordError = True : Exit Function
End If
End Select
If .Words(i).Selected Then
.Words(i).Selected = False
End If
End If
Next
If DoRecheck Then
If StartIndex = 0 Then
CompleteSpellCheck() : Exit Function
End If
StartIndex = 0
GoTo ReCheck
End If
End With
End Function
Private Sub btnChange_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnChange.Click
Dim theSelectedWord = SelectedWord()
If theSelectedWord IsNot Nothing Then
If theSelectedWord.CanChangeWord Then
theSelectedWord.NewWord = txtChangeTo.Text
theSelectedWord.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK
Else
Beep() : MessageBox.Show("This word is protected and/or hidden!")
End If
End If
btnSkip_Click(btnSkip, EventArgs.Empty)
ShowHideSuggestions(pnlSuggestions.Visible)
End Sub
Private Sub StartChangeAll()
btnAdd.Enabled = False
ChangeingAll = True
If mt_ChangeAll IsNot Nothing AndAlso mt_ChangeAll.IsAlive Then
mt_ChangeAll.Abort()
End If
Dim SelectedWord = Me.SelectedWord
NewTextForChangeAll = txtChangeTo.Text
OldTextForChangeAll = SelectedWord.OrigWord
If SelectedWord IsNot Nothing Then
SelectedWord.Selected = False
End If
ShowHideSuggestions(False)
btnChangeAll.Enabled = False
mt_ChangeAll = New System.Threading.Thread(AddressOf ChangeAll)
mt_ChangeAll.Name = "Spell Check - Change all"
mt_ChangeAll.IsBackground = True
mt_ChangeAll.Start()
End Sub
Private Sub ChangeAll()
Dim WordErrors = _
(From xItem In HtmlSpellCheck1.Words _
Where String.Compare(xItem.OrigWord, OldTextForChangeAll, True) = 0 _
AndAlso (xItem.SpellCheckState = _
HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case _
OrElse xItem.SpellCheckState = _
HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error))
For Each item In WordErrors.ToArray
If item.CanChangeWord Then
Dim Suggestions = GetSuggestions(item.NewWord)
If Suggestions.Count > 0 Then
If String.IsNullOrEmpty(NewTextForChangeAll) Then
item.NewWord = Suggestions.First
Else
item.NewWord = NewTextForChangeAll
End If
item.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK
If SelectedWord() Is item Then
HtmlSpellCheck1_SelectionChanged(HtmlSpellCheck1, _
New HTMLSpellCheck.HTMLWordEventArgs() With {.Word = item})
End If
End If
Else
Beep()
End If
Next
btnRevertAll.SafeInvoke(Function(x As Button) InlineAssignHelper(x.Enabled, True))
If HtmlSpellCheck1.mt_SpellCheck IsNot Nothing _
AndAlso HtmlSpellCheck1.mt_SpellCheck.IsAlive Then
Else
Dim btnChangeAllEnabled = _
(From xItem In HtmlSpellCheck1.Words _
Where (xItem.SpellCheckState = _
HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case _
OrElse xItem.SpellCheckState = _
HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error)).Count > 0
ChangeingAll = False
If btnChangeAllEnabled = True Then
btnChangeAll.SafeInvoke(Function(x As Button) _
InlineAssignHelper(x.Enabled, btnChangeAllEnabled))
btnSkip_Click(btnSkip, EventArgs.Empty)
Else
CompleteSpellCheck()
End If
End If
End Sub
Private Sub AddWordButton(ByVal CaseSensitive As Boolean)
Dim theSelectedWord = SelectedWord()
If theSelectedWord IsNot Nothing Then
Dim WordCanBeChanged As Boolean = theSelectedWord.CanChangeWord
Dim Word As String = txtChangeTo.Text
If ChangeToChanged = False Then
Word = theSelectedWord.NewWord
Else
theSelectedWord.NewWord = Word
End If
Try
If SpellCheckControlBase.CurrentDictionary.SpellCheckWord(Word) <> i00SpellCheck.Dictionary.SpellCheckWordError.OK Then
If CaseSensitive = False Then
Word = LCase(Word)
End If
AddWordToDict(Word, theSelectedWord)
End If
Finally
If Not WordCanBeChanged Then
theSelectedWord.NewWord = theSelectedWord.OrigWord
theSelectedWord.Changed = False
End If
End Try
End If
btnSkip_Click(btnSkip, EventArgs.Empty)
End Sub
Private Sub cmsHTMLSpellCheck_Opening(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles cmsHTMLSpellCheck.Opening
If OpenedByClick Then
Dim pCursor = Cursor.Position
Dim pHtml = HtmlSpellCheck1.PointToScreen(New Point(0, 0))
pCursor.X = pCursor.X - pHtml.X
pCursor.Y = pCursor.Y - pHtml.Y
Dim element = HtmlSpellCheck1.Document.GetElementFromPoint(pCursor)
If element IsNot Nothing AndAlso element.Id <> "" Then
Dim ElementNo As Integer
If IsNumeric(Integer.TryParse(element.Id, ElementNo)) AndAlso HtmlSpellCheck1.Words.Count > ElementNo Then
MenuCurrentWord = HtmlSpellCheck1.Words.Item(ElementNo)
tstbChangeTo.Enabled = _
MenuCurrentWord IsNot Nothing AndAlso EnablingWordChange(MenuCurrentWord)
UpdateMenuItems()
Else
e.Cancel = True
End If
Else
e.Cancel = True
End If
Else
OpenedByClick = True
End If
End Sub
Finally, go to file Misc.vb, and change the ShowDialog procedure as follows:
#Region "Show spellcheck dialog"
Public Sub ShowDialog(Optional ByVal SpellCheckHiddenText As Boolean = False) Implements iSpellCheckDialog.ShowDialog
If CurrentDictionary IsNot Nothing AndAlso CurrentDictionary.Loading = False Then
Using SpellCheckDialog As New SpellCheckDialog
Dim SpellCheckResults = _
SpellCheckDialog.ShowDialog(parentTextBox, Me, _
parentTextBox.ExtendedText, SpellCheckHiddenText)
DrawSpellingErrors = False
extTextBoxCommon.LockWindowUpdate(Control.Handle)
Control.SuspendLayout()
Dim SelStart = parentTextBox.SelectionStart
Dim SellLength = parentTextBox.FullSelectionLength
If parentRichTextBox IsNot Nothing Then
Dim Offset As Integer = 0
For Each word In (From xItem In SpellCheckResults Where xItem.Changed = True AndAlso xItem.NewWord <> xItem.OrigWord).ToArray.Reverse
parentRichTextBox.Select(word.StartIndex - Offset, word.OrigWord.Length)
parentRichTextBox.SelectedText = word.NewWord
Next
CType(parentRichTextBox, TextBoxBase).ClearUndo()
Else
Dim OldVertPos = extTextBoxCommon.GetScrollBarLocation(parentTextBox)
Dim NewText As String = parentTextBox.ExtendedText
For Each word In (From xItem In SpellCheckResults Where xItem.Changed = True AndAlso xItem.NewWord <> xItem.OrigWord).ToArray.Reverse
NewText = Strings.Left(NewText, word.StartIndex) & _
word.NewWord & _
Strings.Right(NewText, Len(NewText) - (word.StartIndex + word.OrigWord.Length))
Next
parentTextBox.Text = NewText
extTextBoxCommon.SetScrollBarLocation(parentTextBox, OldVertPos)
End If
parentTextBox.SelectionStart = SelStart
parentTextBox.SelectionLength = SellLength
DrawSpellingErrors = True
Control.ResumeLayout()
extTextBoxCommon.LockWindowUpdate(IntPtr.Zero)
End Using
End If
End Sub
#End Region
Notice that when one invokes ShowDialog(True), the spell-checker spell-checks hidden (invisible) text, but not protected text. If one invokes ShowDialog(False) or simply ShowDialog(), then neither hidden nor protected text is spell-checked.
|
|
|
|
|
EVEN MORE RECOMMENDED CHANGES:
One thing I find irritating is that when this spell-checker is in force, pressing F3 always does a case-cycling on the selected word. In order to be able to turn on and off this feature--say, if F3 is being used for another purpose (like for a menu option)--do the following:
In the i00SpellCheck\ControlExtension\Extension.vb file, add the following code:
Friend ChangeCaseWithF3 As Boolean = True
<System.Runtime.CompilerServices.Extension()> _
Public Sub SetF3CasingOption(ByVal sender As Control, _
Optional ByVal ChangeCase As Boolean = True)
ChangeCaseWithF3 = ChangeCase
End Sub
Now, in the extTextBoxChangeCase.vb file, make the following change:
Private Sub parentTextBox_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles mc_TextBox.KeyUp
Select Case e.KeyCode
Case Keys.F3
If ChangeCaseWithF3 Then
CycleCase()
End If
End Select
End Sub
To enable using F3 to cycle through case, call textbox.SetF3CasingOption(True) (True is the default value) before enabling spell-check--say, right where you would call IncludeOrExcludeStandardMenu. To disable the feature, call textBox.SetF3CasingOption(False) instead; this latter version will free up F3 to be used for another purpose.
==================================
Also, in the file i00SpellCheck\SpellCheck\Engine\Extension.vb, you should make the following change in order to ensure that GetTextType always catches when a selected word in a RichTextBox contains text that is hidden and semi-protected:
<System.Runtime.CompilerServices.Extension()> _
Public Function GetTextType(TextBox As TextBoxBase, _
ByVal StartIndex As Integer, ByVal WordLength As Integer) As TypeOfText
Dim TextType As TypeOfText = TypeOfText.Normal
If TypeOf TextBox Is RichTextBox Then
Dim RichTextBox As RichTextBox = DirectCast(TextBox, RichTextBox)
With RichTextBox
Dim ScrollPosition As Point = .GetScrollPosition()
Dim CurrentStart As Integer = .SelectionStart, _
CurrentLength As Integer = .FullSelectionLength
.Select(StartIndex, WordLength)
If .SelectionProtected _
OrElse ProtectedTextChecker.IsMatch(.SelectedRtf) Then
TextType = TextType Or TypeOfText.Protected
End If
If HiddenTextChecker.IsMatch(.SelectedRtf) Then
TextType = TextType Or TypeOfText.Hidden
ElseIf .SelectionLength < WordLength _
AndAlso Not TextType.HasFlag(TypeOfText.Protected) Then
TextType = TextType Or TypeOfText.Hidden
For position As Integer = StartIndex To StartIndex + WordLength - 1
.Select(position, 1)
If .SelectionProtected Then
TextType = TextType Or TypeOfText.Protected : Exit For
End If
Next position
End If
.Select(CurrentStart, CurrentLength) : .SetScrollPosition(ScrollPosition)
End With
End If
Return TextType
End Function
|
|
|
|
|
I am using i00 Spell check in my project
|
|
|
|
|
very thorough offering with excellent examples, many techniques here have given me a whole bunch of ideas with solving my problem. thank you to the author!
|
|
|
|
|
I am using i00 Spell check in my project. Thank you for sharing this tool.
|
|
|
|
|
|
I am using i00 Spell check in my project.
Thanks for the time you have put into this. It it very useful. Would you consider setting up a GitHub project for this and putting it on NuGet?
|
|
|
|
|
|
Thank you for the very useful code my dude.
|
|
|
|
|
Thank you so much! This is such an awesome plugin. It's going to hopefully help a lot of people in my software! - Curtis @ Wizzard Utilities.
|
|
|
|
|
I am using i00 Spell check in my project.
Great library!
|
|
|
|
|
Hello , first of all i really want to thank you for the great effort to write this amazing complete open source project , to make everyone benefit from it .
I have installed the i00 Spell Check project include the Binding List Projects , to my VB.Net 2010 project , everything worked perfect .
But my problem is only occurred if you changed Scale and Layout of windows display settings to more than 100% !!! .
What happened next that while I am in the run time of my project exe , once i start correcting the incorrect spell only by pointing on the suggested word from i00 spell checker right clicked menu , then the Display and everything related to system layout changed and worked in properly , like labels ,reports fonts , controls ..etc .
Your reply will be so appreciated . let me give you another hint that the same problems occurred using other devices , only if i hit correct words on Controls which i have enabled the i00 spell check on it .
|
|
|
|
|
Hello ... sorry I missed this ...
Disable the references to WPF stuff Windows.Base etc... then fix the errors ...
Also I am going to release a new version in the coming week that will fix this.
There are other posts in here where I have detailed how to fix this.
Kris
|
|
|
|
|
Excellent post, very grateful.
|
|
|
|
|