Linq ToList does nothing - vb.net

I have Option Strict and Option Infer both set "On".
This code works fine:
Dim tBoxes = From t In MainForm.Frame2.Controls.OfType(Of TextBox).ToList
tBoxes.ToList().ForEach(Sub(c) c.DataBindings.Clear())
Why can't I combine them into the one line below (I believe it's related to the fact that the first line above does not set tBoxes to a list but remains an IEnumerable even though I am calling ToList, why is this?)
Dim tBoxes = From t In MainForm.Frame2.Controls.OfType(Of TextBox).ToList.ForEach(Sub(c) c.DataBindings.Clear())
This code results in an error
Expression does not produce a value
This might seem like much ado about nothing but it's not just the reduction to one line, I'd like to understand what's going on here.
VB.NET 2010

The problem is not the ToList call, but List.ForEach Method which is Sub, hence does not have a result and cannot be assigned to a variable.
If you want to use a single line, remove Dim tBoxes =.
Update In fact there is another problem in the above code.
Dim tBoxes = From t In MainForm.Frame2.Controls.OfType(Of TextBox).ToList
is equivalent to
Dim tBoxList = MainForm.Frame2.Controls.OfType(Of TextBox).ToList
Dim tBoxes = From t in tBoxList
so obviously tBoxes is IEnumerable<TextBox>.
Since the from t In .. part is unnecessary in this case, the "oneliner" should be something like this
MainForm.Frame2.Controls.OfType(Of TextBox).ToList.ForEach(Sub(c) c.DataBindings.Clear())
If you really need a query part, to avoid such confusions, don't forget to enclose it in (..) before calling ToList or other methods like Count, Any etc., like this
(from t In MainForm.Frame2.Controls.OfType(Of TextBox)).ToList.ForEach(Sub(c) c.DataBindings.Clear())

Small description but enough to understand
From t In MainForm.Frame2.Controls.OfType(Of TextBox) 'Filter all object of type text box
.ToList 'Convert IEnemerable(Of TextBox) to a IList type.
.ForEach(Sub(c) c.DataBindings.Clear())' Iterate through list and remove bindg of each text box
Issue is that .ForEach does not return any value so that there is nothing to assign the tBoxes object that you have created. It is just like a void method or Sub in VB.net.

Related

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.

GetType does not exist a Reflection puzzle in VB

This is a real basic schoolboy level error but I've not figured it out yet despite having used a fair amount of reflection in my generic updater class.
The puzzle is as follows: I want to get the type of a value that I've extracted in the code sample. Using that type I wish to compare it with another extracted value. To compare it correctly with Option Strict On I must directCast the extracted value to the type that the PropertyInfo says it is.
The code below simply illustrates my using it in Directcast or Ctype versions.
For Each p As PropertyInfo In _validProperties
Dim NewValue = p.GetValue(UpdateItem)
Dim x = CType(NewValue, p.PropertyType) 'Error p.PropertyType does not exist --How?
'Elided
Next
'Alternative approach
For Each p As PropertyInfo In _validProperties
Dim NewValue = p.GetValue(UpdateItem)
Dim x = DirectCast(NewValue, p.PropertyType) 'Error p.PropertyType does not exist --How?
'Elided
Next
I've tried explicitly putting the newly discovered type in it's own variable and it does exist.
This is an odd little trap I've had a few times and usually solved but this one is not budging. Therefore I'm fundementally misunderstanding how I need to use both the instance x.Gettype (OR GetType(Class) syntaxes).
Whatever I do, p.PropertyType does not exist.
Can anyone clear up my foggy mind on this please?
Edit: For now, Option Strict is off merely to allow this to work.
For Each p In _validProperties
Dim NewValue = p.GetValue(UpdateItem), OriginalValue = p.GetValue(originalItem)
If (p.PropertyType.Name.Contains("Nullable") AndAlso Not Nullable.Equals(NewValue, OriginalValue)) _
OrElse NewValue <> OriginalValue Then p.SetValue(originalItem, NewValue)
Next
All my tests pass so I'll live with it for now. I simply don't want the update to fire if there is no difference between the values on the properties in question.

type var is not defined vb.net

I found an example in C# and from my understanding there is no alternative to 'var' in VB.NET. I am trying to create a datatable that will populate depending on a LINQ command further down in my code that calls this function. I have searched for a solution, but unable to find anything that works. Any assistance on what I should use would be appreciated. Note that I do have both Option Strict and Option Infer on as well.
Private Shared Function ToDataTable(rows As List(Of DataRow)) As DataTable
Dim table As New DataTable()
table.Columns.Add("Title")
table.Columns.Add("Console")
table.Columns.Add("Year")
table.Columns.Add("ESRB")
table.Columns.Add("Score")
table.Columns.Add("Publisher")
table.Columns.Add("Developer")
table.Columns.Add("Genre")
table.Columns.Add("Date")
For Each row As var In rows
table.Rows.Add(row.ItemArray)
Next
Return table
End Function
C# uses 'var' for implicit typing - VB uses Option Infer On combined with omitting the type.
The VB equivalent is:
Option Infer On
...
For Each row In rows
table.Rows.Add(row.ItemArray)
Next row
.NET already has .CopyToDataTable extension for that:
Dim table As DataTable = rows.CopyToDataTable
The VB equivalent is simply Dim, without any strong typing.
Dim sName = "John Henry"
In this example, the compiler infers type String (when Option Infer is set to On).
In your example, you may omit the As var portion. The compiler will infer type DataRow.
Tag your questions well, in this case there is no C# issue. Your problem is your are not writing an actual type on the foreach statement. This will fix it:
For Each row As DataRow In rows
table.Rows.Add(row.ItemArray)
Next

