VB.NET: Error: 'Cannot be converted to Boolean' when trying to pass list/array of strings - vb.net-2010

I'm trying to pass a list/array of strings to a public sub in a public module.
form 1:
Dim myList As New List(Of String) From {"Name", "ShortNo", "test1", "test2"}
Validate(myList)
form 2: (Public module)
Public Sub Validate(ByVal Input As List(Of String))
msgbox("Hi")
End sub
All I want to do is pass these strings to the public module in the form of a single package i.e an array of strings or list. I've been fiddling for the last hour or two and keep getting the same error with both. I'm definitely just doing something stupid here.
I've managed to get it to not throw an error, but when I got that working it would just skip over the sub? So I have no idea. (if someone could also explain why vb.net would run past code without calling the sub, that would be great)

Call it as below. It works for me. Fully qualify method call is required in order to stop the ambiguity.
Form2.Validate(myList)

Related

How to Build a List of Form Controls Before the Form is Loaded

I’m new to OOP and VB.NET, so please bear with me.
In VB.Net I currently have working code that defines, for each form, significant information about selected controls on the form.
I have defined in a public class:
Public Class FormFld
Public Property ScrField As Control ' A control on the form
Public Property DbField As String ' Its corresponding field name in the database
… ' Other info about the control or its database field
End Class
When each form loads, I create a list of FormFlds for the form’s selected controls:
At the start of each form
ReadOnly FormFlds As New List(Of FormFld)()
and in the form’s Load routine
FormFlds.Add(New FormFld With {.ScrField = Control1Name, .DbField = "Field1Name", …})
FormFlds.Add(New FormFld With {.ScrField = Control2Name, .DbField = "Field2Name", …})
…
This technique has worked well to easily loop through the selected fields and, on input, populate those fields from the database, or, on output, write those field values to the database.
With this implementation, however, the list must be built every single time the form is loaded. I’m wondering if the setup of the list can be done only once, during program initialization, before the forms are loaded.
Here’s the latest that I have tried.
In Class1:
Public Class indiv
Public FormFlds As List(Of FormFld)() ' The list for the frmIndividual form
End Class
In Module1, I attempt defining the FormFlds for the eventual form frmIndividual, to be saved as Indiv.FormFlds. I’d like the form name (f) and the list owner (owner) to be defineable so I can easily change those for each form.
Dim owner As New indiv
Dim f As FrmIndividual
owner.FormFlds.Add(New FormFld With {.ScrField = f.TxtKey, .DbField = "Sort_Key", …})
In Visual Studio, the third line shows error “BC30456: 'Add' is not a member of 'List(Of FormFld)()”.
Wondering if the problem might be due to not having an actual form FrmIndividual created yet, I tried changing the second line to
Dim f As New FrmIndividual
but it didn’t change anything.
I’m using VS 2022, v17.2.1. If there’s more info you need, please let me know.
The reason that you're told there's no Add method is because arrays have no Add method and you have an array. Here:
ReadOnly FormFlds As New List(Of FormFld)()
You are using the New keyword to invoke a constructor, so the parentheses at the end are for the argument list for that method call. Here:
Public FormFlds As List(Of FormFld)()
there's no New keyword so there's no constructor, so the parentheses at the end indicate that the field is an array type. That code is functionally equivalent to this:
Public FormFlds() As List(Of FormFld)
When you get that field you're getting a reference to an array of List(Of FormFld), not just a single List(Of FormFld) object. Of course, the field is initially Nothing anyway, so you'd have to assign something to it first to be able to use it.
By the way, the error message was already telling you what the issue was:
'Add' is not a member of 'List(Of FormFld)()`
It is telling you that Add is not a member of array of List(Of FormFld), which it's obviously not.

VB.NET Argument Prompt cannot be converted to type string

