Vb.Net: Limit the total width of characters entered into a textbox? - vb.net

Let me be very clear - I am not looking for a static character limit here (Textbox.MaxLength just doesn't cut it.)
I'm making a simple messaging program and I'd like to implement a character limit. The messages are displayed inside a listbox and can't be horizontally scrolled/wrapped. The solution: impose a limit on every message so that users don't accidentally cut off their own messages.
The problem is that 10 small characters are a lot smaller than 10 full width characters. - E.G. i and W:
iiiiiiiiii
WWWWWWWWWW
I'd like to find a way to limit the characters entered into the text box by the actual amount of pixels the string is wide.
so that:
nobody can use all capitals and get cut off, and
nobody can type normally and be stopped by the character limit far earlier than neccesary.
For reference, I'm using Verdana 8.25pt for the listbox.
Any suggestions would be appreciated,
Thanks.

This should do the trick for you. I've deliberately chosen the keyUp event because the user will see the letter typed and it disappearing.
Private Sub TextBox1_TextChanged(sender As Object, e As KeyEventArgs) Handles TextBox1.KeyUp
Dim text1 As String = TextBox1.Text
Dim textboxFont As Font = TextBox1.Font
Dim textSize As Size = TextRenderer.MeasureText(text1, textboxFont)
If textSize.Width > TextBox1.ClientRectangle.Width Then
Dim cursorLocation As Integer = TextBox1.SelectionStart
TextBox1.Text = TextBox1.Text.Remove(cursorLocation - 1, 1)
If cursorLocation > TextBox1.Text.Length Then
TextBox1.SelectionStart = TextBox1.Text.Length
Else
TextBox1.SelectionStart = cursorLocation
End If
End If
End Sub
Basically what is happening is that the text is rendered (not displayed) using the font of the textbox and measured. If the width of the rendered text is greater than the client area of the textbox, the letter is removed at the point it was typed. This can be anywhere in the text box.
If the cursor is at the end of the text when the letter is removed, .net automatically sets the cursor position to the beginning is a letter is removed. So this sub checks if the initial cursor position was a bigger index than the length of the new textbox contents. If so, the cursor is set to the end again. Otherwise it is moved back 1 because a character was deleted.

Related

Font Family and Font Size Combo Box Coding Bugs

it's been a while since I posted. Haven't had much time for programming for a while. Just got back into it a couple of days ago. I'm working on a personal project which is a kind of Project Manager with a fairly complete text editor. I am working with VB.NET in the Visual Studio IDE. I have been trying to mimic the behaviour of Word and other editors as follows:
FONT FAMILY: This code actually works to change the Font Family in the Font Picker Combo Box in real time according to text formatting at cursor position. i.e. click on different text formats in the Rich Text Box and the Font Family is displayed in the Combo Box! I am using a Tool Strip Combo Box!
BUG: The Font Family code (i.e. penultimate snippet) removes the formatting from text when it is reselected after formatting?
FONT SIZE: This code changes the Font Size in the Font Size Combo Box when Ctrl+Shift+< OR Ctrl+Shift+> keyboard shortcuts are used.
BUG: The Font Size code (i.e. Last snippet) affects Font Size when using the ,.<> keys because these keys are configured in the code to alter the value of the Font size in the Font Size ComboBox. So, pressing the keys causes the Font Size in the Combo Box to increase or decrease which also means the font in the RTB increases or decreases?!
WHAT I NEED: 1. Selecting formatted text should not remove formatting, but Font Family should be displayed in Combo Box according to formatting in the RTB. 2. The Font Size Combo Box value should only be altered when I use the keyboard shortcuts, Ctrl+Shift+< OR Ctrl+Shift+> to display the true Font Size in real time. Using the current code increments/reduces in steps of 2 rather than one Font Size at a time??? I realize this code is fairly simple and understand roughly why it's not working, but I can't seem to find a way to fix the issue? Any help would be greatly appreciated!
WHAT I HAVE TRIED
Rich Text Box (i.e. RTB = Description):
FONT FAMILY COMBO BOX CODE: (uses a timer)
Private Sub tbSelectFont_SelectedIndexChanged(sender As Object, e As EventArgs) Handles tbSelectFont.SelectedIndexChanged
Dim NewFont As New Font(tbSelectFont.SelectedItem.ToString(),
Description.SelectionFont.Size,
Description.SelectionFont.Style)
Description.SelectionFont = NewFont
End Sub
FONT SIZE COMBO BOX CODE:
Private Sub tbSelectSize_SelectedIndexChanged(sender As Object, e As EventArgs) Handles tbSelectSize.SelectedIndexChanged
Dim NewSize As Single = tbSelectSize.SelectedItem
Dim NewFont As New Font(Description.SelectionFont.Name, NewSize, Description.SelectionFont.Style)
Description.SelectionFont = NewFont
End Sub
RELATED FORM LOAD EVENTS:
'Display Installed Fonts In Font Picker (tbSelectFont):
Dim fonts As New InstalledFontCollection()
For fntFamily As Integer = 0 To fonts.Families.Length - 1
tbSelectFont.Items.Add(fonts.Families(fntFamily).Name)
Next
'Display Font Size in Font Size Picker:
For fntSize = 10 To 75
tbSelectSize.Items.Add(fntSize)
Next
FONT FAMILY:
Private Sub Description_SelectionChanged(sender As Object, e As EventArgs) Handles Description.SelectionChanged
If Description.SelectionFont IsNot Nothing Then
Dim fontSize As Single = Description.SelectionFont.Size
tbSelectSize.Text = fontSize
tbSelectFont.Text = Description.SelectionFont.Name
End If
End Sub
FONT SIZE:
If (Keys.Control AndAlso Keys.Shift AndAlso e.KeyCode = Keys.Oemcomma) Then
tbSelectSize.Text -= 1
ElseIf (Keys.Control AndAlso Keys.Shift AndAlso e.KeyCode = Keys.OemPeriod) Then
tbSelectSize.Text += 1
End If
OTHER CODE I TRIED:
THIS CODE WORKS, BUT WHEN YOU SELECT FORMATTED TEXT IT THROWS AN EXCEPTION OR REMOVES FORMATTING: System.NullReferenceException: 'Object reference not set to an instance of an object.'
SOURCE: MY OWN COMMENT FROM:
how to get the fontsize of a certain line in a richtextbox in c# using winforms
Dim fontName As String = Description.SelectionFont.Name
Dim fontSize As Single = Description.SelectionFont.Size
tbSelectFont.Text = fontName
tbSelectSize.Text = fontSize
tbSelectFont.SelectedIndex = tbSelectFont.FindStringExact(Description.SelectionFont.Name)
tbSelectSize.SelectedIndex = tbSelectSize.FindStringExact(Description.SelectionFont.Size)
THIS CODE WORKS, BUT WHEN YOU SELECT FORMATTED TEXT IT THROWS AN EXCEPTION: System.NullReferenceException: 'Object reference not set to an instance of an object.'
SOURCE: how to get the fontsize of a certain line in a richtextbox in c# using winforms - MarkusEgle's solution.
Dim comboBox1Index As Integer = tbSelectFont.FindStringExact(Description.SelectionFont.Name)
Dim comboBox2Index As Integer = tbSelectSize.FindStringExact(Description.SelectionFont.Size.ToString())
tbSelectFont.SelectedIndex = comboBox1Index
tbSelectSize.SelectedIndex = comboBox2Index

VB.NET Listbox item color [duplicate]

So I'm trying to make a listbox with 2 buttons.
Listbox is supposed to display files from a specific folder (I get back to this)
And the two buttons are supposed to be called "Set.." (As in set directory)
and Update (As the list will be refreshed (Something I do every time the Windows Form Runs.
So as of now, when I start my application, and go to the form with the listbox, the listbox is empty. When pressing "Update", the List box picks up files from an address located on my Harddrive (So this is a static address located in my code).
It also finds 7 different extensions (Filetypes), and lists all of them correctly.
My problem is as follows, I want the set Button to open a File Dialog for the user on First-Time Runtime, so the user himself can choose what folder the program "Indexes or Searches" if you will. And then when he runs the application again, and finds the listbox, he can only press Update, and the listbox shows the content of the folder he choose last time.
The Set - button doesn't do anything in my code right now.
Second up, I want each filetype to be labeled or colored with a specific color.
Like; .txt should be blue, .jpg is red, ect..
Running Visual Studio 2013 if that helps.
Also, when checking my code, if you have any suggestions too, how I can improve the code, make it easier, shorter, and just to change things to avoid duplicate codes, please let me know.
Here is from the Design in VS2013
Code:
Private Sub Form_Load(sender As Object, e As EventArgs) Handles Me.Load
FolderBrowserDialog1.SelectedPath = "xxx\xxx\xxx\xxx"
System.IO.Directory.GetCurrentDirectory()
Private Sub updateButtonGame_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles updateButtonGame.Click
If FolderBrowserDialog1.SelectedPath = "xxx\xxx\xxx\xxx" Then
ListFiles(FolderBrowserDialog1.SelectedPath)
End If
End Sub
Private Sub ListFiles(ByVal folderPath As String)
filesListBox.Items.Clear()
Dim fi = From f In New IO.DirectoryInfo(FolderBrowserDialog1.SelectedPath).GetFiles().Cast(Of IO.FileInfo)() _
Where f.Extension = ".z64" OrElse f.Extension = ".nds" OrElse f.Extension = ".BIN" OrElse f.Extension = ".smc" OrElse f.Extension = ".ISO" OrElse f.Extension = ".nes" OrElse f.Extension = ".gb"
Order By f.Extension
Select f
For Each fileInfo As System.IO.FileInfo In fi
filesListBox.Items.Add(fileInfo.Name)
Next
End Sub
Another thing, this is more optional..
My list is completely black, so I choose to have the "Items" in the Listbox turn Light gray.
I played around with something called e.Graphics in hope to achieve Coloring a specific filetype, and it turned ALL items either Black, Red, or whatever I put it to.
But after removing the code, all Items turns into the same color as the Background color of the Listbox. So I can no longer see the elements actually being there, other than the Scroll-bar popping up on the side (Since its many Items in the folder I picked)
Also, I am not that good with coding/visual studio yet, as I started around 1 week ago to date.
Started with VB 2010 and then went to VS2013 to see if I managed to fix some issues, also related to List Box.
If I explained rather poorly, let me know and I'll update with better info.
Project was also first created in VB 2010, and then "Migrated" or opened in VS 2013.
A much, much better way to do this is with a ListView and an ImageList with some standard images for Text, Image, PDF etc in the list, then just set the image key for each item when you add them to the list.
Alternatively, you could emulate the same thing in a listbox (using OwnerDrawFixed) to draw a specified image to indicate the file type. A really good way to implement this is as an ExtenderProvider using code similar to that below as a starting point. As an EP, you can link any cbo or listbox to an image list to provide image cues very much like the ListView works:
The reason you do not see your colored item idiom very often is that whatever colors you pick will not look right on all systems. The more colors, the more likely and more often they wont have enough contrast, be readable etc with the user's color scheme. You also dont need a "Legend" to explain what the colors mean - an image is self explanatory. That said, the DrawItem code would be something like this:
NB: Listbox control is set to OwnerDrawFixed, ItemHeight = 16
Private Sub lb_DrawItem(sender As Object,
e As DrawItemEventArgs) Handles lb.DrawItem
Dim TXT As Color = Color.Black
Dim JPG As Color = Color.Green
Dim PDF As Color = Color.Blue
Dim EXE As Color = Color.Gray
Dim SEL As Color = SystemColors.HighlightText
Dim thisColor As Color = Color.Orange
Dim ndx As Integer = e.Index
' isolate ext ans text to draw
Dim text As String = lb.Items(ndx).ToString()
Dim ext As String = System.IO.Path.GetExtension(text).ToLowerInvariant
' dont do anything if no item being drawn
If ndx = -1 Then Exit Sub
' default
e.DrawBackground()
' color selector
Select Case ext
Case ".jpg"
thisColor = JPG
Case ".txt"
thisColor = TXT
Case ".exe"
thisColor = EXE
Case ".pdf"
thisColor = PDF
End Select
' override color to use default when selected
If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
thisColor = SEL
End If
' render the text
TextRenderer.DrawText(e.Graphics, text, lb.Font, e.Bounds,
thisColor, TextFormatFlags.Left)
' default
e.DrawFocusRectangle()
End Sub
Result:
Works on My SystemTM

Force a MultiLine TextBox Horizontal ScrollBar to the left

I have a MultiLine TextBox that is updated over a period of time as an app runs, and I've managed to make it so that the TextBox scrolls to the bottom, ensuring that the latest entry is always shown.
However, sometimes the text is quite long and goes off of the side of the TextBox, so the Horizontal ScrollBar scrolls to the right.
How can I amend the code below so that the ScrollBar is always to the left, meaning that the beginning of lines is always visible? Please note that I do not wish to wrap text, as I can't have one entry on multiple lines. Thanks.
Private Sub UpdateCurrentProgress(ByVal Text As String)
If Text = "" Then Exit Sub
Dim Textbox As TextBox = Me.txtCurrentProgress
If Textbox.Text <> "" Then Text = vbCrLf & Text
Textbox.AppendText(Text)
Textbox.Select(Textbox.TextLength, 0)
Textbox.ScrollToCaret()
End Sub
You can select the first char at the current line like this:
Me.TextBox1.Select(Me.TextBox1.GetFirstCharIndexOfCurrentLine(), 0)
If I understand your problem correctly, then you need to get first the last line index and then select the first char of that line.
Dim lineNumber = textBox1.Lines.Count()-1
textBox1.Select(textBox1.GetFirstCharIndexFromLine(lineNumber), 0)

Show only latest lines in VB Label

Problem
In a VB label, if there are more lines than the fixed height can support, then the additional lines get cut off and the user only sees the first couple of lines.
I need it to be completely opposite. I want to see the latest 5 or 6 lines. What that means is that if there is more lines than the fixed height of the label can show, then instead of simply cutting them off, all the lines should move up with the latest one at the bottom. The top lines can be cut off, but the latest one needs to be in the bottom.
Example of what I am trying to do
If you look at a console and enter a command like dir, then it lists the latest directories, but you see latest read directory at the bottom. Basically, you see the latest directory it read. You only see the latest 5 or 6 directories it read instead of seeing every printed line.
Another Example: Look a textbox. If you type in more text than the height, then you see that the textbox autoscrolls with you on the text and shows the latest lines while the older ones keep moving up and eventually get cut-off until you move the scroll bar up. I need it to be exactly the same, except without scroll bars.
One more example: If you set the TextAlign property of the label to Bottom Center, then you see the text move up as you add more lines. The problem occurs when the label is filled with the lines and the text exceeds the height and gets cut off. That shouldn't happen. The text at top should get cut off, but the latest line should keep coming from the bottom.
Solutions recommended by others
The only solution that I have been given is to create a custom control derived from the label.
Is there any other way that this can be done?
Thank You for your help.
Drop a button and a label on a NEW form (so as not to mess up your existing code) and copy and paste the code below and click the button repeatedly and see if this solves your issue.
obviously if it does you still have to mess with the code so it suits your particular needs.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Static TextLines As Generic.List(Of String) = Nothing
Static SingleLineHeight As Integer = Nothing
Static maxrows As Integer
Static qty As Integer = Nothing
Dim text As String = Nothing
Dim counta As Integer = Nothing
'
'set MAX ROWS
maxrows = 6
' Initalise
If TextLines Is Nothing Then TextLines = New Generic.List(Of String)
If SingleLineHeight = 0 Then
Label1.Text = "Test Line"
SingleLineHeight = Label1.Font.Height
Label1.Text = ""
End If
'
'process
qty = qty + 1
text = "Line Number " & qty
TextLines.Add(text)
Label1.Text = ""
If TextLines.Count > maxrows - 1 Then TextLines.RemoveAt(0)
For counta = 0 To TextLines.Count - 1
Label1.Text = Label1.Text & TextLines(counta) & vbCrLf
Next
End Sub

"Caret Position" in VB.NET for syntax highlighting

I'm trying to make a TextBox with syntax highlighting (for (HTML/CSS) in VB.NET 2008.
I figured that if I use RichTextBox.Find(), I can color specific text, but then I need to call RichTextBox.DeselectAll().
The problem is that the the cursor jumps to the beginning of the RTB.
I'm using WinForms.
Any ideas?
You can get and set the cursor position using the SelectionStart property.
Therefore, you can write,
Dim selStart As Integer = rtb.SelectionStart
'Do things
rtb.SelectionStart = selStart
Imports System.Text.RegularExpressions
Public Class Form1
'Create a Html Keyword Regex
Dim htmlkeywords As New System.Text.RegularExpressions.Regex("<html>|</html>|<head>|</head>|<meta|<p>|</p>|<div>|</div>") <----add as many terms as you like between the () don't forget the pipe symbol between each term in the Regex.
'Then in your Richtextbox textchanged event add this
Private Sub rtText_TextChanged(sender As Object, e As EventArgs) Handles rtText.TextChanged
Dim selStart As Integer = rtText.SelectionStart
Do Until False
For Each keyWordMatch As Match In htmlkeywords.Matches(rtText.Text)
rtText.Select(keyWordMatch.Index, keyWordMatch.Length)
rtText.SelectionColor = Color.Purple
rtText.SelectionStart = rtText.Text.Length 'this puts the caret at the end
rtText.SelectionLength = 0 ' of the word
Next keyWordMatch
Exit Do
Loop
rtText.SelectionColor = Color.Black
rtText.SelectionStart = selStart ' this makes sure that if your caret is behind a word and you press enter to move a text down a line; the caret will stay in position on the next line that you start typing on. You can remove this code to see what I'm talking about
End Sub
rtText is my RichTextBox name. This will change the word you want to whatever color then change it back to black, which you can change which colors do what. Hope this helps!