Range variable name cannot match the name of a member of the 'Object' class. when querying a DataGridView using Linq

I am trying to Query a DataGridViewRowsCollection object using LINQ so I don't need a for loop. I want to get the first cells out what is a String and put it in a generic list.
Based on my knowledge and research the query should be the following:
Dim Result As List(Of String) = (From row In gridMappingClasses.Rows.Cast(Of DataGridViewRow)()
Select row.Cells(0).Value.ToString).ToList()
but it fails with the following error: Range variable name cannot match the name of a member of the 'Object' class.
But if I remove the ToString method call and change Result to List(of object) it works fine. Btw I am using Strict on.
Can anyone help?
Try giving it an alias (ToString is already a member of Object, can't have another one):
(From row In gridMappingClasses.Rows.Cast(Of DataGridViewRow)()
Select v = row.Cells(0).Value.ToString).toList
But there is nothing wrong with having a for loop. You should generally use LINQ for simple queries that don't cause any debugging headache. If a LINQ query starts to become problematic, it's time to rewrite it as a loop. In your case it would have resolved the problem.

ComboBox DataBinding DisplayMember and LINQ queries

Update
I decided to iterate through the Data.DataTable and trimmed the values there.
Utilizing SirDemon's post, I have updated the code a little bit:
Sub test(ByVal path As String)
Dim oData As GSDataObject = GetDataObj(path)
EmptyComboBoxes()
Dim oDT As New Data.DataTable
Try
Dim t = From r In oData.GetTable(String.Format("SELECT * FROM {0}gsobj\paths ORDER BY keyid", AddBS(path))) Select r
If t.Count > 0 Then
oDT = t.CopyToDataTable
For Each dr As Data.DataRow In oDT.Rows
dr.Item("key_code") = dr.Item("key_code").ToString.Trim
dr.Item("descript") = dr.Item("descript").ToString.Trim
Next
dataPathComboBox.DataSource = oDT
dataPathComboBox.DisplayMember = "descript"
dataPathComboBox.ValueMember = "key_code"
dataPathComboBox.SelectedIndex = 0
dataPathComboBox.Enabled = True
End If
Catch ex As Exception
End Try
End Sub
This works almost as I need it to, the data is originally from a foxpro table, so the strings it returns are <value> plus (<Field>.maxlength-<value>.length) of trailing whitespace characters. For example, a field with a 12 character length has a value of bob. When I query the database, I get "bob_________", where _ is a space.
I have tried a couple of different things to get rid of the whitespace such as:
dataPathComboBox.DisplayMember.Trim()
dataPathComboBox.DisplayMember = "descript".Trim.
But nothing has worked yet. Other than iterating through the Data.DataTable or creating a custom CopyToDataTable method, is there any way I can trim the values? Perhaps it can be done in-line with the LINQ query?
Here is the code I have so far, I have no problem querying the database and getting the information, but I cannot figure out how to display the proper text in the ComboBox list. I always get System.Data.DataRow :
Try
Dim t = From r In oData.GetTable("SELECT * FROM ../gsobj/paths ORDER BY keyid") _
Select r
dataPathComboBox.DataSource = t.ToList
dataPathComboBox.SelectedIndex = 0
'dataPathComboBox.DisplayMember = t.ToList.First.Item("descript")
dataPathComboBox.Enabled = True
Catch ex As Exception
Stop
End Try
I know that on the DisplayMember line the .First.Item() part is wrong, I just wanted to show what row I am trying to designate as the DisplayMember.
I'm pretty sure your code tries to set an entire DataRow to a property that is simply the name of the Field (in a strongly type class) or a Column (in a DataTable).
dataPathComboBox.DisplayMember = "descript"
Should work if the DataTable contains a retrieved column of that name.
Also, I'd suggest setting your SelectedIndex only AFTER you've done the DataBinding and you know you actually have items, otherwise SelectedIndex = 0 may throw an exception.
EDIT: Trimming the name of the bound column will trim just that, not the actual bound value string. You either have to go through all the items after they've been bound and do something like:
dataPathComboBox.Item[i].Text = dataPathComboBox.Item[i].Text.Trim()
For each one of the items. Not sure what ComboBox control you're using, so the item collection name might be something else.
Another solution is doing that for each item when it is bound if the ComboBox control exposes an onItemDataBound event of some kind.
There are plenty of other ways to do this, depending on what the control itself offers and what you choose to do.
DisplayMember is intended to indicate the name of the property holding the value to be displayed.
In your case, I'm not sure what the syntax will by since you seem to be using a DataSet, but that should be
... DisplayMember="Item['descript']" ...
in Xaml, unless you need to switch that at runtime in which case you can do it in code with
dataPathComboBox.DisplayMember = "Item['descript']"
Again, not 100% sure on the syntax. If you are using a strongly typed DataSet it's even easier since you should have a "descript" property on your row, but given hat your error indicates "System.DataRow" and not a custom type, I guess you are not.
Because I can't figure out the underlying type of the datasource you are using I suggest you to change commented string to
dataPathComboBox.DisplayMember = t.ElementType.GetProperties.GetValue(0).Name
and try to determine correct index (initially it is zero) in practice.