I'm made a new list of string and when I try to add something there it gives me an error: Argument 'Prompt' cannot be converted to type 'string
My code:
Dim variables As New List(Of String)
Try
variables.Append(CStr(TextBox1.Text))
variables.Append(CStr(TextBox2.Text))
MsgBox(variables)
Catch ex As Exception
MsgBox(ex.Message)
End Try
How can I fix that?
The error is coming from your use of MsgBox(). You're passing it the variables variable, and it doesn't know how to convert a List(Of String) to a String.
As stated by jmcilhinney, you should be using Add() instead of Append().
Additionally, you should use MessageBox.Show() instead of MsgBox().
As for the error, we can only assume you want to see all the current values in your List? If so, one solution is to use String.Join() and display that instead:
Dim variables As New List(Of String)
variables.Add(TextBox1.Text)
variables.Add(TextBox2.Text)
MessageBox.Show(String.Join(",", variables))
But your variables list should be declared at Form level so that you aren't creating a new one each time. It isn't clear from your post if this is the case or not.
You should be calling the Add instance method of your List(Of String). Append is an extension method and is not appropriate in that scenario. Append would be used for an enumerable list that you wanted to enumerate on the spot, e.g.
Dim names = {"Peter", "Paul", "Mary"}
Dim pNames = names.Where(Function(name) name.StartsWith("P"))
For Each pName In pNames.Append("Philip")
Console.WriteLine(pName)
Next
In that case the Append only affects the list being enumerated by the loop where it's used. The original list is unaffected.
You should be using the Add method:
Dim variables As List(Of String) = New List(Of String)
variables.Add(TextBox1.Text)
(No need for the redundant CStr as TextBox1.Text is already of type String.)
As #Mary will no doubt suggest - rightly! - always have Option Explicit and Option Strict set to On (and in my opinion Option Infer Off); it'll help you with any syntax issues.
And always pay attention to the syntax/compiler suggestions in the left margin which will give you clues/tips to fix or improve your code.
Lastly, refer to the Microsoft documentation if in doubt. It should be your first port-of-call if you're unsure of anything.
As several people have mentioned, use the .Add method not .Append.
The .Text property of a TextBox is already a String. No need to convert with CStr().
A message box displays a String. variables is not a String; it is a List(Of String). To see what is in your list use a For Each loop.
Private Sub OpCode()
Dim variables As New List(Of String)
variables.Add(TextBox1.Text)
variables.Add(TextBox2.Text)
For Each s In variables
MsgBox(s)
Next
End Sub
#SteveCinq is correct :-); turn on Option Strict.

String Function with No Paramaters Invoked with Int Should Fail Build

We have a VB.NET project which I am doing some refactoring in. A lot of current functions require a userId to be passed in as a parameter. For example:
Private Function Foo(userId As Integer) As String
Return "Foo"
End Function
But now, I have made this userId paramater no longer needed in much of our code.
I was thinking I would just remove all the userId parameters from all of the functions that don't need them anymore, and try re-building the project to see what calling code needs to change. To my surprise though, the code built successfully.
I learned that you can call a function which returns a string and doesn't take any parameters, but still pass in an Integer value. VB.NET builds/executes this code as if you were trying to invoke the function and then get the character from the String at the specified index. For example:
Imports System
Public Module MyModule
Private Function Foo() As String
Return "Foo"
End Function
Public Sub Main()
Console.WriteLine(Foo(0)) ' prints 'F'
Console.WriteLine(Foo(1)) ' prints 'o'
End Sub
End Module
(.NET Fiddle)
I want my build to fail in this case. I have tried using Option Strict On and Option Explicit On but neither seem to change this behaviour.
Is there anyway to make this kind of function invocation invalid in my VB.NET project?
Create an overload with no parameters which is a copy of the original function in every other respect.
Public Function Foo() As String
Return "Foo"
End Function
Now change the type of userId to something else (not Object or anything numeric etc.)
Public Function Foo(userId As DateTime) As String
Return "Foo"
End Function
Since there is an overload with a parameter, the compiler thinks you mean to call that, instead of indexing the chararray. So you have compile errors. Fix the errors
Then delete the original function.

