So I have written a really really basic word processor and am having trouble figuring out how to add the keys ctrl+c, ctrl+x, ctrl+v so that i can highlight text copy, cut, or paste when i want without using the buttons i have created. Please help. Here is my code.
VB.NET:
Option Strict On
Option Explicit On
Imports System
Imports System.ComponentModel
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Collections
Imports System.Text
Public Class StyledTextArea : Inherits UserControl
Private view As View ' the view in the MVC pattern
Private lineNumberView As LineNumberView
Private model As model ' the model in the MVC pattern
Private selecting As Boolean ' user selecting text
Private panel As New Panel() ' we use panel so we can set the borderstyle
Private hScrollBar As New HScrollBar()
Private vScrollBar As New VScrollBar()
Private Const hScrollBarHeight As Integer = 15
Private Const vScrollBarWidth As Integer = 15
Public LineNumberWidth As Integer = 50
Public Event ColumnChanged As ColumnEventHandler
Public Event LineChanged As LineEventHandler
' indicates that the key press has been processed by ProcessDialogKey,
' so KeyPressed does not have to process this.
Private keyProcessed As Boolean
Private caretLocation As New ColumnLine(1, 1)
' indicates whether the text has been edited
Private editedField As Boolean
Friend selectionStartLocation As New ColumnLine(0, 0)
Friend selectionEndLocation As New ColumnLine(0, 0)
' represents the number of lines not displayed
' because the screen was scrolled down
Friend TopInvisibleLineCount As Integer
Public Sub New()
InitializeComponents()
End Sub
Public Property Edited() As Boolean
Get
Return editedField
End Get
Set(ByVal value As Boolean)
'allows it to be reset/set,
'for example if the document using this control is saved
editedField = value
End Set
End Property
Public Property CaretColor() As Color
Get
Return view.CaretColor
End Get
Set(ByVal caretColor As Color)
view.CaretColor = caretColor
End Set
End Property
Public Property LineNumberForeColor() As Color
Get
Return lineNumberView.ForeColor
End Get
Set(ByVal color As Color)
lineNumberView.ForeColor = color
End Set
End Property
Public Property LineNumberBackColor() As Color
Get
Return lineNumberView.BackColor
End Get
Set(ByVal color As Color)
lineNumberView.BackColor = color
End Set
End Property
Public Property TextForeColor() As Color
Get
Return view.ForeColor
End Get
Set(ByVal color As Color)
view.ForeColor = color
End Set
End Property
Public Property TextBackColor() As Color
Get
Return view.BackColor
End Get
Set(ByVal color As Color)
view.BackColor = color
End Set
End Property
Public Property HighlightForeColor() As Color
Get
Return view.highlightForeColor
End Get
Set(ByVal color As Color)
view.highlightForeColor = color
End Set
End Property
Public Property HighlightBackColor() As Color
Get
Return view.highlightBackColor
End Get
Set(ByVal color As Color)
view.highlightBackColor = color
End Set
End Property
Public ReadOnly Property LineCount() As Integer
Get
Return model.LineCount
End Get
End Property
Public Function GetLine(ByVal lineNo As Integer) As String
Return model.GetLine(lineNo)
End Function
Friend Function HasSelection() As Boolean
If selectionStartLocation.Equals(selectionEndLocation) Then
Return False
Else
Return True
End If
End Function
Private Sub InitializeComponents()
model = New Model()
view = New View(model)
view.controller = Me
view.ForeColor = Color.Black
view.BackColor = Color.White
view.Cursor = Cursors.IBeam
lineNumberView = New LineNumberView(model)
lineNumberView.controller = Me
lineNumberView.BackColor = Color.AntiqueWhite
lineNumberView.ForeColor = Color.Black
lineNumberView.TabStop = False
vScrollBar.TabStop = False
hScrollBar.TabStop = False
ResizeComponents()
panel.Dock = DockStyle.Fill
panel.Controls.Add(lineNumberView)
panel.Controls.Add(view)
panel.Controls.Add(hScrollBar)
panel.Controls.Add(vScrollBar)
panel.BorderStyle = BorderStyle.Fixed3D
Me.Controls.Add(panel)
AddHandler view.KeyPress, AddressOf view_KeyPress
AddHandler view.MouseDown, AddressOf view_MouseDown
AddHandler view.MouseUp, AddressOf view_MouseUp
AddHandler view.MouseMove, AddressOf view_MouseMove
AddHandler model.LineCountChanged, AddressOf Model_LineCountChanged
AddHandler model.LongestLineCharCountChanged, _
AddressOf model_LongestLineCharCountChanged
AddHandler vScrollBar.Scroll, AddressOf vScrollBar_Scroll
AddHandler hScrollBar.Scroll, AddressOf hScrollBar_Scroll
AddHandler panel.Resize, AddressOf panel_Resize
hScrollBar.Enabled = False
hScrollBar.SmallChange = 1 ' 1 character
hScrollBar.LargeChange = 2
vScrollBar.Enabled = False
vScrollBar.SmallChange = 1
vScrollBar.LargeChange = 2
End Sub
Friend Property CaretLineNo() As Integer
Get
Return caretLocation.Line
End Get
Set(ByVal newLineNo As Integer)
If newLineNo > 0 And newLineNo <= model.LineCount Then
Dim oldLineNo As Integer = caretLocation.Line
caretLocation.Line = newLineNo
If oldLineNo <> newLineNo Then
OnLineChanged(New LineEventArgs(oldLineNo, newLineNo))
End If
End If
End Set
End Property
Friend Property CaretColumnNo() As Integer
Get
Return caretLocation.Column
End Get
Set(ByVal newColumnNo As Integer)
If newColumnNo > 0 And newColumnNo <= GetCurrentLine().Length + 1 Then
Dim oldColumnNo As Integer = caretLocation.Column
caretLocation.Column = newColumnNo
If oldColumnNo <> newColumnNo Then
OnColumnChanged(New ColumnEventArgs(oldColumnNo, newColumnNo))
End If
End If
End Set
End Property
Protected Overridable Sub OnColumnChanged(ByVal e As ColumnEventArgs)
RaiseEvent ColumnChanged(Me, e)
End Sub
Protected Overridable Sub OnLineChanged(ByVal e As LineEventArgs)
RaiseEvent LineChanged(Me, e)
End Sub
Public Function Find(ByVal userPattern As String, _
ByVal startColumn As Integer, ByVal startLine As Integer, _
ByVal caseSensitive As Boolean, _
ByVal wholeWord As Boolean, ByVal goUp As Boolean) As ColumnLine
'make sure startColumn and startLine is greater than 0
startColumn = Math.Max(startColumn, 1)
Dim pattern As String = userPattern.Trim()
Dim patternLength As Integer = pattern.Length
Dim lineCount As Integer = model.LineCount
Dim direction As Integer = 1
If goUp Then direction = -1
startLine = Math.Max(startLine, 1)
startLine = Math.Min(startLine, lineCount)
Dim lineNo As Integer = startLine
If Not caseSensitive Then
pattern = pattern.ToUpper()
End If
While lineNo <= lineCount And lineNo > 0
Dim thisLine As String = model.GetLine(lineNo)
If Not caseSensitive Then
thisLine = thisLine.ToUpper()
End If
Dim searchResult As Integer = -1
If lineNo = startLine Then
If startColumn - 1 < thisLine.Length Then
If goUp Then
searchResult = thisLine.LastIndexOf(pattern, startColumn - 1)
Else
searchResult = thisLine.IndexOf(pattern, startColumn - 1)
End If
End If
Else
If goUp Then
searchResult = thisLine.LastIndexOf(pattern)
Else
searchResult = thisLine.IndexOf(pattern)
End If
End If
If searchResult <> -1 And wholeWord Then
'search successful but now test if the found pattern is a
'whole word by checking the characters after and before
'the match
If searchResult > 0 Then
'test the character before the match
If Char.IsLetterOrDigit( _
Convert.ToChar(thisLine.Substring(searchResult - 1, 1))) Then
searchResult = -1
End If
End If
If searchResult <> -1 And _
thisLine.Length > searchResult + patternLength Then
'test the character after the match
If Char.IsLetterOrDigit( _
Convert.ToChar(thisLine.Substring(searchResult + _
patternLength, 1))) Then
searchResult = -1
End If
End If
End If
If searchResult <> -1 Then 'successful
'move caret to new position
CaretLineNo = lineNo
CaretColumnNo = searchResult + patternLength + 1
Highlight(searchResult + 1, lineNo, _
searchResult + patternLength + 1, lineNo)
RedrawAll()
Return New ColumnLine(searchResult + 1, lineNo)
End If
lineNo = lineNo + direction
End While
Return New ColumnLine(0, 0)
End Function
Private Sub ResizeComponents()
lineNumberView.Size = New Size(LineNumberWidth, panel.Height - _
hScrollBarHeight - 4)
lineNumberView.Location = New Point(0, 0)
view.Size = New Size(panel.Width - lineNumberView.Width - _
vScrollBarWidth - 4, panel.Height - hScrollBarHeight - 4)
view.Location = New Point(lineNumberView.Width, 0)
vScrollBar.Location = New Point(view.Width + lineNumberView.Width, 0)
vScrollBar.Size = New Size(vScrollBarWidth, view.Height)
hScrollBar.Location = New Point(0, view.Height)
hScrollBar.Size = _
New Size(view.Width + lineNumberView.Width, hScrollBarHeight)
AdjustVScrollBar()
AdjustHScrollBar()
End Sub
Public Sub SelectLine(ByVal lineNo As Integer)
If lineNo <= model.LineCount Then
Dim thisLine As String = model.GetLine(lineNo)
Dim length As Integer = thisLine.Length
selectionStartLocation = New ColumnLine(1, lineNo)
selectionEndLocation = New ColumnLine(length + 1, lineNo)
caretLocation = selectionStartLocation
RedrawAll()
End If
End Sub
Private Sub panel_Resize(ByVal sender As Object, ByVal e As EventArgs)
ResizeComponents()
End Sub
Private Sub hScrollBar_Scroll(ByVal sender As Object, _
ByVal e As ScrollEventArgs)
Select Case e.Type
Case ScrollEventType.SmallIncrement
If view.LeftInvisibleCharCount < _
model.LongestLineCharCount - view.VisibleCharCount + 1 Then
view.MoveScreen(hScrollBar.SmallChange)
End If
Case ScrollEventType.LargeIncrement
If view.LeftInvisibleCharCount < _
model.LongestLineCharCount - view.VisibleCharCount Then
Dim maxIncrement As Integer = _
Math.Min(hScrollBar.LargeChange, model.LongestLineCharCount - _
view.VisibleCharCount - view.LeftInvisibleCharCount)
view.MoveScreen(maxIncrement)
End If
Case ScrollEventType.SmallDecrement
view.MoveScreen(-hScrollBar.SmallChange)
Case ScrollEventType.LargeDecrement
view.MoveScreen(-hScrollBar.LargeChange)
Case ScrollEventType.ThumbTrack
view.LeftInvisibleCharCount = e.NewValue
RedrawAll()
Case ScrollEventType.ThumbPosition
view.LeftInvisibleCharCount = e.NewValue
RedrawAll()
End Select
End Sub
Private Sub vScrollBar_Scroll(ByVal sender As Object, _
ByVal e As ScrollEventArgs)
Select Case e.Type
Case ScrollEventType.SmallIncrement
If TopInvisibleLineCount < model.LineCount - view.VisibleLineCount Then
view.Scroll(vScrollBar.SmallChange)
lineNumberView.RedrawAll()
End If
Case ScrollEventType.LargeIncrement
If TopInvisibleLineCount < model.LineCount - view.VisibleLineCount Then
Dim maxIncrement As Integer = _
Math.Min(vScrollBar.LargeChange, _
model.LineCount - view.VisibleLineCount - TopInvisibleLineCount)
view.Scroll(maxIncrement)
lineNumberView.RedrawAll()
End If
Case ScrollEventType.SmallDecrement
view.Scroll(-vScrollBar.SmallChange)
lineNumberView.RedrawAll()
Case ScrollEventType.LargeDecrement
view.Scroll(-vScrollBar.LargeChange)
lineNumberView.RedrawAll()
Case ScrollEventType.ThumbTrack
TopInvisibleLineCount = e.NewValue
RedrawAll()
Case ScrollEventType.ThumbPosition
TopInvisibleLineCount = e.NewValue
RedrawAll()
End Select
End Sub
Private Sub Model_LineCountChanged(ByVal sender As Object, _
ByVal e As LineCountEventArgs)
AdjustVScrollBar()
End Sub
Private Sub AdjustHScrollBar()
If view.Width < model.LongestLineCharCount * view.characterWidth + 1 Then
hScrollBar.Enabled = True
hScrollBar.Maximum = model.LongestLineCharCount - _
view.VisibleCharCount + 1 '10 is the margin
hScrollBar.Value = Math.Min(hScrollBar.Maximum, _
view.LeftInvisibleCharCount)
Else
hScrollBar.Enabled = False
End If
End Sub
Private Sub AdjustVScrollBar()
'adjust vScrollBar
If view.VisibleLineCount < model.LineCount Then
vScrollBar.Enabled = True
vScrollBar.Maximum = model.LineCount - view.VisibleLineCount + 1
vScrollBar.Value = Math.Min(TopInvisibleLineCount, vScrollBar.Maximum)
Else
vScrollBar.Enabled = False
End If
End Sub
Private Sub model_LongestLineCharCountChanged(ByVal sender As Object, _
ByVal e As LongestLineEventArgs)
If e.LongestLineCharCount < view.VisibleCharCount Then
view.LeftInvisibleCharCount = 0
End If
AdjustHScrollBar()
End Sub
Private Sub view_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs)
'move the caret
If selecting And e.Button = MouseButtons.Left Then
selecting = False 'reset selecting
view.RepositionCaret(e.X, e.Y)
RedrawAll()
End If
End Sub
Private Sub view_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
'move the caret
If e.Button = MouseButtons.Left Then
view.RepositionCaret(e.X, e.Y)
selecting = True
Dim cl As ColumnLine = view.TranslateIntoCaretLocation(e.X, e.Y)
selectionStartLocation = cl
selectionEndLocation = cl
RedrawAll()
End If
End Sub
Private Sub view_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
'only respond when selecting, i.e. when the left button is pressed
If selecting Then
Dim cl As ColumnLine
cl = view.TranslateIntoCaretLocation(e.X, e.Y)
selectionEndLocation = cl
RedrawAll()
End If
End Sub
Private Sub view_KeyPress(ByVal sender As Object, ByVal e As KeyPressEventArgs)
If Not keyProcessed Then
Dim c As Char = e.KeyChar
Dim convertedChar As Integer = Convert.ToInt32(c)
RemoveSelection()
ResetSelection()
Select Case convertedChar
Case Keys.Back 'backspace
If Not (IsCaretOnFirstColumn() And IsCaretOnFirstLine()) Then
'not at beginning of Model
If IsCaretOnFirstColumn() Then
Dim oldLine As String = model.GetLine(CaretLineNo)
Dim prevLine As String = model.GetLine(CaretLineNo - 1)
Dim newLine As String = prevLine & oldLine
model.SetLine(CaretLineNo - 1, newLine)
model.RemoveLine(CaretLineNo, True)
CaretLineNo = CaretLineNo - 1
CaretColumnNo = prevLine.Length + 1
Else
Dim oldLine As String = model.GetLine(CaretLineNo)
Dim newLine As String = oldLine.Remove(CaretColumnNo - 2, 1)
model.SetLine(CaretLineNo, newline)
CaretColumnNo = CaretColumnNo - 1
End If
editedField = True
End If
Case Keys.Return ' return key
Dim oldLine As String = GetCurrentLine()
Dim newLine As String = oldLine.Substring(0, CaretColumnNo - 1)
Dim nextLine As String = oldLine.Substring(CaretColumnNo - 1)
model.SetLine(CaretLineNo, newline)
model.InsertLine(CaretLineNo + 1, nextLine)
Dim needToScroll As Boolean = IsCaretOnLastVisibleLine()
CaretColumnNo = 1
CaretLineNo = CaretLineNo + 1
If needToScroll Then
view.Scroll(1)
End If
editedField = True
Case Keys.Escape 'Escape
'escape key, do nothing
Case Else
model.InsertChar(c, caretLocation)
CaretColumnNo = CaretColumnNo + 1
editedField = True
End Select
ScrollToShowCaret()
RedrawAll()
e.Handled = True
End If
End Sub
Public Sub ResetSelection()
selectionStartLocation.Column = 0
selectionStartLocation.Line = 0
selectionEndLocation.Column = 0
selectionEndLocation.Line = 0
End Sub
Public Sub RemoveSelection()
If Not HasSelection() Then
Return
End If
Dim initialCaretLocation As ColumnLine = caretLocation
' after selection is removed, adjust CaretX position.
Dim x1, y1, x2, y2 As Integer
x1 = selectionStartLocation.Column
y1 = selectionStartLocation.Line
x2 = selectionEndLocation.Column
y2 = selectionEndLocation.Line
If y1 > y2 Or (y1 = y2 And x1 > x2) Then
'swap (x1, y1) and (x2, y2)
Dim t As Integer
t = x1 : x1 = x2 : x2 = t
t = y1 : y1 = y2 : y2 = t
End If
If y1 = y2 Then
Dim thisLine As String = model.GetLine(y1)
model.SetLine(y1, thisLine.Substring(0, x1 - 1) & _
thisLine.Substring(x2 - 1, thisLine.Length - x2 + 1))
'it's okay if event is raised when CaretColumnNo is set
CaretColumnNo = x1
Else
'delete lines between y1 and y2
Dim j As Integer
For j = 1 To (y2 - y1 - 1)
model.RemoveLine(y1 + 1, False) 'false means "do not raise event"
Next
'merge line y1 with line y2 and delete the original line y2
Dim thisLine As String = model.GetLine(y1)
Dim nextLine As String = model.GetLine(y1 + 1)
model.SetLine(y1, thisLine.Substring(0, x1 - 1) & _
nextLine.Substring(x2 - 1, nextLine.Length - x2 + 1))
model.RemoveLine(y1 + 1, True)
' CaretLineNo must be adjusted before CaretColumnNo because
' CaretColumnNo property will use CaretLineNo. Therefore, it
' is important that CaretLineNo contains the correct value
CaretLineNo = y1
CaretColumnNo = x1
End If
End Sub
Protected Overrides Function ProcessDialogKey(ByVal keyData As Keys) As Boolean
keyProcessed = True
Select Case keyData
Case Keys.Down
ResetSelection()
If Not IsCaretOnLastLine() Then
If IsCaretOnLastVisibleLine() Then
view.Scroll(1)
End If
CaretLineNo = CaretLineNo + 1
If CaretColumnNo > GetCurrentLine().Length + 1 Then
CaretColumnNo = GetCurrentLine().Length + 1
End If
ScrollToShowCaret()
RedrawAll()
End If
Return True
Case Keys.Up
ResetSelection()
If Not IsCaretOnFirstLine() Then
If IsCaretOnFirstVisibleLine() Then
view.Scroll(-1)
End If
CaretLineNo = CaretLineNo - 1
If CaretColumnNo > GetCurrentLine().Length + 1 Then
CaretColumnNo = GetCurrentLine().Length + 1
End If
ScrollToShowCaret()
RedrawAll()
End If
Return True
Case Keys.Right
ResetSelection()
If IsCaretOnLastColumn() Then
If Not IsCaretOnLastLine() Then
If IsCaretOnLastVisibleLine() Then
view.Scroll(1)
End If
CaretLineNo = CaretLineNo + 1
CaretColumnNo = 1
End If
Else
CaretColumnNo = CaretColumnNo + 1
End If
ScrollToShowCaret()
RedrawAll()
Return True
Case Keys.Left
ResetSelection()
If IsCaretOnFirstColumn() Then
If Not IsCaretOnFirstLine() Then
If IsCaretOnFirstVisibleLine() Then
view.Scroll(-1)
End If
CaretLineNo = CaretLineNo - 1
CaretColumnNo = GetCurrentLine().Length + 1
End If
Else
CaretColumnNo = CaretColumnNo - 1
End If
ScrollToShowCaret()
RedrawAll()
Return True
Case Keys.Delete
'Deleting character does not change caret position but
'may change the longest line char count
If HasSelection() Then
RemoveSelection()
ResetSelection()
' then don't delete anything
Else
If CaretColumnNo = GetCurrentLine().Length + 1 Then
' at the end of line
If CaretLineNo < model.LineCount Then
'concatenate current line and next line
'and delete next line
Dim nextLine As String = model.GetLine(CaretLineNo + 1)
model.SetLine(CaretLineNo, GetCurrentLine() & nextLine)
model.RemoveLine(CaretLineNo + 1, True)
End If
Else
'delete one character
model.DeleteChar(caretLocation)
End If
End If
RedrawAll()
Return True
Case Else
If CInt(Keys.Control And keyData) = 0 And _
CInt(Keys.Alt And keyData) = 0 Then
' let KeyPress process the key
keyProcessed = False
End If
Return MyBase.ProcessDialogKey(keyData)
End Select
End Function
Private Sub ScrollToShowCaret()
If Not view.IsCaretVisible() Then
If model.GetLine(CaretLineNo).Length > view.VisibleCharCount Then
view.LeftInvisibleCharCount = GetCurrentLine().Length - _
view.VisibleCharCount
Else
view.LeftInvisibleCharCount = 0
End If
If CaretLineNo > TopInvisibleLineCount + view.VisibleLineCount Then
TopInvisibleLineCount = CaretLineNo - view.VisibleLineCount + 1
End If
End If
End Sub
Private Function IsCaretOnFirstColumn() As Boolean
Return (CaretColumnNo = 1)
End Function
Private Function IsCaretOnLastColumn() As Boolean
Return (CaretColumnNo = GetCurrentLine().Length + 1)
End Function
Private Function IsCaretOnLastVisibleLine() As Boolean
Return (CaretLineNo = view.VisibleLineCount + TopInvisibleLineCount)
End Function
Private Function IsCaretOnLastLine() As Boolean
Return (CaretLineNo = model.LineCount)
End Function
Private Function IsCaretOnFirstLine() As Boolean
Return (CaretLineNo = 1)
End Function
Private Function IsCaretOnFirstVisibleLine() As Boolean
Return (CaretLineNo = TopInvisibleLineCount + 1)
End Function
Private Sub RedrawAll()
view.RedrawAll()
lineNumberView.RedrawAll()
AdjustVScrollBar()
AdjustHScrollBar()
End Sub
Private Sub Highlight(ByVal x1 As Integer, ByVal y1 As Integer, _
ByVal x2 As Integer, ByVal y2 As Integer)
'(x1, y1) is the starting column,line of highlight
'(x2, y2) is the end column,line of hightlight
'swap (x1,y1) and (x2,y2) if necessary
If y1 > y2 Or (y1 = y2 And x1 > x2) Then
Dim t As Integer
t = x1 : x1 = x2 : x2 = t
t = y1 : y1 = y2 : y2 = t
End If
selectionStartLocation.Column = x1
selectionStartLocation.Line = y1
selectionEndLocation.Column = x2
selectionEndLocation.Line = y2
End Sub
Public ReadOnly Property SelectedText() As String
Get
If HasSelection() Then
Dim x1, y1, x2, y2 As Integer
x1 = selectionStartLocation.Column
y1 = selectionStartLocation.Line
x2 = selectionEndLocation.Column
y2 = selectionEndLocation.Line
'swap if necessary
If y1 > y2 Or (y1 = y2 And x1 > x2) Then
Dim t As Integer
t = x1 : x1 = x2 : x2 = t
t = y1 : y1 = y2 : y2 = t
End If
If y1 = y2 Then
Return model.GetLine(y1).Substring(x1 - 1, x2 - x1)
Else
Dim sb As New StringBuilder(model.CharCount + 2 * model.LineCount)
Dim lineCount As Integer = model.LineCount
Dim i, lineNo As Integer
For i = y1 To y2
Dim thisLine As String = model.GetLine(i)
If i = y1 Then
sb.Append(thisLine.Substring(x1 - 1))
ElseIf i = y2 Then
sb.Append(Microsoft.VisualBasic.Constants.vbCrLf)
sb.Append(thisLine.Substring(0, x2 - 1))
Else
sb.Append(Microsoft.VisualBasic.Constants.vbCrLf)
sb.Append(thisLine)
End If
Next
Return sb.ToString()
End If
Else
Return ""
End If
End Get
End Property
Public Overrides Property Text() As String
Get
Dim sb As New StringBuilder(model.CharCount + 2 * model.LineCount)
Dim lineCount As Integer = model.LineCount
Dim i As Integer
For i = 1 To lineCount
Dim thisLine As String = model.GetLine(i)
sb.Append(thisLine)
If i < lineCount Then
sb.Append(Microsoft.VisualBasic.Constants.vbCrLf)
End If
Next
Return sb.ToString()
End Get
Set(ByVal s As String)
If Not s Is Nothing Then
Dim initialColumn As Integer = caretLocation.Column
Dim initialLine As Integer = caretLocation.Line
'remove all lines
Dim i As Integer
Dim lineCount As Integer = model.LineCount
For i = 2 To lineCount
model.RemoveLine(2, False)
Next
model.SetLine(1, "") ' don't remove the first line
caretLocation.Column = 1
caretLocation.Line = 1
caretLocation = model.InsertData(s, caretLocation)
If caretLocation.Column <> initialColumn Then
OnColumnChanged(New _
ColumnEventArgs(initialColumn, caretLocation.Column))
End If
If caretLocation.Line <> initialLine Then
OnLineChanged(New LineEventArgs(initialLine, caretLocation.Line))
End If
ResetSelection()
If Not view.IsCaretVisible() Then
ScrollToShowCaret()
End If
RedrawAll()
End If
End Set
End Property
Public Function IsInSelection(ByVal column As Integer, _
ByVal line As Integer) As Boolean
'indicate that the character at (column, line) is selected
If Not HasSelection() Then
Return False
Else
Dim x1, y1, x2, y2 As Integer
x1 = selectionStartLocation.Column
y1 = selectionStartLocation.Line
x2 = selectionEndLocation.Column
y2 = selectionEndLocation.Line
'swap if necessary to make (x2, y2) below (x1, y1)
If (y1 > y2) Or (y1 = y2 And x1 > x2) Then
Dim t As Integer
t = x2 : x2 = x1 : x1 = t
t = y2 : y2 = y1 : y1 = t
End If
If y2 > model.LineCount Then
y2 = model.LineCount
If y1 = y2 And x1 > x2 Then
Dim t As Integer
t = x2 : x2 = x1 : x1 = t
End If
End If
If line < y2 And line > y1 Then
Return True
ElseIf y1 = y2 And line = y1 And column >= x1 And column < x2 Then
'selection in one line
Return True
ElseIf line = y1 And line <> y2 And column >= x1 Then
Return True
ElseIf line = y2 And line <> y1 And column < x2 Then
Return True
Else
Return False
End If
End If
End Function
Public Sub Copy()
'copy selected text to clipboard
If HasSelection() Then
Clipboard.SetDataObject(SelectedText)
End If
End Sub
Public Sub Cut()
If HasSelection() Then
Copy()
RemoveSelection()
ResetSelection()
RedrawAll()
End If
End Sub
Public Sub Paste()
' In this method, CaretLocation's Line and Column fields
' are accessed without going through the CaretLineNo and CaretColumnNo
' properties so as not to raise the OnLineChanged and OnColumnChanged
' events repeatedly.
Dim buffer As IDataObject = Clipboard.GetDataObject()
If buffer.GetDataPresent(DataFormats.Text) Then
Dim initialColumn As Integer = caretLocation.Column
Dim initialLine As Integer = caretLocation.Line
If HasSelection() Then
RemoveSelection()
End If
Dim s As String = buffer.GetData(DataFormats.Text).ToString()
caretLocation = model.InsertData(s, caretLocation)
If caretLocation.Column <> initialColumn Then
OnColumnChanged(New ColumnEventArgs(initialColumn, caretLocation.Column))
End If
If caretLocation.Line <> initialLine Then
OnLineChanged(New LineEventArgs(initialLine, caretLocation.Line))
End If
If HasSelection() Then
ResetSelection()
End If
If Not view.IsCaretVisible() Then
ScrollToShowCaret()
End If
RedrawAll()
Else
'MsgBox("Incompatible data format")
End If
End Sub
Public Sub SelectAll()
selectionStartLocation.Column = 1
selectionStartLocation.Line = 1
selectionEndLocation.Column = model.GetLine(model.LineCount).Length + 1
selectionEndLocation.Line = model.LineCount
RedrawAll()
End Sub
Public Function GetCurrentLine() As String
'return the line where the caret is on
Return model.GetLine(CaretLineNo)
End Function
Protected Overrides Sub Finalize()
MyBase.Finalize()
End Sub
Private Sub InitializeComponent()
'
'StyledTextArea
'
Me.Name = "StyledTextArea"
End Sub
End Class
Option Strict On
Option Explicit On
Public Class Model
Private list As New ArrayList(1024) ' number of initial lines
Private longestLineCharCountField As Integer
Public Event LineCountChanged As LineCountEventHandler
Public Event LongestLineCharCountChanged As LongestLineEventHandler
Public Sub New()
list.Add("")
End Sub
Protected Overridable Sub OnLineCountChanged(ByVal e As LineCountEventArgs)
RaiseEvent LineCountChanged(Me, e)
End Sub
Protected Overridable Sub OnLongestLineCharCountChanged( _
ByVal e As LongestLineEventArgs)
RaiseEvent LongestLineCharCountChanged(Me, e)
End Sub
Public ReadOnly Property LongestLineCharCount() As Integer
Get
Dim lineCount As Integer = list.Count
If lineCount = 0 Then
Return 0
Else
Dim i, max As Integer
For i = 0 To lineCount - 1
Dim thisLineCharCount As Integer = CType(list.Item(i), String).Length
If thisLineCharCount > max Then
max = thisLineCharCount
End If
Next
Return max
End If
End Get
End Property
Public ReadOnly Property CharCount() As Integer
' the total of lengths of all lines, therefore newline is not counted
Get
Dim i, total As Integer
For i = 0 To LineCount - 1
total = total + list.Item(i).ToString().Length
Next i
Return total
End Get
End Property
Public ReadOnly Property LineCount() As Integer
Get
Return list.Count
End Get
End Property
Public Function GetLine(ByVal lineNo As Integer) As String 'lineNo is 1-based
If lineNo > 0 And lineNo <= list.Count Then
Return CType(list.Item(lineNo - 1), String)
Else
Throw New ArgumentOutOfRangeException()
End If
End Function
Public Function InsertData(ByVal data As String, _
ByVal insertLocation As ColumnLine) As ColumnLine
'delete vbLf character
data = data.Replace(Microsoft.VisualBasic.Constants.vbLf.ToString(), "")
Dim initialLongestLineCharCount As Integer = LongestLineCharCount
Dim x As Integer = insertLocation.Column
Dim y As Integer = insertLocation.Line
Dim returnInserted As Boolean
' data may contain carriage return character
Dim thisLine As String = GetLine(y)
Dim head As String = thisLine.Substring(0, x - 1)
Dim tail As String = thisLine.Substring(x - 1)
list.RemoveAt(y - 1)
Dim startIndex As Integer
Do While (startIndex >= 0)
Dim endIndex As Integer = _
data.IndexOf(Microsoft.VisualBasic.Constants.vbCr, startIndex)
Dim line As String
If endIndex = -1 Then
line = data.Substring(startIndex)
'don't use SetLine bec it can raise event
Dim newLine As String = head & line & tail
list.Insert(y - 1, newLine)
x = head.Length + line.Length + 1
startIndex = endIndex
Else
line = data.Substring(startIndex, endIndex - startIndex)
list.Insert(y - 1, head & line)
returnInserted = True
y = y + 1
x = 1
head = ""
startIndex = endIndex + 1 'without carriage return
End If
Loop
Dim currentCharCount As Integer = LongestLineCharCount
If initialLongestLineCharCount <> currentCharCount Then
OnLongestLineCharCountChanged(New LongestLineEventArgs(currentCharCount))
End If
If returnInserted Then
OnLineCountChanged(New LineCountEventArgs(LineCount))
End If
Return New ColumnLine(x, y)
End Function
Public Sub InsertLine(ByVal lineNo As Integer, ByVal line As String)
If lineNo > 0 And lineNo <= list.Count + 1 Then
Dim oldLongestLineCharCount As Integer = LongestLineCharCount
list.Insert(lineNo - 1, line)
OnLineCountChanged(New LineCountEventArgs(LineCount))
Dim newLongestLineCharCount As Integer = LongestLineCharCount
If oldLongestLineCharCount <> newLongestLineCharCount Then
OnLongestLineCharCountChanged(New _
LongestLineEventArgs(newLongestLineCharCount))
End If
End If
End Sub
Public Sub InsertChar(ByVal c As Char, ByVal insertLocation As ColumnLine)
Dim oldLongestLineCharCount As Integer = LongestLineCharCount
Dim oldLine As String = list.Item(insertLocation.Line - 1).ToString()
Dim newLine As String = _
oldLine.Insert(insertLocation.Column - 1, c.ToString())
list.Item(insertLocation.Line - 1) = newLine
Dim newLongestLineCharCount As Integer = LongestLineCharCount
If oldLongestLineCharCount <> newLongestLineCharCount Then
OnLongestLineCharCountChanged(New _
LongestLineEventArgs(newLongestLineCharCount))
End If
End Sub
Public Sub RemoveLine(ByVal lineNo As Integer, ByVal triggerEvent As Boolean)
If lineNo > 0 And lineNo <= list.Count Then
Dim oldLongestLineCharCount As Integer = LongestLineCharCount
list.RemoveAt(lineNo - 1)
If triggerEvent Then
Dim newLongestLineCharCount As Integer = LongestLineCharCount
If oldLongestLineCharCount <> newLongestLineCharCount Then
OnLongestLineCharCountChanged(New _
LongestLineEventArgs(newLongestLineCharCount))
End If
OnLineCountChanged(New LineCountEventArgs(LineCount))
End If
End If
End Sub
Public Sub SetLine(ByVal lineNo As Integer, ByVal line As String)
Dim oldLongestLineCharCount As Integer = LongestLineCharCount
If (lineNo > 0 And lineNo <= list.Count) Then
list.Item(lineNo - 1) = line
End If
Dim newLongestLineCharCount As Integer = LongestLineCharCount
If oldLongestLineCharCount <> newLongestLineCharCount Then
OnLongestLineCharCountChanged(New _
LongestLineEventArgs(newLongestLineCharCount))
End If
End Sub
Public Sub DeleteChar(ByVal deleteLocation As ColumnLine)
Dim oldLongestLineCharCount As Integer = LongestLineCharCount
list.Item(deleteLocation.Line - 1) = _
GetLine(deleteLocation.Line).Remove(deleteLocation.Column - 1, 1)
Dim newLongestLineCharCount As Integer = LongestLineCharCount
If oldLongestLineCharCount <> newLongestLineCharCount Then
OnLongestLineCharCountChanged(New _
LongestLineEventArgs(newLongestLineCharCount))
End If
End Sub
End Class
Option Strict On
Option Explicit On
Public Class LineNumberView : Inherits UserControl
Private model As model
Public controller As StyledTextArea
Public lineSpace As Integer = 2
Public fontFace As String = "Courier New"
Public Sub New(ByRef model As model)
Me.model = model
fontHeight = 10
Font = New Font(fontFace, fontHeight)
End Sub
Public ReadOnly Property VisibleLineCount() As Integer
Get
Return CInt(Me.Height / (lineSpace + FontHeight))
End Get
End Property
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
Dim graphics As Graphics = e.Graphics
Dim textBrush As SolidBrush = New SolidBrush(ForeColor)
Dim characterWidth As Integer = 8
Dim i, visibleLine As Integer
For i = 1 To Math.Min(model.LineCount, VisibleLineCount)
Dim number As Integer = i + controller.TopInvisibleLineCount
Dim x As Integer = Me.Width - characterWidth - _
(number).ToString().Length * characterWidth
Dim y As Integer = (i - 1) * (lineSpace + fontheight)
graphics.DrawString(number.ToString(), Font, textBrush, x, y)
Next i
End Sub
Public Sub RedrawAll()
Me.Invalidate()
Me.Update()
End Sub
End Class
Imports System
Imports System.ComponentModel
Imports System.Drawing
Imports System.Windows.Forms
Public Class Form1 : Inherits Form
Friend WithEvents textArea As StyledTextArea
Friend WithEvents findPattern As TextBox
Friend WithEvents findButton As Button
Friend WithEvents copyButton As Button
Friend WithEvents cutButton As Button
Friend WithEvents pasteButton As Button
Friend WithEvents selectButton As Button
Friend WithEvents Label1 As Label
Friend WithEvents caseCB As CheckBox
Friend WithEvents wholeWordCB As CheckBox
Friend WithEvents goUpCB As CheckBox
Private x1 As Integer = 1
Private y1 As Integer = 1
Private column As Integer = 1
Private line As Integer = 1
Public Sub New()
InitializeComponent()
End Sub
Private Sub textArea_ColumnChanged(ByVal sender As Object, _
ByVal e As ColumnEventArgs) Handles textArea.ColumnChanged
column = e.NewColumn
UpdateLabel()
End Sub
Private Sub textArea_LineChanged(ByVal sender As Object, _
ByVal e As LineEventArgs) Handles textArea.LineChanged
line = e.NewLine
UpdateLabel()
End Sub
Private Sub InitializeComponent()
Me.textArea = New StyledTextArea
Me.findPattern = New System.Windows.Forms.TextBox()
Me.findButton = New System.Windows.Forms.Button()
Me.caseCB = New System.Windows.Forms.CheckBox()
Me.wholeWordCB = New System.Windows.Forms.CheckBox()
Me.goUpCB = New System.Windows.Forms.CheckBox()
Me.copyButton = New System.Windows.Forms.Button()
Me.cutButton = New System.Windows.Forms.Button()
Me.pasteButton = New System.Windows.Forms.Button()
Me.selectButton = New System.Windows.Forms.Button()
Me.Label1 = New System.Windows.Forms.Label()
Me.SuspendLayout()
'
'textArea
'
Me.textArea.CaretColor = System.Drawing.Color.Red
Me.textArea.Edited = False
Me.textArea.HighlightBackColor = System.Drawing.Color.Black
Me.textArea.HighlightForeColor = System.Drawing.Color.White
Me.textArea.LineNumberBackColor = System.Drawing.Color.FromArgb(CType(240, Byte), CType(240, Byte), CType(240, Byte))
Me.textArea.LineNumberForeColor = System.Drawing.Color.DarkCyan
Me.textArea.Location = New System.Drawing.Point(5, 5)
Me.textArea.Name = "textArea"
Me.textArea.Size = New System.Drawing.Size(680, 345)
Me.textArea.TabIndex = 0
Me.textArea.TextBackColor = System.Drawing.Color.White
Me.textArea.TextForeColor = System.Drawing.Color.Black
'
'findPattern
'
Me.findPattern.Location = New System.Drawing.Point(392, 400)
Me.findPattern.Name = "findPattern"
Me.findPattern.Size = New System.Drawing.Size(128, 20)
Me.findPattern.TabIndex = 1
Me.findPattern.Text = ""
'
'findButton
'
Me.findButton.Location = New System.Drawing.Point(536, 392)
Me.findButton.Name = "findButton"
Me.findButton.Size = New System.Drawing.Size(128, 32)
Me.findButton.TabIndex = 2
Me.findButton.Text = "Find"
'
'caseCB
'
Me.caseCB.Location = New System.Drawing.Point(390, 368)
Me.caseCB.Name = "caseCB"
Me.caseCB.Size = New System.Drawing.Size(123, 14)
Me.caseCB.TabIndex = 3
Me.caseCB.Text = "Case sensitive"
'
'wholeWordCB
'
Me.wholeWordCB.Location = New System.Drawing.Point(505, 368)
Me.wholeWordCB.Name = "wholeWordCB"
Me.wholeWordCB.Size = New System.Drawing.Size(84, 14)
Me.wholeWordCB.TabIndex = 4
Me.wholeWordCB.Text = "WholeWord"
'
'goUpCB
'
Me.goUpCB.Location = New System.Drawing.Point(600, 368)
Me.goUpCB.Name = "goUpCB"
Me.goUpCB.Size = New System.Drawing.Size(96, 14)
Me.goUpCB.TabIndex = 5
Me.goUpCB.Text = "Go Up"
'
'copyButton
'
Me.copyButton.Location = New System.Drawing.Point(4, 392)
Me.copyButton.Name = "copyButton"
Me.copyButton.Size = New System.Drawing.Size(80, 32)
Me.copyButton.TabIndex = 6
Me.copyButton.Text = "Copy"
'
'cutButton
'
Me.cutButton.Location = New System.Drawing.Point(90, 392)
Me.cutButton.Name = "cutButton"
Me.cutButton.Size = New System.Drawing.Size(80, 32)
Me.cutButton.TabIndex = 7
Me.cutButton.Text = "Cut"
'
'pasteButton
'
Me.pasteButton.Location = New System.Drawing.Point(176, 392)
Me.pasteButton.Name = "pasteButton"
Me.pasteButton.Size = New System.Drawing.Size(80, 32)
Me.pasteButton.TabIndex = 8
Me.pasteButton.Text = "Paste"
'
'selectButton
'
Me.selectButton.Location = New System.Drawing.Point(280, 392)
Me.selectButton.Name = "selectButton"
Me.selectButton.Size = New System.Drawing.Size(80, 32)
Me.selectButton.TabIndex = 9
Me.selectButton.Text = "Select All"
'
'Label1
'
Me.Label1.Location = New System.Drawing.Point(6, 367)
Me.Label1.Name = "Label1"
Me.Label1.Size = New System.Drawing.Size(160, 24)
Me.Label1.TabIndex = 0
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(692, 433)
Me.Controls.AddRange(New System.Windows.Forms.Control() {Me.Label1, Me.selectButton, Me.pasteButton, Me.cutButton, Me.copyButton, Me.goUpCB, Me.textArea, Me.wholeWordCB, Me.caseCB, Me.findButton, Me.findPattern})
Me.Name = "Form1"
Me.Text = "Using StyledTextArea"
Me.ResumeLayout(False)
End Sub
Private Sub goUpCB_Click(ByVal sender As Object, ByVal e As EventArgs) _
Handles goUpCB.CheckedChanged
If goUpCB.Checked Then
y1 = textArea.LineCount
x1 = textArea.GetLine(y1).Length + 1
Else
x1 = 1
y1 = 1
End If
End Sub
Private Sub findButton_Click(ByVal sender As Object, ByVal e As EventArgs) _
Handles findButton.Click
Dim pattern As String = findPattern.Text
Dim p As ColumnLine = textArea.Find(pattern, x1, y1, caseCB.Checked, _
wholeWordCB.Checked, goUpCB.Checked)
If p.Equals(New ColumnLine(0, 0)) Then
'search not found
If goUpCB.Checked Then
y1 = textArea.LineCount
x1 = textArea.GetLine(y1).Length + 1
Else
y1 = 1
x1 = 1
End If
Else
If Not goUpCB.Checked Then
x1 = p.Column + 1
y1 = p.Line
Else
x1 = p.Column - findPattern.Text.Length - 1
If x1 <= 1 Then
x1 = 1
y1 = p.Line - 1
Else
y1 = p.Line
End If
End If
End If
End Sub
Private Sub copyButton_Click(ByVal sender As Object, ByVal e As EventArgs) _
Handles copyButton.Click
textArea.Copy()
textArea.Focus()
End Sub
Private Sub cutButton_Click(ByVal sender As Object, ByVal e As EventArgs) _
Handles cutButton.Click
textArea.Cut()
textArea.Focus()
End Sub
Private Sub pasteButton_Click(ByVal sender As Object, ByVal e As EventArgs) _
Handles pasteButton.Click
textArea.Paste()
textArea.Focus()
End Sub
Private Sub selectButton_Click(ByVal sender As Object, ByVal e As EventArgs) _
Handles selectButton.Click
textArea.SelectAll()
textArea.Focus()
End Sub
Private Sub UpdateLabel()
Label1.Text = "Ln: " & line & " Col: " & column
End Sub
<STAThread()> Shared Sub Main()
System.Windows.Forms.Application.Run(New Form1())
End Sub
End Class
Option Strict On
Option Explicit On
Public Delegate Sub ColumnEventHandler(ByVal sender As Object, _
ByVal e As ColumnEventArgs)
Public Class ColumnEventArgs : Inherits EventArgs
Private oldColumnField, newColumnField As Integer
Public Sub New(ByVal oldColumn As Integer, ByVal newColumn As Integer)
oldColumnField = oldColumn
newColumnField = newColumn
End Sub
Public ReadOnly Property OldColumn() As Integer
Get
Return oldColumnField
End Get
End Property
Public ReadOnly Property NewColumn() As Integer
Get
Return newColumnField
End Get
End Property
End Class
Public Delegate Sub LineEventHandler(ByVal sender As Object, _
ByVal e As LineEventArgs)
Public Class LineEventArgs : Inherits EventArgs
Private oldLineField, newLineField As Integer
Public Sub New(ByVal oldLine As Integer, ByVal newLine As Integer)
oldLineField = oldLine
newLineField = newLine
End Sub
Public ReadOnly Property OldLine() As Integer
Get
Return oldLineField
End Get
End Property
Public ReadOnly Property NewLine() As Integer
Get
Return newLineField
End Get
End Property
End Class
Public Delegate Sub LineCountEventHandler(ByVal sender As Object, _
ByVal e As LineCountEventArgs)
Public Class LineCountEventArgs : Inherits EventArgs
Private lineCountField As Integer ' the current line count
Public Sub New(ByVal lineCount As Integer)
lineCountField = lineCount
End Sub
Public ReadOnly Property LineCount() As Integer
Get
Return lineCountField
End Get
End Property
End Class
Public Delegate Sub LongestLineEventHandler(ByVal sender As Object, _
ByVal e As LongestLineEventArgs)
Public Class LongestLineEventArgs : Inherits EventArgs
Private charCountField As Integer
Public Sub New(ByVal charCount As Integer)
charCountField = charCount
End Sub
Public ReadOnly Property LongestLineCharCount() As Integer
Get
Return charCountField
End Get
End Property
End Class
Option Strict On
Option Explicit On
Imports System.Threading
Public Class View : Inherits UserControl
Public controller As StyledTextArea
Public lineSpace As Integer = 2 ' number of pixels between 2 lines
Public fontFace As String = "Courier New"
Public characterWidth As Integer = 8
Public model As Model
Private caretThread As Thread
Private caretVisible As Boolean = True
Private penWidth As Integer = 2
Public highlightBackColor As Color = Color.DarkBlue
Public highlightForeColor As Color = Color.White
Public LeftInvisibleCharCount As Integer
Private pen As New Pen(Color.Black, penWidth)
Public Property CaretColor() As Color
Get
Return pen.Color
End Get
Set(ByVal Value As Color)
pen.Color = Value
End Set
End Property
Public Sub New(ByRef Model As Model)
Me.model = Model
fontHeight = 10
Font = New Font(fontFace, fontHeight)
caretThread = New Thread(New ThreadStart(AddressOf DisplayCaret))
caretThread.Start()
End Sub
Public Function GetFontHeight() As Integer
Return fontHeight
End Function
Public ReadOnly Property VisibleCharCount() As Integer
Get
Return CInt(Math.Floor(Me.Width / characterWidth)) - 1
End Get
End Property
Public ReadOnly Property VisibleLineCount() As Integer
Get
Return CInt(Me.Height / (lineSpace + GetFontHeight()))
End Get
End Property
Public Sub Scroll(ByVal increment As Integer)
controller.TopInvisibleLineCount = _
controller.TopInvisibleLineCount + increment
If controller.TopInvisibleLineCount < 0 Then
controller.TopInvisibleLineCount = 0
End If
RedrawAll()
End Sub
Public Sub MoveScreen(ByVal increment As Integer)
'move screen horizontally by x character
LeftInvisibleCharCount = Math.Max(LeftInvisibleCharCount + increment, 0)
RedrawAll()
End Sub
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
caretThread.Abort()
caretThread.Join()
MyBase.Dispose(disposing)
End Sub
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
Dim graphics As Graphics = e.Graphics
Dim textBrush As SolidBrush = New SolidBrush(ForeColor)
Dim highlightTextBrush As SolidBrush = New SolidBrush(highlightForeColor)
If controller.HasSelection() Then
PaintSelectionArea(graphics)
End If
Dim i, visibleLine As Integer
Dim maxCharCount As Integer
For i = 1 To model.LineCount
Dim thisLine As String = model.GetLine(i)
If i > controller.TopInvisibleLineCount Then
Dim y As Integer = visibleLine * (lineSpace + fontHeight)
Dim j As Integer
For j = 1 To thisLine.Length
If j > LeftInvisibleCharCount And _
j <= LeftInvisibleCharCount + VisibleCharCount + 1 Then
Dim x As Integer = GetStringWidth(thisLine.Substring(0, j - 1)) - _
LeftInvisibleCharCount * characterWidth
If controller.IsInSelection(j, i) Then
graphics.DrawString(thisLine.Substring(j - 1, 1), Font, _
highlightTextBrush, x, y)
Else
graphics.DrawString(thisLine.Substring(j - 1, 1), Font, _
textBrush, x, y)
End If
End If
Next j
visibleLine = visibleLine + 1
End If
Next i
'draw caret here
DrawCaret(graphics)
End Sub
Protected Sub DrawCaret(ByRef graphics As Graphics)
'it's protected so that it can be overriden by subclass
If caretVisible And Me.Focused Then
'Measure string
Dim caretsLine As String = model.GetLine(controller.CaretLineNo)
Dim x As Integer = GetStringWidth(caretsLine.Substring(0, _
controller.CaretColumnNo - 1)) + _
penWidth - (LeftInvisibleCharCount * characterWidth)
Dim y As Integer = (controller.CaretLineNo - 1 - _
controller.TopInvisibleLineCount) * (lineSpace + fontHeight)
graphics.DrawLine(pen, x, y, x, y + lineSpace + fontHeight)
End If
End Sub
Private Sub PaintSelectionArea(ByRef graphics As Graphics)
Dim brush As New SolidBrush(highlightBackColor)
' representing start and end coordinates of selected text
Dim x1, y1, x2, y2 As Integer
Dim p1, p2 As ColumnLine
p1 = controller.selectionStartLocation
p2 = controller.selectionEndLocation
x1 = p1.Column : y1 = p1.Line
x2 = p2.Column : y2 = p2.Line
If y1 > y2 Or (y1 = y2 And x1 > x2) Then
'swap
Dim t As Integer
t = y1 : y1 = y2 : y2 = t
t = x1 : x1 = x2 : x2 = t
End If
Dim i As Integer
Dim beginLine As Integer = Math.Max(y1, 1)
Dim endLine As Integer = Math.Min(y2, model.LineCount)
If beginLine = endLine Then
If x1 > x2 Then
Dim t As Integer
t = x1 : x1 = x2 : x2 = t
End If
Dim thisLine As String = model.GetLine(beginLine)
graphics.FillRectangle(brush, _
2 + GetStringWidth(thisLine.Substring(0, x1 - 1)) - _
(LeftInvisibleCharCount * characterWidth), _
(beginLine - 1 - controller.TopInvisibleLineCount) * _
(lineSpace + fontHeight), _
GetStringWidth(thisLine.Substring(x1 - 1, x2 - x1)), _
(lineSpace + fontHeight))
Else
For i = beginLine To endLine
Dim thisLine As String = model.GetLine(i)
If i = beginLine Then
' first line may not be the whole line,
' but from initial position of selection to end of string
graphics.FillRectangle(brush, _
2 + GetStringWidth(thisLine.Substring(0, x1 - 1)) - _
LeftInvisibleCharCount * characterWidth, _
(i - 1 - controller.TopInvisibleLineCount) * _
(lineSpace + fontHeight), GetStringWidth(thisLine) - _
GetStringWidth(thisLine.Substring(0, x1 - 1)), _
(lineSpace + fontHeight))
ElseIf i = endLine Then
graphics.FillRectangle(brush, _
2 - LeftInvisibleCharCount * characterWidth, _
(i - 1 - controller.TopInvisibleLineCount) * _
(lineSpace + fontHeight), _
GetStringWidth(thisLine.Substring(0, x2 - 1)), _
(lineSpace + fontHeight))
Else
' last line may not be the whole line,
' but from first column to initial position of selection
graphics.FillRectangle(brush, _
2 - LeftInvisibleCharCount * characterWidth, _
(i - 1 - controller.TopInvisibleLineCount) * _
(lineSpace + fontHeight), GetStringWidth(thisLine), _
(lineSpace + fontHeight))
End If
Next i
End If
'don't dispose graphics!!
End Sub
Private Function GetStringWidth(ByRef s As String) As Integer
If Not s Is Nothing Then
Return s.Length * characterWidth
Else
Return 0
End If
End Function
Public Sub RedrawAll()
'before redraw correct invisible line count
controller.TopInvisibleLineCount = _
Math.Min(controller.TopInvisibleLineCount, _
model.LineCount - VisibleLineCount)
If controller.TopInvisibleLineCount < 0 Then
controller.TopInvisibleLineCount = 0
End If
LeftInvisibleCharCount = Math.Min(LeftInvisibleCharCount, _
model.LongestLineCharCount - VisibleCharCount)
If LeftInvisibleCharCount < 0 Then LeftInvisibleCharCount = 0
Me.Invalidate()
Me.Update()
End Sub
Private Sub DisplayCaret()
Try
While True
' call DrawCaret here
Dim caretsLine As String = model.GetLine(controller.CaretLineNo)
Dim x As Integer = GetStringWidth(caretsLine.Substring(0, _
controller.CaretColumnNo - 1)) + _
penWidth - (LeftInvisibleCharCount * characterWidth)
Dim y As Integer = (controller.CaretLineNo - 1 - _
controller.TopInvisibleLineCount) * (lineSpace + fontHeight)
Dim caretRectangle As New Rectangle( _
x - penWidth, y, 2 * penWidth, lineSpace + fontheight)
Me.Invalidate(caretRectangle)
Me.Update()
If Not caretVisible Then
Thread.Sleep(150)
Else
Thread.Sleep(350)
End If
caretVisible = Not caretVisible
End While
Catch
End Try
End Sub
Public Function TranslateIntoCaretLocation( _
ByVal x1 As Integer, ByVal y1 As Integer) As ColumnLine
Dim column, line As Integer ' the coordinate for the returned Point
'set lowest value for y1 in case the use keeps dragging above the control
If y1 < 1 Then
y1 = 1
End If
'Get the visible line number
line = Math.Min(1 + controller.TopInvisibleLineCount + _
CInt(y1 / (fontHeight + lineSpace)), model.LineCount)
'Now calculate the closest position of the character in the current line
Dim thisLine As String = model.GetLine(line)
Dim i As Integer, minDistance As Single = Me.Width 'the width of this control
Dim j As Integer = 0
For i = 0 To thisLine.Length
Dim distance As Single = _
Math.Abs(x1 + LeftInvisibleCharCount * characterWidth - _
GetStringWidth(thisLine.Substring(0, i)))
If distance < minDistance Then
minDistance = distance
j = i
End If
Next i
column = j + 1
Return New ColumnLine(column, line)
End Function
Public Sub RepositionCaret(ByVal x As Integer, ByVal y As Integer)
'Get the (visible) line number
Dim lineNumber As Integer = 1 + CInt(y / (fontHeight + lineSpace))
controller.CaretLineNo = Math.Min(lineNumber + _
controller.TopInvisibleLineCount, model.LineCount)
'Now calculate the closest position of the character in the current line
Dim thisLine As String = controller.GetCurrentLine()
Dim i As Integer, minDistance As Single = Width ' the width of this control
Dim j As Integer = 0
For i = 0 To thisLine.Length
Dim distance As Integer = _
Math.Abs(x + LeftInvisibleCharCount * characterWidth - _
GetStringWidth(thisLine.Substring(0, i)))
If distance < minDistance Then
minDistance = distance
j = i
End If
Next i
controller.CaretColumnNo = j + 1
RedrawAll()
End Sub
Public Function GetCaretXAbsolutePosition() As Integer
Dim caretsLine As String = controller.GetCurrentLine()
If Not caretsLine Is Nothing Then
Return _
GetStringWidth(caretsLine.Substring(0, controller.CaretColumnNo - 1))
Else
Return 0
End If
End Function
Public Function IsCaretVisible() As Boolean
Dim xPosition As Integer = GetCaretXAbsolutePosition()
Dim leftInvisibleWidth As Integer = LeftInvisibleCharCount * characterWidth
If xPosition < leftInvisibleWidth Or _
xPosition > leftInvisibleWidth + Me.Width - 5 Or _
controller.CaretLineNo > _
controller.TopInvisibleLineCount + VisibleLineCount Then
Return False
Else
Return True
End If
End Function
Private Sub InitializeComponent()
'
'View
'
Me.Name = "View"
controller.TopInvisibleLineCount = 10
End Sub
End Class
Last edited by a moderator: