Set the exact position of a Textbox's Scrollbar - vb.net

I have a multi-line textbox with both its horizontal and vertical scrollbars visible, and WordWrap set to False. I load a large textfile in the textbox to allow the user to edit it.
The editing features automation which requires me to store the value of the entire textbox into a value, process it and then store it back into the textbox. This all is working great, but by setting a string into the textbox, it first clears it and then fills it again. This action sets the scrollbar position to the top-left corner.
I have tried to use Textbox1.scrolltocaret in my update routine which makes the cursor visible again, but it doesn't scroll to the exact position the control was set to before.
Also, I can't find out how to call textbox1.scrolltocaret every time the user moves using the arrowkey.
How can I store and restore the scrollbar locations?
Here's my code:
Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
If Me.bPauseUpdate = True Then Exit Sub
Me.bPauseUpdate = True
Me.CursorLocation = TextBox1.SelectionStart
Dim aWorkingText() As String = Me.TextBox1.Lines
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Code that changes aWorkingText lives here, but is not relevant for this question
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
TextBox1.Lines = aWorkingText
Me.bPauseUpdate = False
TextBox1.SelectionStart = Me.CursorLocation
Textbox1.scrolltocaret
End Sub
I looked into using Textbox1.Scrollbars but they are only for controlling whether the scrollbars are visible.
I tried to make it work with the following code, but that does not allow me to actually set the value even though the IDE says I should be able to do so:
Me.ScrollbarX = TextBox1.AutoScrollOffset.X
Me.ScrollbarY = TextBox1.AutoScrollOffset.Y
.
.
.
TextBox1.AutoScrollOffset.X = Me.ScrollbarX
TextBox1.AutoScrollOffset.Y = Me.ScrollbarY
I even tried this, but it yields the same result.
TextBox1.AutoScrollOffset.X = New Point(Me.ScrollBarX)
TextBox1.AutoScrollOffset.Y = New Point(Me.ScrollBarY)

You can use the GetScrollPos Win32 API to get the current vertical and horizontal scrollbar position of the TextBox control.
Imports System.Runtime.InteropServices
<DllImport("user32.dll", CharSet:=CharSet.Auto)> Friend Shared Function GetScrollPos(hWnd As IntPtr, nBar As Integer) As Integer
End Function
Friend Enum SBOrientation As Integer
SB_HORZ = &H0
SB_VERT = &H1
End Enum
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Horizontal scroll position
GetScrollPos(TextBox1.Handle, SBOrientation.SB_HORZ)
'Vertical scroll position
GetScrollPos(TextBox1.Handle, SBOrientation.SB_VERT)
End Sub
Use the SendMessage Win32 API to set the TextBox scroll position
<DllImport("user32.dll")> Friend Shared Function SendMessage(hWnd As IntPtr, msg As UInteger, wParam As UInteger, lParam As UInteger) As IntPtr
End Function
Friend Const SB_THUMBPOSITION As UInteger = 4
Friend Enum WindowMessage As Integer
WM_HSCROLL = &H114
WM_VSCROLL = &H115
End Enum
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Set Vertical scroll position
Dim vPos = 16
Dim wparam1 = CUInt(vPos) << 16 Or (SB_THUMBPOSITION And &HFFFF)
SendMessage(TextBox1.Handle, CUInt(WindowMessage.WM_VSCROLL), CUInt(wparam1), CUInt(0))
'Set Horizontal scroll position
Dim hPos = 100
Dim wparam2 = CUInt(hPos) << 16 Or (SB_THUMBPOSITION And &HFFFF)
SendMessage(TextBox1.Handle, CUInt(WindowMessage.WM_HSCROLL), CUInt(wparam2), CUInt(0))
End Sub

Related

How to stop a timer when mouse is scrolling or on top of scrollbar in listbox

I'm looking for a way to detect and switch off a timer when the mouse cursor is scrolling a listbox.
There is an easy way despite to create a new class like this one?link
Would be possible to check rectangle location of listbox 1 scroll bar and say: if mouse is in this range then timer1.stop?
EDIT1:
In order to create a rectangle I'm using
If e.X >= 364 AndAlso e.X <= 446 AndAlso e.Y >= 86 AndAlso e.Y <= 144 Then
MessageBox.Show("Clicked within the rectangle")
Else
MessageBox.Show("Clicked outside the rectangle")
End If
449-359 are the Top left corner location of the rectangle
while the rectangle size is x30 y156
The problem is I don't know in which event let it run!
Listbox click event doesn't recognize scrollbar as "inside of listbox"
Form_mouse click event doesn't recognize listbox scroll bar as a click in the form.
There is an event that despite the control you are on, it will let you play with this workaround?
Thanks
Here is what I posted on MSDN using this C# code. There is no code presented below that will restart the Timer.
Public Class BetterListBox
Inherits ListBox
' Event declaration
Public Delegate Sub BetterListBoxScrollDelegate(ByVal Sender As Object, ByVal e As BetterListBoxScrollArgs)
Public Event Scroll As BetterListBoxScrollDelegate
' WM_VSCROLL message constants
Private Const WM_VSCROLL As Integer = &H115
Private Const SB_THUMBTRACK As Integer = 5
Private Const SB_ENDSCROLL As Integer = 8
Protected Overrides Sub WndProc(ByRef m As Message)
' Trap the WM_VSCROLL message to generate the Scroll event
MyBase.WndProc(m)
If m.Msg = WM_VSCROLL Then
Dim nfy As Integer = m.WParam.ToInt32() And &HFFFF
If (nfy = SB_THUMBTRACK OrElse nfy = SB_ENDSCROLL) Then
RaiseEvent Scroll(Me, New BetterListBoxScrollArgs(Me.TopIndex, nfy = SB_THUMBTRACK))
End If
End If
End Sub
Public Class BetterListBoxScrollArgs
' Scroll event argument
Private mTop As Integer
Private mTracking As Boolean
Public Sub New(ByVal top As Integer, ByVal tracking As Boolean)
mTop = top
mTracking = tracking
End Sub
Public ReadOnly Property Top() As Integer
Get
Return mTop
End Get
End Property
Public ReadOnly Property Tracking() As Boolean
Get
Return mTracking
End Get
End Property
End Class
End Class
Then in your form subscribe to the Scroll event. Requires the ListBox above in your project, one Timer enabled and a Label.
Private Sub BetterListBox1_Scroll(Sender As Object, e As BetterListBox.BetterListBoxScrollArgs) _
Handles BetterListBox1.Scroll
Timer1.Enabled = False
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Label1.Text = Now.ToString()
End Sub

How to load a specific text in textbox with greyish colour and uneditable [duplicate]

This question already has answers here:
Watermark TextBox in WinForms
(11 answers)
Creating a TextBox with watermark using ControlStyles.UserPaint shows the watermark just once at component creation
(2 answers)
Closed 2 years ago.
I am creating a login form by using Visual Basic. Is it possible for me to load a specific text in a textbox with greyish color and uneditable? (Just like the effect in Youtube search bar) What I manage to found on the internet is just hide function and change the color of the text.
Sorry for my confusing title.
Add this Module (ModExtentions) to the Project: it adds an extension method, SetCueBanner(), to TextBox Controls.
Specifying True or False, changes the cue banner behavior:
False: the cue banner is visible until the control gets focus,
True, the cue banner is visible until the first char is entered.
This internal functionality is activated sending an EM_SETCUEBANNER message to the Control.
Use it like this:
' The Cue Banner is visible until the control gets focus
TextBox1.SetCueBanner("Some text...", False)
' The Cue Banner is visible until a character is entered
TextBox1.SetCueBanner("Some text...", True)
The Module where the extension method is defined:
Imports System.Runtime.InteropServices
Public Module ModExtentions
Private Const EM_SETCUEBANNER As Integer = &H1501
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Private Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Integer, ByVal wParam As Integer, ByVal lParam As String) As Integer
End Function
<Extension()>
Public Sub SetCueBanner(tbox As TextBox, ByVal text As String, ByVal showOnFocus As Boolean)
SendMessage(tbox.Handle, EM_SETCUEBANNER, If(showOnFocus, 1, 0), text)
End Sub
End Module
Set the TextBox Enabled property to false. You can put in code from code but the user can't type in the box.
Set up your gray text in the Form.Load. The use the Enter and Leave events to change it to normal and back again.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TextBox1.ForeColor = Color.Gray
TextBox1.Text = "Search term here..."
End Sub
Private Sub TextBox1_Enter(sender As Object, e As EventArgs) Handles TextBox1.Enter
TextBox1.ForeColor = Color.Black
TextBox1.Text = ""
End Sub
Private Sub TextBox1_Leave(sender As Object, e As EventArgs) Handles TextBox1.Leave
If String.IsNullOrWhiteSpace(TextBox1.Text) Then
TextBox1.ForeColor = Color.Gray
TextBox1.Text = "Search term here..."
End If
End Sub

WM_SETREDRAW in RichTextBox fails in .NET Framework 4.7.2 but works in 4.6.2

