Display String From Text File in a Label Box in Visual Basic - vb.net

So I am trying to make a code where the user inputs an ID number and receives the line of text that the ID corresponds to. I am having trouble with the code as I am unable to display the result (or correct result) in a label box.
For example:
If I type in ID 1, it displays the data that corresponds to ID 2, ID 2 corresponds to ID 3 and ID 3 ends the loop (there are only 3 records in the data currently).
I have included my code below
Private Sub btnSearch_Click(sender As Object, e As EventArgs) Handles btnSearch.Click
Filename = "NamesAndAges.txt"
FileOpen(1, Filename, OpenMode.Input,,,)
Dim ID As Integer
ID = txtSearch.Text
Dim Found As Boolean
Found = False
Do While Not EOF(1) And Found = False
If LineInput(1).Contains(ID) Then
lblDisplaySearch.Text = LineInput(1)
Found = True
Else MsgBox("Not Found")
End If
Loop
FileClose(1)
End Sub
Thanks in advance, would also really appreciate if anyone could explain the code they use as I am still a visual basic beginner.

Every time you call LineInput(1), it reads a line, so you're reading a line and checking if it contains ID then reading another line and setting lblDisplaySearch.Text to that value.
Try something like this:
Dim line As String
Do While Not EOF(1) And Found = False
line = LineInput(1)
If line.Contains(ID) Then
lblDisplaySearch.Text = line
Found = True
Else MsgBox("Not Found")
End If
Loop