Is there a way to determine the value of a property on a form that calls a method in a seperate class library

Specifically aimed at winforms development.
I suspect that the answer to this is probably No but S.O. has a nice way of introducing me to things I didn't know so I thought that I would ask anyway.
I have a class library with a number of defined methods therein. I know from personal experimentation that it is possible to get information about the application within which the class library is referenced. What I would like to know is whether it would be possible to get information about the value of a property of a control on a form when a routine on that form calls a method in my class library without passing a specific reference to that form as a parameter of the method in the class library?
So purely as an example (because it's the only thing I can think of off the top of my head). Is there a way that a message box (if it had been so designed to do so in the first place) could 'know' from which form a call to it had been made without that form being specifically referenced as a parameter of the message box in the first place?
Thanks for any insights you might have.
To address the example of the MessageBox, in many of the cases you can use the active form. You can retrieve it by using Form.ActiveForm. Of course, as regards the properties that you can request, you are limited to the properties provided by the Form or an interface that the Form implements and that the method in the other assembly also knows. To access other properties you can use Reflection, but this approach would neither be straightforward nor would it be clean.
In a more general scenario, you could provide the property value to the method as a parameter. If it is to complex to retrieve the value of the property and the value is not needed every time, you can provide a Func(Of TRESULT) to the method that retrieves the value like this (sample for an integer property):
Public Sub DoSomethingWithAPropertyValue(propValFunc As Func(Of Integer))
' Do something before
If propertyValueIsNeeded Then
Dim propVal = propValFunc()
End If
' Do something afterwards
End Sub
You call the method like this:
Public Sub SubInForm()
Dim x As New ClassInOtherAssembly()
x.DoSomethingWithAPropertyValue(Function() Me.IntegerProperty)
End Sub
I kind of question your intentions. There's no problem sending the information to a function or the constructor.
Instead of giving the information to the class, the class would ask for the information instead using an event.
Module Module1
Sub Main()
Dim t As New Test
AddHandler t.GetValue, AddressOf GetValue
t.ShowValue()
Console.ReadLine()
End Sub
Public Sub GetValue(ByRef retVal As Integer)
retVal = 123
End Sub
End Module
Class Test
Public Delegate Sub DelegateGetValue(ByRef retVal As Integer)
Public Event GetValue As DelegateGetValue
Public Sub ShowValue()
Dim val As Integer
RaiseEvent GetValue(val)
Console.WriteLine(val)
End Sub
End Class

Opposite to a Structure?

I'm sure there is an answer to this somewhere but I'm clearly using the wrong terminology in my searches, so I apologise in advance for this inevitably being a duplicate.
Take the function CType. Clearly I can cast (or at least try) a given object to a given reference type. The function will not work if trying to cast to a structure, i.e.
CType(myObject, Integer)
...will generate a compiler error. This I'm sure most often crops up when working with generics:
Public Function GetResults(Of T)() As T
Dim instance As T
Return CType(GetData(instance), T)
End Function
Public Function GetData(ByVal param As myClass) As myClass
'do stuff
Return param
End Function
Public Function GetData(byval param As Integer) As Integer
'do stuff
Return param
End Function
Public Function GetResults(ByVal param As Object) As Object
Throw New NotImplementedException
End Function
Probably not the best of examples but hopefully shows what I mean. The following will work:
Dim result = GetResult(Of myClass)
The following will fail on the CType
Dim result = GetResult(Of Integer)
I'm not a big fan of using exceptions/Try-Catch if logic can be applied, so although I could Try-Catch the CType, I'd rather find a way to route it through different code. I know with Generics I can use
Public Function GetResults(Of T As Structure) As T
What is the opposite? I want only reference types so that CType doesn't fail... I can't overload the T As Structure with a plain T because it considers them identical signatures, so surely there's a keyword I'm missing somewhere? ^^
Long question, probably only requries a one word answer (sorry)... Thanks in advance!
Public Function GetResults(Of T As Class) As T