I have a control that inherits from RichTextBox and the idea is, that the user types in numbers per line and the control colors each line read or green depending on if the text in the line is a valid number.
On TextChanged each line is checked and by applying SelectionColor the color of each line is adjusted accordingly. Please find the full code below.
In order to keep that somewhat speedy I suspend redrawing of the control before updating the colors by sending the WM_SETREDRAW message, as is described in various questions/answers on this site, e.g. this one about RichTextBox in particular.
I have used this for quite a while without issue but recently reused the control in a project that targets .NET Framework 4.7.2 instead of older frameworks like 4.5 or 4.6.
Here I run into the problem of graphical glitches as soon as the control contains enough lines to cause scrolling. The colors are not applied correctly, ghost cursors appear and so on. I tried to show that in the picture:
The only thing I changed between the second and third picture was to change the target framework to 4.6.2 and recompile.
Does someone have an idea what causes this and how to work around the problem?
Please feel free to use VB or C# to your taste, I don't judge :-)
Here is the code of the control I used to reproduce the behavior:
Public Class RichtextNumberlist
Inherits RichTextBox
Private Sub tbValues_TextChanged(sender As Object, e As EventArgs) Handles Me.TextChanged
SuspendDrawing(Me)
UpdateColor(Me)
ResumeDrawing(Me)
End Sub
<DllImport("user32.dll")>
Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As Boolean, ByVal lParam As IntPtr) As Integer
End Function
Private Const WM_SETREDRAW As Integer = 11
Public Sub SuspendDrawing(ByVal Target As Control)
SendMessage(Me.Handle, WM_SETREDRAW, False, 0)
End Sub
Public Sub ResumeDrawing(ByVal Target As Control)
SendMessage(Me.Handle, WM_SETREDRAW, True, 0)
Target.Invalidate()
End Sub
Private Function CheckLine(line As String) As Boolean
Dim d As Double
Return Double.TryParse(line, d)
End Function
Private Sub UpdateColor(tbValues As RichTextBox)
Dim t = Threading.Thread.CurrentThread
Console.WriteLine($"Updating color from thread STA={t.GetApartmentState().ToString()}, BG={t.IsBackground}")
'Save selection parameters to reset them afterwards
Dim oldsel As Integer = tbValues.SelectionStart
Dim oldsell As Integer = tbValues.SelectionLength
Dim pos As Integer = 0
Dim lCount = tbValues.Lines.Count
Dim lines = tbValues.Lines
For i = 0 To lCount - 1
'Set selection on the ith line
tbValues.SelectionStart = pos
tbValues.SelectionLength = lines(i).Length
Dim lineok = CheckLine(lines(i))
'Adjust the color accordingly
If lineok Then
tbValues.SelectionColor = Color.ForestGreen
Else
tbValues.SelectionColor = Color.Red
End If
'Move forward
pos += lines(i).Length + 1
Next
'Reset the selection
tbValues.SelectionStart = oldsel
tbValues.SelectionLength = oldsell
End Sub
End Class

How would I go about creating a textbox help label (cuebanner/cuetext)?

I wanted to create a textbox which has a help label inside the box which then disappears when the box has characters entered into it. I found one way of doing it which involves loading the form with text inside the textbox in the colour grey and then removing it when the user clicks on the box... The problem with this is i wanted to use a string.IsNullOrEmpty(textboxIP) but when the user hasn't typed anything into the box, the program sees the box as not empty as it has the pre-loaded writing in it. This is the code I used to remove the text on user click...
Dim WatermarkIP As String = "Yes"
Dim WatermarkPing As String = "Yes"
Private Sub textboxIP_Enter(sender As Object, e As EventArgs) Handles textboxIP.Enter
If WatermarkIP = "Yes" Then
textboxIP.Clear()
textboxIP.ForeColor = Color.Black
WatermarkIP = "No"
End If
End Sub
Private Sub textboxPing_Enter(sender As Object, e As EventArgs) Handles textboxPing.Enter
If WatermarkPing = "Yes" Then
textboxPing.Clear()
textboxPing.ForeColor = Color.Black
WatermarkPing = "No"
End If
End Sub
Does anyone know of a better way of creating a greyed out help/hint label inside the textbox which IS NOT counted as text inside the box, does not have to be deleted by the user before they can type in the box and is maybe a bit simpler?
I've found it finally!
This will give you that watermark text on your textbox controls:
Imports:
Imports System.Runtime.InteropServices
Global Declarations in your main class:
Private Const EM_SETCUEBANNER As Integer = &H1501
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Integer, ByVal wParam As Integer, <MarshalAs(UnmanagedType.LPWStr)> ByVal lParam As String) As Int32
End Function
Functions in your main class:
Private Sub SetCueText(ByVal control As Control, ByVal text As String)
SendMessage(control.Handle, EM_SETCUEBANNER, 0, text)
End Sub
Usage (usually in form_load event):
SetCueText(TextBox1, "blah")
SetCueText(TextBox2, "blahblah")
Hope this helps :)
Credit: http://www.vbforums.com/showthread.php?638105-CueBanner-Watermark-text-for-Textboxes

Vertical ScrollBar does not repaint using EnableScrollBar api

I am trying to disable a VScrollBar control using the EnableScrollBar api. When I call the api it returns as if no problems ocurred but the VScrollBar is not repainted.
To reproduce the problem create a Vb.Net windows forms project, drop a VScrollBar control and a button to the form and paste the following code:
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True, ExactSpelling:=True)>
Public Shared Function EnableScrollBar(ByVal hWnd As IntPtr, ByVal nBar As Integer, ByVal value As Integer) As Boolean
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim wSBflags As UInteger = 3UI 'SB_VERT
Dim wArrows As UInteger = 3UI 'ESB_DISABLE_BOTH
Dim result As Boolean = EnableScrollBar(Me.VScrollBar1.Handle, wSBflags, wArrows)
End Sub
I tried using SendMessage to send a redraw (WM_REDRAW) and a paint (WM_PAINT) but cant get it to work. Any ideias?
Ps: If you drop a multiline textbox and use the same code it works....
SB_VERT is for the vertical scrollbar as part of the non-client area of a window. For a scroll bar control, use the SB_CTL constant.