I would strongly suggest simplifying this code, by using File.ReadLines:
Private Sub btnSearch_Click(sender As Object, e As EventArgs) Handles btnSearch.Click
Filename = "NamesAndAges.txt"
Dim line = File.ReadLines(Filename).FirstOrDefault(Function(x) x.Contains(txtSearch.Text))
If line Is Nothing Then
MsgBox("Not Found")
Exit Sub
End If
lblDisplaySearch.Text = line
End Sub
The primary advantage is that you don't need to manage the Do While loop. Instead, you are passing the match condition (Function(x) x.Contains(txtSearch.Text)) to the FirstOrDefault method, which internally will find the first line matching the condition and return it, or return Nothing if it's not found.
For Each and IEnumerable
VB.NET allows you to loop over a set of items without knowing or caring about the index within the set. For example:
For Each x As String In File.ReadLines(Filename)
'do something with the line, which is now in x
Next
In order to use For Each with an object, that object has to implement a specific interface — the IEnumerable interface.
Interfaces
Interfaces guarantee that a given object has, or implements, specific members. In this case, if an object implements the IEnumerable interface, that means it has a GetEnumerator method, which the For Each uses under the hood.
IEnumerable(Of T)
The object returned from File.ReadLines implements another more advanced generic interface called IEnumerable(Of T). With this interface, the compiler can figure out automatically that each step of the For Each will be parsing a string, and we don't need to specify it as a string:
For Each x In File.ReadLines(Filename)
'x is known to be a String here
Next
Lambda expressions
The condition "the line which contains the search text" is written as a lambda expression, which (in this case) is made into a method without an explicit name or definition. This is convenient, because we don't have to write this:
Function ContainsCondition(line As String, toFind As String) As Boolean
Return line.Contains(toFind)
End Function`
every time we want to express a condition this way.
Type of x in the condition
Because File.ReadLines returns an IEnumerable(Of T), in this case an IEnumerable(Of String), the compiler can figure out that the condition is working on strings, so we don't have to specify that x is a string within the condition.

Related

calling a method from another class in vb.net

i have an external class(colorCode.vb) in same project as my main form.vb.
My objective is to send a value as argument as i call a method in the colorCode.vb class, and use the value returned by the method. I don't know if its logically possible . Here i tried this but failed.
in my colorCode.vb Class i have this codes:
Public Sub getValue(ByVal itemCode As Integer)
Dim codeVal() As Integer = {9999, 3034, 3040, 3035}
Dim colorVal As String
For counter As Integer = 0 To codeVal.Count Step 1
If (itemCode = codeVal(counter)) Then
Select Case codeVal(counter)
Case 9999
colorVal = "BRILLIANT WHITE EMULSION"
Case 3034
colorVal = "OFF-WHITE EMULSION"
End Select
End If
Next
End Sub
and in my main form.vb i did this
Private Sub descTextBox_TextChanged(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles descTextBox.TextChanged
Dim colorDesc As colorCode = New colorCode()
Dim itemCode As Integer = Integer.Parse(itemCodeTextBox.Text)
descTextBox.Text = colorDesc.getValue(itemCode)'this line triggers an error.
End Sub
please i need some help here. already running nuts
Please turn on Option Strict. This is a 2 part process. First for the current project - In Solution Explorer double click My Project. Choose Compile on the left. In the Option Strict drop-down select ON. Second for future projects - Go to the Tools Menu -> Options -> Projects and Solutions -> VB Defaults. In the Option Strict drop-down select ON. This will save you from bugs at runtime.
Subs in vb.net do not return values. You need a Function. Instead of looping through the array you can just check if the array contains itemCode and if it does proceed with the Select Case.
Do not put your code in the text changed. It will run every time a character is entered before your user has a chance to enter the entire code. Create a button and use that. Use .TryParse to validate the input. It will return True if valid and fill in the second parameter with the number.
Public Class colorCode
Public Function getValue(ByVal itemCode As Integer) As String
Dim codeVal() As Integer = {9999, 3034, 3040, 3035}
Dim colorVal As String = ""
If codeVal.Contains(itemCode) Then
Select Case itemCode
Case 9999
colorVal = "BRILLIANT WHITE EMULSION"
Case 3034
colorVal = "OFF-WHITE EMULSION"
Case 3040
colorVal = "Blue"
Case 3035
colorVal = "Green"
End Select
Else
colorVal = "No matching color"
End If
Return colorVal
End Function
End Class
And in the form...
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim itemCode As Integer
If Integer.TryParse(TextBox1.Text, itemCode) Then
Dim colorDesc As colorCode = New colorCode()
TextBox2.Text = colorDesc.getValue(itemCode)
Else
MessageBox.Show("Please enter a valid number")
End If
End Sub
Add the return statement. You should also check the value of itemCodeTextBox.Text to make sure it's a numeric value before you call colorDesc.getValue otherwise your program may crash if a non-numeric value is entered into the textbox
You are missing the "return" part of your method.
Public Function getValue(ByVal itemCode As Integer) As String
That's what it'll take to declare you're returning a string value, and then you actually return your vale at the end of the method.
Next
return colorVal
End Sub
There are other ways to do this as well as better ways to deal with your "getValue" code, but this should get you running for now. The rest of the improvements are likely a different Question, or even series of Questions. Or you could go to the Code Review stack and get more help there.

How Do I loop through this class once I have added items

How do i loop through this class once I add items via this method. Just I am quite new to generic lists so was wonding if someone could point me in right direction in datatables im used to doing the following:
For Each thisentry In dt.rows
Next
What do I use in collections
Calling Code
Calling this in my delciarations of main class
Dim infoNoProductAvail As List(Of infoProductsNotFound) = New List(Of infoProductsNotFound)()
this is how i am adding the files but I have checked in the routine and the count for the list is at 2 products
If medProductInfo.SKU.SKUID = 0 Then
infoNoProductAvail.Add(New infoProductsNotFound(thisenty2.Item("EAN13").ToString(), True))
End If
this is the class itselfs
Public Class infoProductsNotFound
Public Sub New(tbcode As String, notfound As Boolean)
Me.tagbarcode = tbcode
Me.notfound = notfound
End Sub
Private tagbarcode As String = String.Empty
Private notfound As Boolean
Public Property tbcode() As String
Get
Return tagbarcode
End Get
Set(ByVal value As String)
tagbarcode = value
End Set
End Property
Public Property isNotFound() As Boolean
Get
Return notfound
End Get
Set(ByVal value As Boolean)
notfound = value
End Set
End Property
End Class
Tried
I tried using the following
Function BuildExceptionsForEmail()
Dim retval As String = ""
Dim cnt As Int32 = 0
retval = "The following products are not avialable" & vbCrLf
For Each info As infoProductsNotFound In infoNoProductAvail
retval &= info.tbcode
cnt &= 1
Next
Return retval
but for some reason at this point my info noproductAvail is blank even though in the routine above its sitting at count of 2 what gives?
First I'd shrink that declaration a bit:
Dim infoNoProductAvail As New List(Of infoProductsNotFound)
Next, to iterate there are several options. First (and what you're likely most used to):
For Each info as infoProductsNotFound in infoNoProductAvail
If info.tbCode = "xyz" Then
DoSomething(info)
End If
Next
Or you might want to use lambda expressions (if you're using .Net 3.5 and above I think - might be .Net 4):
infoNoProductAvail.ForEach (Function(item) DoSomething(item))
Remember that generics are strongly typed (unlike the old VB collections) so no need to cast whatever comes out: you can access properties and methods directly.
If infoNoProductAvail(3).isNotFound Then
'Do something
End If
(Not that that is a great example, but you get the idea).
The For Each syntax is the same. It works the same way for all IEnumerable objects. The only "trick" to it is to make sure that your iterator variable is of the correct type, and also to make sure that you are iterating through the correct object.
In the case of the DataTable, you are iterating over it's Rows property. That property is an IEnumerable object containing a list of DataRow objects. Therefore, to iterate through it with For Each, you must use an iterator variable of type DataRow (or one of its base classes, such as Object).
To iterate through a generic List(Of T), the IEnumerable object is the List object itself. You don't need to go to one of it's properties. The type of the iterator needs to match the type of the items in the list:
For Each i As infoProductsNotFound In infoNoProductAvail
' ...
Next
Or:
Dim i As infoProductsNotFound
For Each i In infoNoProductAvail
' ...
Next
Or:
For Each i As Object In infoNoProductAvail
' ...
Next
Etc.

How can I get specific items from text file? Visual Basic 2010

I am trying to figure out how to get a message box to show only specific words from a text file, which contains all of the words within the dictionary. I have tried various different ways, but cannot get it to work, but I do think I am on the right track so just need some pointers.
Basically, there is a scrambled up string, which is different every time, and is contained within a label. I want the program to only show words which contains the letters inside the scrambled string, but not sure how to achieve this?
Here is the code that I have so far:
Private Sub btnAnswers_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAnswers.Click
Dim hash As List(Of String) = New List(Of String)(System.IO.File.ReadAllLines("C:\Users\Katie\Documents\Project\dictionary.txt"))
Dim Letters As String
Dim OneCharacter As String
Dim Found As Boolean
Dim item As String
Dim AllCharacters As String
Found = False
Letters = lblLetters.Text
For i = 0 To Letters.Length - 1
OneCharacter = Letters.Substring(i, 1)
For Each item In hash
If item.Contains(OneCharacter) Then
Found = True
AllCharacters = OneCharacter
Else
Found = False
End If
MsgBox(item)
Next
Next i
End Sub
The message box does show up words, from the dictionary, but words can contain letters that are not present in the label string, so my code is wrong. Can anyone help? Apologies, but I am new to programming.
With a simple modification to the previous answer you can restrict the output to only words that contain only the scrambled letters:
Private Sub btnAnswers_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim hash As List(Of String) = New List(Of String)(System.IO.File.ReadAllLines("C:\Users\Katie\Documents\Project\dictionary.txt"))
Dim Letters As String = lblLetters.Text
For Each item As String In hash
Dim word As String = item.ToLower()
For i = 0 To Letters.Length - 1
Dim OneCharacter As Char = Char.ToLower(Letters(i))
While word.Contains(OneCharacter)
word = word.Remove(word.IndexOf(OneCharacter), 1)
End While
Next
If (word.Length = 0) Then
'The given dictionary entry includes all the letters in the label. No more iterations will be performed
MsgBox(item)
Exit For
End If
Next
End Sub
With this code if the scrambled letters contain 'bok' then "book" will get selected. However removing the while loop and leaving only the remove statement, will ensure that only the exact number of each different letter will match. so that 'obok' will be needed to match "book".
As suggested by Steven Doggart, you have to invert the loop nesting. What you want is going through all the dictionary entries and, for each of them, checking if it contains all the letters in the string. Your loop structure is not allowing to do that.
I have performed the required updates in your code. Bear in mind that this code ignores caps ("A" is the same than "a").
Private Sub btnAnswers_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim hash As List(Of String) = New List(Of String)(System.IO.File.ReadAllLines("C:\Users\Katie\Documents\Project\dictionary.txt"))
Dim Letters As String = lblLetters.Text
For Each item As String In hash
Dim Found As Boolean = True
For i = 0 To Letters.Length - 1
Dim OneCharacter As String = Letters.Substring(i, 1)
Dim itemToLower As String = item.ToLower()
If Not itemToLower.Contains(OneCharacter.ToLower()) Then
Found = False
Exit For
End If
Next i
If (Found) Then
'The given dictionary entry includes all the letters in the label. No more iterations will be performed
MsgBox(item)
Exit For
End If
Next
End Sub
This code looks for keys containing all the characters in the given label (not at the contrary), that is: with a label "dict", "dictentry" would be right.
In any case, the whole point of my answer is not delivering a code which you just have to execute; the point of this code is helping you to understand what you did wrong and how to start doing things right. If you are not interested in this exact functionality, you woud have to edit my code such that what you want can be accomplished; or, ideally, you would be writing your own code completely from scratch.
You're calling MsgBox(item) outside of the if statement that determines whether the current word contains the current character. Which means it's going to pop up for every letter, every word. Move MsgBox(item) inside the first half of the if statement if you only want it to show when the letter is actually found in the word.
I'd also suggest following Steven Doggart's advice and reversing the way the loops are nested. If the characters are the outer loop, you could get the message box multiple times per word (e.g., if your letters are "sdf" and one of the words is "foods," it will pop up 3 times).
For Each item In hash
For i = 0 To Letters.Length - 1
OneCharacter = Letters.Substring(i, 1)
If item.Contains(OneCharacter) Then
Found = True
AllCharacters = OneCharacter
MsgBox(item)
Else
Found = False
End If
Next
Next

Using Delegates for handling an event

Sorry, that's the best subject I can come up with, if I understood the solution better, I could probably phrase a better subject line.
I am using a great grid control, Super List,l located here:
http://www.codeproject.com/KB/list/outlooklistcontrol.aspx?fid=449232&df=90&mpp=25&noise=3&sort=Position&view=Quick&fr=276
Before you read the problem, please note that you can download a very small VB.NET 2005 sample app which demos the problem:
http://dokmanovich.com/Documents/SuperListEvents.zip
Getting the answer to my question will, I hope, help me to understand dynamic events better in the context of what I am trying to accomplish.
The grid works like this: When you add a column to the grid, you specify the address of an event handler which will return the value at run time. In this case, the CC_ItemValueAccessor function. The latter function will be called with an input parameter which, in this case, is a "ToDo" object. Each ToDo object will be rendered as one row in the grid. The job of the CC_ItemValueAccessor function is to return the column value to be displayed by the grid for the row that corresponds to the passed-in ToDo object.
This works fine till I take it to the next step:
I want to dynamically create columns at run time. For example, I want to display the output of a datatable returned as a result of executing a user-specified SQL.
Using the earlier described static approach, I have one columnItemValueAccessor function responsible for returning the value of each column in the grid for the passed in row object. Now, since the columns are determined at run time based on the SQL returned results, I believe I need to write a generic handler that handles all columns, determines the name of the column that triggered this event and then returns the value for that column within the row object that is passed in as the sole parameter.
The problem is that the ItemValueAccessor function has a signature that only includes the row object and I do not know of a way to determine which column name is needed since all of the columns were hooked up to the same ItemValueAccessor function as the event handler.
I suspect that this is just a limitation of the control and that to overcome this problem I would have to enhance the underlying custom control, but that is likely beyond my current skills as it is an advanced control written in C# and I am a VB guy.
Here's the code:
Private Sub AddCcColumn()
Dim NewColumn As New BinaryComponents.SuperList.Column("CC", "CC", 110, AddressOf Cc_ItemValueAccessor)
_SuperList.Columns.Add(NewColumn)
End Sub
Private Function Cc_ItemValueAccessor(ByVal rowItem As Object) As Object
Dim ToDo As ToDo = CType(rowItem, SrToDoAndException).ToDo
Return ToDo.CCs.ToString
End Function
'---------------------------
And here are the signatures of the Column's instantiator method and the definition of the last parameter which is responsible for specifying the procedure that handles identifies the event handler responsible for returning the value of the column.
Public Sub New(ByVal name As String, ByVal caption As String, ByVal width As Integer, ByVal columnItemValueAccessor As BinaryComponents.SuperList.ColumnItemValueAccessor)
Member of BinaryComponents.SuperList.Column
Public Sub New(ByVal object As Object, ByVal method As System.IntPtr)
Member of BinaryComponents.SuperList.ColumnItemValueAccessor
Does anyone have any suggestions or am I stuck? I would really love to utilize the fantasic grouping capabilities of this control so I can display dynamic output that allows the user to group the dynamic output of a SQL by any column that they want.
I addressed the question to the author at the above site but it has gone unanswered. This is a desperate attempt to find a way to do this.
Thanks for bearing with me. I hope this question isn't rejected based on the fact that I refer to a third party control. My hope is that the answer lies in a better understanding of delegates, a more universal topic.
I used a lambda function, as Matthew suggested. Here is the code from the dynamic approach:
Private Sub btnDynamic_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDynamic.Click
ListControl1.Columns.Clear()
For Each DataCol As DataColumn In _ds.dtbPerson.Columns
' Get the column name in a loop variable - it needs to be in loop scope or this won\'t work properly'
Dim colName = DataCol.ColumnName
' Create the function that will be called by the grid'
Dim colLambda As ColumnItemValueAccessor = Function(rowItem As Object) General_ItemValueAccessor(rowItem, colName)
' Setup each column in the grid'
Dim NewColumn As New BinaryComponents.SuperList.Column(DataCol.ColumnName, DataCol.ColumnName, 220, colLambda)
ListControl1.Columns.Add(NewColumn)
Next
End Sub
Private Function General_ItemValueAccessor(ByVal rowItem As Object, ByVal colName As Object) As Object
Dim rowPerson As DataRow = CType(rowItem, DataRow)
Return rowPerson.Item(colName).ToString
End Function
Here's a quick primer on how it works:
Each time through the loop the lambda function is creating a new callback function for each column that looks something like this:
Class Func1
Dim colName1 As String = "PersonId"
Private Function General_ItemValueAccessor1(ByVal rowItem As Object) As Object
Dim rowPerson As DataRow = CType(rowItem, DataRow)
Return rowPerson.Item(Me.colName1).ToString
End Function
End Class
Class Func2
Dim colName2 As String = "LastName"
Private Function General_ItemValueAccessor2(ByVal rowItem As Object) As Object
Dim rowPerson As DataRow = CType(rowItem, DataRow)
Return rowPerson.Item(Me.colName2).ToString
End Function
End Class
... for however many columns you have - 3 in this case.
You need the colName variable within the loop and not use just DataCol.ColumnName directly in the lambda. Otherwise, when the grid gets around to calling the callback functions, that DataCol variable would be equal to the last value from the collection (or Nothing) for all the callback functions.
Basically, it would do this and you wouldn't get what you expected:
Class Func
Dim DataCol1 = DataCol
Private Function General_ItemValueAccessor1(ByVal rowItem As Object) As Object
Dim rowPerson As DataRow = CType(rowItem, DataRow)
Return rowPerson.Item(Me.DataCol1.ColumnName).ToString
End Function
Private Function General_ItemValueAccessor2(ByVal rowItem As Object) As Object
Dim rowPerson As DataRow = CType(rowItem, DataRow)
Return rowPerson.Item(Me.DataCol1.ColumnName).ToString
End Function
...
End Class
Hope that helps. Good luck.
The problem is that the ItemValueAccessor function has a signature that only includes the row object and I do not know of a way to determine which column name is needed since all of the columns were hooked up to the same ItemValueAccessor function as the event handler.
Okay, I haven't used that control in the past, and I'm really a C# person. But I think you may be able to accomplish this by creating a new lambda function for each column. Something like:
Private Sub AddCcColumn(ByVal sender As System.Object As System.String)
colLambda = (Function(rowItem As Object) Cc_InternalItemValueAccessor(columnName, rowItem))
Dim NewColumn As New BinaryComponents.SuperList.Column("CC", "CC", 110, colLambda)
_SuperList.Columns.Add(NewColumn)
End Sub
Then, colLambda will fit the signature, while your internal Cc_InternalItemValueAccessor gets the info it needs. Totally untested, but I think the basic idea works.

Hidden Features of VB.NET?

Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
I have learned quite a bit browsing through Hidden Features
of C# and was surprised when I couldn't find something
similar for VB.NET.
So what are some of its hidden or lesser known features?
The Exception When clause is largely unknown.
Consider this:
Public Sub Login(host as string, user as String, password as string, _
Optional bRetry as Boolean = False)
Try
ssh.Connect(host, user, password)
Catch ex as TimeoutException When Not bRetry
''//Try again, but only once.
Login(host, user, password, True)
Catch ex as TimeoutException
''//Log exception
End Try
End Sub
Custom Enums
One of the real hidden features of VB is the completionlist XML documentation tag that can be used to create own Enum-like types with extended functionality. This feature doesn't work in C#, though.
One example from a recent code of mine:
'
''' <completionlist cref="RuleTemplates"/>
Public Class Rule
Private ReadOnly m_Expression As String
Private ReadOnly m_Options As RegexOptions
Public Sub New(ByVal expression As String)
Me.New(expression, RegexOptions.None)
End Sub
Public Sub New(ByVal expression As String, ByVal options As RegexOptions)
m_Expression = expression
m_options = options
End Sub
Public ReadOnly Property Expression() As String
Get
Return m_Expression
End Get
End Property
Public ReadOnly Property Options() As RegexOptions
Get
Return m_Options
End Get
End Property
End Class
Public NotInheritable Class RuleTemplates
Public Shared ReadOnly Whitespace As New Rule("\s+")
Public Shared ReadOnly Identifier As New Rule("\w+")
Public Shared ReadOnly [String] As New Rule("""([^""]|"""")*""")
End Class
Now, when assigning a value to a variable declared as Rule, the IDE offers an IntelliSense list of possible values from RuleTemplates.
/EDIT:
Since this is a feature that relies on the IDE, it's hard to show how this looks when you use it but I'll just use a screenshot:
Completion list in action http://page.mi.fu-berlin.de/krudolph/stuff/completionlist.png
In fact, the IntelliSense is 100% identical to what you get when using an Enum.
Have you noticed the Like comparison operator?
Dim b As Boolean = "file.txt" Like "*.txt"
More from MSDN
Dim testCheck As Boolean
' The following statement returns True (does "F" satisfy "F"?)'
testCheck = "F" Like "F"
' The following statement returns False for Option Compare Binary'
' and True for Option Compare Text (does "F" satisfy "f"?)'
testCheck = "F" Like "f"
' The following statement returns False (does "F" satisfy "FFF"?)'
testCheck = "F" Like "FFF"
' The following statement returns True (does "aBBBa" have an "a" at the'
' beginning, an "a" at the end, and any number of characters in '
' between?)'
testCheck = "aBBBa" Like "a*a"
' The following statement returns True (does "F" occur in the set of'
' characters from "A" through "Z"?)'
testCheck = "F" Like "[A-Z]"
' The following statement returns False (does "F" NOT occur in the '
' set of characters from "A" through "Z"?)'
testCheck = "F" Like "[!A-Z]"
' The following statement returns True (does "a2a" begin and end with'
' an "a" and have any single-digit number in between?)'
testCheck = "a2a" Like "a#a"
' The following statement returns True (does "aM5b" begin with an "a",'
' followed by any character from the set "L" through "P", followed'
' by any single-digit number, and end with any character NOT in'
' the character set "c" through "e"?)'
testCheck = "aM5b" Like "a[L-P]#[!c-e]"
' The following statement returns True (does "BAT123khg" begin with a'
' "B", followed by any single character, followed by a "T", and end'
' with zero or more characters of any type?)'
testCheck = "BAT123khg" Like "B?T*"
' The following statement returns False (does "CAT123khg" begin with'
' a "B", followed by any single character, followed by a "T", and'
' end with zero or more characters of any type?)'
testCheck = "CAT123khg" Like "B?T*"
Typedefs
VB knows a primitive kind of typedef via Import aliases:
Imports S = System.String
Dim x As S = "Hello"
This is more useful when used in conjunction with generic types:
Imports StringPair = System.Collections.Generic.KeyValuePair(Of String, String)
Oh! and don't forget XML Literals.
Dim contact2 = _
<contact>
<name>Patrick Hines</name>
<%= From p In phoneNumbers2 _
Select <phone type=<%= p.Type %>><%= p.Number %></phone> _
%>
</contact>
Object initialization is in there too!
Dim x as New MyClass With {.Prop1 = foo, .Prop2 = bar}
DirectCast
DirectCast is a marvel. On the surface, it works similar to the CType operator in that it converts an object from one type into another. However, it works by a much stricter set of rules. CType's actual behaviour is therefore often opaque and it's not at all evident which kind of conversion is executed.
DirectCast only supports two distinct operations:
Unboxing of a value type, and
upcasting in the class hierarchy.
Any other cast will not work (e.g. trying to unbox an Integer to a Double) and will result in a compile time/runtime error (depending on the situation and what can be detected by static type checking). I therefore use DirectCast whenever possible, as this captures my intent best: depending on the situation, I either want to unbox a value of known type or perform an upcast. End of story.
Using CType, on the other hand, leaves the reader of the code wondering what the programmer really intended because it resolves to all kinds of different operations, including calling user-defined code.
Why is this a hidden feature? The VB team has published a guideline1 that discourages the use of DirectCast (even though it's actually faster!) in order to make the code more uniform. I argue that this is a bad guideline that should be reversed: Whenever possible, favour DirectCast over the more general CType operator. It makes the code much clearer. CType, on the other hand, should only be called if this is indeed intended, i.e. when a narrowing CType operator (cf. operator overloading) should be called.
1) I'm unable to come up with a link to the guideline but I've found Paul Vick's take on it (chief developer of the VB team):
In the real world, you're hardly ever going to notice the difference, so you might as well go with the more flexible conversion operators like CType, CInt, etc.
(EDIT by Zack: Learn more here: How should I cast in VB.NET?)
If conditional and coalesce operator
I don't know how hidden you'd call it, but the Iif([expression],[value if true],[value if false]) As Object function could count.
It's not so much hidden as deprecated! VB 9 has the If operator which is much better and works exactly as C#'s conditional and coalesce operator (depending on what you want):
Dim x = If(a = b, c, d)
Dim hello As String = Nothing
Dim y = If(hello, "World")
Edited to show another example:
This will work with If(), but cause an exception with IIf()
Dim x = If(b<>0,a/b,0)
This is a nice one. The Select Case statement within VB.Net is very powerful.
Sure there is the standard
Select Case Role
Case "Admin"
''//Do X
Case "Tester"
''//Do Y
Case "Developer"
''//Do Z
Case Else
''//Exception case
End Select
But there is more...
You can do ranges:
Select Case Amount
Case Is < 0
''//What!!
Case 0 To 15
Shipping = 2.0
Case 16 To 59
Shipping = 5.87
Case Is > 59
Shipping = 12.50
Case Else
Shipping = 9.99
End Select
And even more...
You can (although may not be a good idea) do boolean checks on multiple variables:
Select Case True
Case a = b
''//Do X
Case a = c
''//Do Y
Case b = c
''//Do Z
Case Else
''//Exception case
End Select
One major time saver I use all the time is the With keyword:
With ReallyLongClassName
.Property1 = Value1
.Property2 = Value2
...
End With
I just don't like typing more than I have to!
The best and easy CSV parser:
Microsoft.VisualBasic.FileIO.TextFieldParser
By adding a reference to Microsoft.VisualBasic, this can be used in any other .Net language, e.g. C#
AndAlso/OrElse logical operators
(EDIT: Learn more here: Should I always use the AndAlso and OrElse operators?)
Static members in methods.
For example:
Function CleanString(byval input As String) As String
Static pattern As New RegEx("...")
return pattern.Replace(input, "")
End Function
In the above function, the pattern regular expression will only ever be created once no matter how many times the function is called.
Another use is to keep an instance of "random" around:
Function GetNextRandom() As Integer
Static r As New Random(getSeed())
Return r.Next()
End Function
Also, this isn't the same as simply declaring it as a Shared member of the class; items declared this way are guaranteed to be thread-safe as well. It doesn't matter in this scenario since the expression will never change, but there are others where it might.
In vb there is a different between these operators:
/ is Double
\ is Integer ignoring the remainder
Sub Main()
Dim x = 9 / 5
Dim y = 9 \ 5
Console.WriteLine("item x of '{0}' equals to {1}", x.GetType.FullName, x)
Console.WriteLine("item y of '{0}' equals to {1}", y.GetType.FullName, y)
'Results:
'item x of 'System.Double' equals to 1.8
'item y of 'System.Int32' equals to 1
End Sub
I really like the "My" Namespace which was introduced in Visual Basic 2005. My is a shortcut to several groups of information and functionality. It provides quick and intuitive access to the following types of information:
My.Computer: Access to information related to the computer such as file system, network, devices, system information, etc. It provides access to a number of very important resources including My.Computer.Network, My.Computer.FileSystem, and My.Computer.Printers.
My.Application: Access to information related to the particular application such as name, version, current directory, etc.
My.User: Access to information related to the current authenticated user.
My.Resources: Access to resources used by the application residing in resource files in a strongly typed manner.
My.Settings: Access to configuration settings of the application in a strongly typed manner.
Custom Events
Though seldom useful, event handling can be heavily customized:
Public Class ApplePie
Private ReadOnly m_BakedEvent As New List(Of EventHandler)()
Custom Event Baked As EventHandler
AddHandler(ByVal value As EventHandler)
Console.WriteLine("Adding a new subscriber: {0}", value.Method)
m_BakedEvent.Add(value)
End AddHandler
RemoveHandler(ByVal value As EventHandler)
Console.WriteLine("Removing subscriber: {0}", value.Method)
m_BakedEvent.Remove(value)
End RemoveHandler
RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)
Console.WriteLine("{0} is raising an event.", sender)
For Each ev In m_BakedEvent
ev.Invoke(sender, e)
Next
End RaiseEvent
End Event
Public Sub Bake()
''// 1. Add ingredients
''// 2. Stir
''// 3. Put into oven (heated, not pre-heated!)
''// 4. Bake
RaiseEvent Baked(Me, EventArgs.Empty)
''// 5. Digest
End Sub
End Class
This can then be tested in the following fashion:
Module Module1
Public Sub Foo(ByVal sender As Object, ByVal e As EventArgs)
Console.WriteLine("Hmm, freshly baked apple pie.")
End Sub
Sub Main()
Dim pie As New ApplePie()
AddHandler pie.Baked, AddressOf Foo
pie.Bake()
RemoveHandler pie.Baked, AddressOf Foo
End Sub
End Module
I just found an article talking about the "!" operator, also know as the "dictionary lookup operator". Here's an excerpt from the article at: http://panopticoncentral.net/articles/902.aspx
The technical name for the ! operator
is the "dictionary lookup operator." A
dictionary is any collection type that
is indexed by a key rather than a
number, just like the way that the
entries in an English dictionary are
indexed by the word you want the
definition of. The most common example
of a dictionary type is the
System.Collections.Hashtable, which
allows you to add (key, value) pairs
into the hashtable and then retrieve
values using the keys. For example,
the following code adds three entries
to a hashtable, and looks one of them
up using the key "Pork".
Dim Table As Hashtable = New Hashtable
Table("Orange") = "A fruit"
Table("Broccoli") = "A vegetable"
Table("Pork") = "A meat"
Console.WriteLine(Table("Pork"))
The ! operator can be used to look up
values from any dictionary type that
indexes its values using strings. The
identifier after the ! is used as the
key in the lookup operation. So the
above code could instead have been
written:
Dim Table As Hashtable = New Hashtable
Table!Orange = "A fruit"
Table!Broccoli = "A vegetable"
Table!Pork = "A meat"
Console.WriteLine(Table!Pork)
The second example is completely
equivalent to the first, but just
looks a lot nicer, at least to my
eyes. I find that there are a lot of
places where ! can be used, especially
when it comes to XML and the web,
where there are just tons of
collections that are indexed by
string. One unfortunate limitation is
that the thing following the ! still
has to be a valid identifier, so if
the string you want to use as a key
has some invalid identifier character
in it, you can't use the ! operator.
(You can't, for example, say
"Table!AB$CD = 5" because $ isn't
legal in identifiers.) In VB6 and
before, you could use brackets to
escape invalid identifiers (i.e.
"Table![AB$CD]"), but when we started
using brackets to escape keywords, we
lost the ability to do that. In most
cases, however, this isn't too much of
a limitation.
To get really technical, x!y works if
x has a default property that takes a
String or Object as a parameter. In
that case, x!y is changed into
x.DefaultProperty("y"). An interesting
side note is that there is a special
rule in the lexical grammar of the
language to make this all work. The !
character is also used as a type
character in the language, and type
characters are eaten before operators.
So without a special rule, x!y would
be scanned as "x! y" instead of "x !
y". Fortunately, since there is no
place in the language where two
identifiers in a row are valid, we
just introduced the rule that if the
next character after the ! is the
start of an identifier, we consider
the ! to be an operator and not a type
character.
This is built-in, and a definite advantage over C#. The ability to implement an interface Method without having to use the same name.
Such as:
Public Sub GetISCSIAdmInfo(ByRef xDoc As System.Xml.XmlDocument) Implements IUnix.GetISCSIInfo
End Sub
Forcing ByVal
In VB, if you wrap your arguments in an extra set of parentheses you can override the ByRef declaration of the method and turn it into a ByVal. For instance, the following code produces 4, 5, 5 instead of 4,5,6
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim R = 4
Trace.WriteLine(R)
Test(R)
Trace.WriteLine(R)
Test((R))
Trace.WriteLine(R)
End Sub
Private Sub Test(ByRef i As Integer)
i += 1
End Sub
See Argument Not Being Modified by Procedure Call - Underlying Variable
Passing parameters by name and, so reordering them
Sub MyFunc(Optional msg as String= "", Optional displayOrder As integer = 0)
'Do stuff
End function
Usage:
Module Module1
Sub Main()
MyFunc() 'No params specified
End Sub
End Module
Can also be called using the ":=" parameter specification in any order:
MyFunc(displayOrder:=10, msg:="mystring")
The Using statement is new as of VB 8, C# had it from the start. It calls dispose automagically for you.
E.g.
Using lockThis as New MyLocker(objToLock)
End Using
Import aliases are also largely unknown:
Import winf = System.Windows.Forms
''Later
Dim x as winf.Form
Consider the following event declaration
Public Event SomethingHappened As EventHandler
In C#, you can check for event subscribers by using the following syntax:
if(SomethingHappened != null)
{
...
}
However, the VB.NET compiler does not support this. It actually creates a hidden private member field which is not visible in IntelliSense:
If Not SomethingHappenedEvent Is Nothing OrElse SomethingHappenedEvent.GetInvocationList.Length = 0 Then
...
End If
More Information:
http://jelle.druyts.net/2003/05/09/BehindTheScenesOfEventsInVBNET.aspx
http://blogs.msdn.com/vbteam/archive/2009/09/25/testing-events-for-nothing-null-doug-rothaus.aspx
If you need a variable name to match that of a keyword, enclose it with brackets. Not nec. the best practice though - but it can be used wisely.
e.g.
Class CodeException
Public [Error] as String
''...
End Class
''later
Dim e as new CodeException
e.Error = "Invalid Syntax"
e.g. Example from comments(#Pondidum):
Class Timer
Public Sub Start()
''...
End Sub
Public Sub [Stop]()
''...
End Sub
There are a couple of answers about XML Literals, but not about this specific case:
You can use XML Literals to enclose string literals that would otherwise need to be escaped. String literals that contain double-quotes, for instance.
Instead of this:
Dim myString = _
"This string contains ""quotes"" and they're ugly."
You can do this:
Dim myString = _
<string>This string contains "quotes" and they're nice.</string>.Value
This is especially useful if you're testing a literal for CSV parsing:
Dim csvTestYuck = _
"""Smith"", ""Bob"", ""123 Anywhere St"", ""Los Angeles"", ""CA"""
Dim csvTestMuchBetter = _
<string>"Smith", "Bob", "123 Anywhere St", "Los Angeles", "CA"</string>.Value
(You don't have to use the <string> tag, of course; you can use any tag you like.)
DateTime can be initialized by surrounding your date with #
Dim independanceDay As DateTime = #7/4/1776#
You can also use type inference along with this syntax
Dim independanceDay = #7/4/1776#
That's a lot nicer than using the constructor
Dim independanceDay as DateTime = New DateTime(1776, 7, 4)
You can have 2 lines of code in just one line. hence:
Dim x As New Something : x.CallAMethod
Optional Parameters
Optionals are so much easier than creating a new overloads, such as :
Function CloseTheSystem(Optional ByVal msg AS String = "Shutting down the system...")
Console.Writeline(msg)
''//do stuff
End Function
Title Case in VB.Net can be achieved by an old VB6 fxn:
StrConv(stringToTitleCase, VbStrConv.ProperCase,0) ''0 is localeID
Properties with parameters
I have been doing some C# programming, and discovered a feature that was missing that VB.Net had, but was not mentioned here.
An example of how to do this (as well as the c# limitation) can be seen at: Using the typical get set properties in C#... with parameters
I have excerpted the code from that answer:
Private Shared m_Dictionary As IDictionary(Of String, Object) = _
New Dictionary(Of String, Object)
Public Shared Property DictionaryElement(ByVal Key As String) As Object
Get
If m_Dictionary.ContainsKey(Key) Then
Return m_Dictionary(Key)
Else
Return [String].Empty
End If
End Get
Set(ByVal value As Object)
If m_Dictionary.ContainsKey(Key) Then
m_Dictionary(Key) = value
Else
m_Dictionary.Add(Key, value)
End If
End Set
End Property