How to get the value of a column using a dataview in vb.net - vb.net

I have a list of records and for Employee R1005, I need to check if that Employee has been Enabled for login alert (i.e EnableLoginAlert = Yes), then a button will be displayed.
CompanyID EmployeeNo EnableLoginAlert
10046 R1005 Yes
20041 Ajax12 No
47021 Drek Yes
I have tried the below codes:
If dCompanyDetails.Tables(0).Rows.Count > 0 Then
Dim dataView As DataView = dCompanyDetails.Tables(0).DefaultView
dataView.RowFilter = "EmployeeNo = '" & strEmployeeNumber & "'"
Dim svalue As String = dataView.Table.Rows(0).ItemArray(0).ToString()
If svalue = "No" Then
AlertButton.Visible = False
ElseIf svalue = "Yes" Then
{
//Do something else
}
End If
End If

If you are going to use a DataView then use it. This:
Dim svalue As String = dataView.Table.Rows(0).ItemArray(0).ToString()
is simply going back to the DataTable and using it, ignoring the DataView. The DataView contains DataRowView objects so get the one you need and use it. It is similar to a DataRow and you can use it the same way in this case:
Dim enableLoginAlert = CStr(dataView(0)("EnableLoginAlert")) = "Yes"
Now you have an actual Boolean that represents the state you want.
That's not how you should do it though. Generally speaking, you would use a DataView when you want to bind data. In fact, if you bind a DataTable then the data you see in the UI actually comes from the DefaultView. That's why you can filter and sort it. In this case, there are better options.
If you want to find a row by its primary key then the Rows collection of a DataTable has a Find method, e.g.
Dim row = dCompanyDetails.Tables(0).Rows.Find(strEmployeeNumber)
Dim enableLoginAlert = CStr(row("EnableLoginAlert")) = "Yes"
If you're searching by other than the primary key, the DataTable itself has a Select method. Because multiple rows may match, it returns an array, so you need to get the row out of that, e.g.
Dim row = dCompanyDetails.Tables(0).Select($"EmployeeNo = '{strEmployeeNumber}'").First()
Dim enableLoginAlert = CStr(row("EnableLoginAlert")) = "Yes"

If you want to look up a single row it's perhaps easiest to use LINQ:
Dim row = dCompanyDetails.Tables(0).Rows.Cast(Of DataRow).AsQueryable().FirstOrDefault(Function(r) r("EmployeeNo").ToString() = strEmployeeNumber)
If row IsNot Nothing AndAlso row("EnableLoginAlert").ToString() = "Yes" Then
...
..though I'd be the first to claim that using LINQ on base DataTables is very verbose, because of the Cast/AsQueryable. I'd use strongly typed DataTables (in a dataset); if you were to convert your code to using strongly typed tables it would look like:
Dim r = someDataSet.AProperTableName.FirstOrDefault(Function(r) r.EmployeeNo = strEmployeeNumber)
If r?.EnableLoginALert = "Yes" Then
...
...using strongly typed datatables is much less messy..
nb: You need to Imports System.Linq for these to work
That LINQ is the same thing as:
For Each r as DataRow in dCompanyDetails.Tables(0)
If r("EmployeeNo").ToString() = "R1005" AndAlso r("EnableLoginAlert").ToString() = "Yes" Then
...
You also have the option of using DataTable.Select (not a LINQ thing, though LINQ has a Select too)
Dim matchingRows = dCompanyDetails.Tables(0).Select($"[EmployeeNo] = '{strEmployeeNumber}'")
If matchingrows.Count > 0 AndAlso matchingRows(0)("EnableLoginAlert").ToString() = "Yes"

Related

How to Select Rows in a Datatable?

How can I use the Variable Itmnmbr instead of hard-coding its value, 'i-2051'?
Dim fr() As DataRow
Dim Itmnmbr As string = "i-2051"
fr = dt.Select("item = 'i-2051'")
The most direct way is to use an Interpolated String, which are available from Visual Studio 2015, VB.Net 14:
Dim Itmnmbr As string = "i-2051"
fr = dt.Select($"item = '{Itmnmbr}'")
As a suggestion, let's change the names of Variables / Fields so it's easier to read them and also understand what these objects are used for. For example:
Dim dt as New DataTable()
'[...]
Dim itemNunmber As string = "i-2051"
Dim filteredRows As DataRow() = dt.Select(...)
itemNunmber is easier to read than Itmnmbr and filteredRows is more explicit than fr. There are some convetions that most are used to, as dt for DataTable, ds for DataSet etc., in this context. Better be sure that when you read your code after some time you don't get mad with yourself :)
Note that an Interpolated String is the same as a string formatted with String.Format(), so these two are actually the same thing:
Dim filteredRows As DataRow() = dt.Select($"item = '{itemNumber}'")
Dim filteredRows As DataRow() = dt.Select(String.Format("item = '{0}'", itemNumber))
Setting Option Infer On (should be On already), to make use of local type inference, you can write:
Dim filteredRows = dt.Select($"item = '{itemNumber}'")
and let the compiler infer the Type. In Visual Studio, if you move the mouse pointer over the variable, it will tell you what Type that is.
You have other options, if you need more dynamic selections.
The DataTableExtensions (which require a Project Reference to the System.Data.DataSetExtensions assembly - usually already linked along with System.Data), let you use the the AsEnumerable() method.
In LINQ to Objects style:
Here, using the default string Comparer
Dim filteredRows =
dt.AsEnumerable().Where(Function(dr) dr("item").ToString().Equals(itemNumber))
Or in LINQ to SQL style:
Here, using the InvariantCulture for the comparison.
Dim filteredRows =
From row In dt.AsEnumerable()
Where row.Field(Of String)("item").Equals(itemNumber, StringComparison.InvariantCulture)
Select row
See also: StringComparison and Best practices for comparing strings in .NET
These two last methods don't return an array of DataRow objects references, but a EnumerableRowCollection. The advantage is (when you can make use of it) that the collection is returned only when you actually use it (the execution is deferred).
When used correctly, it can improve the performance of your code. Try it out.
Instead of DataTable.Select(), you could also filter your DataTable, using its DefaultView.RowFilter property.
dt.DefaultView.RowFilter = $"item = '{itemNumber}'"
' You can save the filter to restore it later, if needed
Dim previousFilter = dt.DefaultView.RowFilter
When you present the Rows of your DataTable, only the Rows that meet the criteria defined by the Filter are shown (e.g., in a DataGrid of sort).
As mentioned, you're working with References here. The Collection of Rows returned by DataTable.Select() contain references of the Rows in the DataTable.
For example, if you consider the Collection and the filtered DataTable:
Dim filteredRows = dt.Select($"item = '{itemNumber}'")
dt.DefaultView.RowFilter = $"item = '{itemNumber}'"
Assume that filteredRows contains a single Row. Then you apply a Filter.
If you now change the value of filteredRows(0)("item"):
filteredRows(0)("item") = "Some other value"
when you present your DataTable in a UI, no Rows will be shown, since the Filter is active and now none of the Rows meet the filter's criteria: setting filteredRows(0)("item") has changed the value of the Row it refers to.
To remove a Filter, set it to string.Empty:
dt.DefaultView.RowFilter = Sting.Empty
To restore the previously saved filter:
dt.DefaultView.RowFilter = previousFilter

vb.net form linq nullreferenceexception

I'm working on a Sindows Forms application to help keep inventory of some scanners. I'm using Linq2Sql, each table has an id column. On my repair history form. I'm trying to use the serial number from the inventory table so it goes to the database and looks up the sID from the table and it returns the correct value, but when I go to send all the entered data to the history table it gets a null reference exception.
Dim db As New DataClasses1DataContext
Dim rep As Scanner_Repair_History
Dim scan = (From Scanner_Inventory In db.Scanner_Inventories Where scannerid.Text = Scanner_Inventory.SN Select Scanner_Inventory.SID).FirstOrDefault
rep.SID = scan
rep.Date_Broken = datebroke.Value
rep.Description = description.Text
rep.Send_Date = senddate.Text
rep.Recieve_Date = recievedate.Text
rep.Cost = cost.Text
rep.PlantID = plantid.Text
rep.BID = brokenid.Text
rep.RMAnumber = rmanum.Text
db.Scanner_Repair_Histories.InsertOnSubmit(rep)
db.SubmitChanges()
is that me but you didn't instanciate your "rep" variable
You don't have a defined object for placement with a 'new' keyword but I am also curious if it is a system.type.
Update based on Jinx88909
You may be returning an entire POCO Object that may be null and have a null property. You can adjust this most times by doing a null condition if you are using .NET 4.5 and up. '?.' operator.
Dim db As New DataClasses1DataContext
'I need to be a new object and not instantiated as Nothing
Dim rep As New Scanner_Repair_History
'You have the potential for a nothing value here as 'FirstOrDefault' includes a potential Nothing'.
'I would get the entire object and then just a property of it after the fact
Dim scan = (From Scanner_Inventory In db.Scanner_Inventories Where scannerid?.Text = Scanner_Inventory?.SN Select Scanner_Inventory).FirstOrDefault?.Sid
If scan IsNot Nothing Then
rep.SID = scan 'Could you maybe want scan.Id or something similar?
rep.Date_Broken = datebroke.Value
rep.Description = description.Text
rep.Send_Date = senddate.Text
rep.Recieve_Date = recievedate.Text
rep.Cost = cost.Text
rep.PlantID = plantid.Text
rep.BID = brokenid.Text
rep.RMAnumber = rmanum.Text
db.Scanner_Repair_Histories.InsertOnSubmit(rep)
db.SubmitChanges()
Else
Console.WriteLine("I got nothing for you with the inputs you put in!")
End If

Syntax in using the checked item in CheckListBox

I am trying to create an if statement using a checklistbox. How can I make an if statement with my checked value in check list box? I tried using the code below just like in combobox:
If CheckedListBox1.CheckedItems = "AHRM" Then
But I got an error:
value of type cannot be converted to string.
CheckedListBox.CheckedItems returns a collection of all checked items. Not a single string.
You could check it's Count property and then take the first if you expect only one:
Dim checked As String = Nothing
Dim checkedItems = CheckedListBox1.CheckedItems
If checkedItems.Count <> 0 Then checked = checkedItems(0).ToString()
If checked = "AHRM" Then ....
If you expect multiple you could use following little LINQ query and Contains or String.Join(if you want to concat them):
Dim checkedItems = From obj In CheckedListBox1.CheckedItems.Cast(Of Object)()
Select checkedItemAsString = obj.ToString()
Dim containsAHRM As Boolean = checkedItems.Contains("AHRM")
Dim allChecked = String.Join(",", checkedItems)
Dim values As [String] = ""
For i As Integer = 0 To CheckBoxList1.Items.Count - 1
If CheckBoxList1.Items(i).Selected Then
values = CheckBoxList1.Items(i).Value
If values = "AHRM" Then
// your code
end if
End If
Next

Access a form's control by name

not sure whether the title of this post is accurate.
I'm trying to access windows form controls and their properties by "composing" their name within a loop, but I can't seem to find the related documentation. Using VB.net. Basically, say I have the following:
Dim myDt As New DataTable
Dim row As DataRow = myDt.NewRow()
row.Item("col01") = Me.label01.Text
row.Item("col02") = Me.label02.Text
'...
row.Item("colN") = Me.labelN.Text
I'd like to write a for loop instead of N separate instructions.
While it's simple enough to express the left-hand side of the assignments, I'm stumped when it comes to the right-hand side:
For i As Integer = 1 to N
row.Item(String.format("col{0:00}", i)) = ???
' ??? <- write "label" & i (zero-padded, like col) and use that string to access Me's control that has such name
Next
As an extra, I'd like to be able to pass the final ".Text" property as a string as well, for in some cases I need the value of the "Text" property, in other cases the value of the "Value" property; generally speaking, the property I'm interested in might be a function of i.
Cheers.
You could use the ControlsCollection.Find method with the searchAllChildren option set to true
For i As Integer = 1 to N
Dim ctrl = Me.Controls.Find(string.Format("label{0:00}", i), True)
if ctrl IsNot Nothing AndAlso ctrl.Length > 0 Then
row.Item(String.format("col{0:00}", i)) = ctrl(0).Text
End If
Next
An example on how to approach the problem using reflection to set a property that you identify using a string
Dim myLabel As Label = new Label()
Dim prop as PropertyInfo = myLabel.GetType().GetProperty("Text")
prop.SetValue(myLabel, "A Label.Text set with Reflection classes", Nothing)
Dim newText = prop.GetValue(myLabel)
Console.WriteLine(newText)

VB.NET Array/Hashtable Issue

I am trying to make an array of hashtables. I don't know if this is the best solution, in PHP I would just do a multi-dim array, but it's not so easy in .NET. I am pretty new o VB, so if there is a better solution for this please explain.
I have 2 emails fields for a contact, and a contact can have many. I just want to load in the first two on the edit page for editing, whatever they may be.
Private Sub loadPrimaryContactEmails(ByVal db As CRMDataDataContext, ByVal contactID As Guid)
Dim q = (From ce In db.ContactEmails Where ce.ContactID = contactID Select ce).Take(2)
Dim Emails As Array
Dim count = 0
For Each email In q
Emails(count) = New Hashtable
Emails(count).Add("email", email.Email)
Emails(count).Add("label", email.Label)
Emails(count).Add("id", email.ContactEmailID)
count = count + 1
Next
txtPCEmail1.Text = Emails(0).Item("email")
txtPCEmail1Label.Text = Emails(0).Item("label")
lblEmail1ID.Text = Emails(0).Item("id")
txtPCEmail2.Text = Emails(1).Item("email")
txtPCEmail2Label.Text = Emails(1).Item("label")
lblEmail2ID.Text = Emails(1).Item("id")
End Sub
I get the error the first time I try to reference my array:
txtPCEmail1.Text = Emails(0).Item("email")
The error is:
Object variable or With block variable not set.
It builds, so I thought it might work. I can't just loop through my datasource because I have to explicitly set textbox fields. Is there a better way to go about doing this? Or is there a way to make an array of hashtables work?
EDIT - here is the good code:
So I went w/ the HybridDictionary...
Private Sub loadPrimaryContactEmails(ByVal db As CRMDataDataContext, ByVal contactID As Guid)
Dim q = (From ce In db.ContactEmails Where ce.ContactID = contactID Select ce).Take(2)
Dim Emails As New HybridDictionary()
Dim count = 1
For Each email In q
Emails.Add("email" + NCStr(count), email.Email)
Emails.Add("label" + NCStr(count), email.Label)
Emails.Add("id" + NCStr(count), email.ContactEmailID)
count = count + 1
Next
txtPCEmail1.Text = Emails("email1")
txtPCEmail1Label.Text = Emails("label1")
lblEmail1ID.Text = Emails("id1")
txtPCEmail2.Text = Emails("email2")
txtPCEmail2Label.Text = Emails("label2")
lblEmail2ID.Text = Emails("id2")
End Sub
SO yeah, kind of a hack, but I don't feel like I should have to making special methods just to load some data into a dictionary or array or whatever.
Arrays in VB.NET are different than in PHP. You will need to define the size of your array before attempting to set elements of the array.
Better yet, consider using the generic List<T> collection.
Yes Phil is right you haven't specified the Initial Size of the Array.
And as suggested by him Use generic list or I would recommend
"System.Collections.Specialized.StringCollection" Class or "System.Collections.Specialized.HybridDictionary" class
Build the hashtable first and then build the array.
Dim hash As New Hashtable()
hash.Add("Header", shortModel)
hash.Add("SpecInfo", specinfo)
hash.Add("SerialNumber", serie & "-L")
hash.Add("SerialNumber2", serie)
hash.Add("seriel", serie & "-L")
hash.Add("serie", serie)
hash.Add("Product", modelBase)
hash.Add("varBC", bc)
hash.Add("box_id", boxId.Substring(4).ToString)
Dim dt As DataTable = DbUtil.GetCursor("SFISM4.PKG_AGENCY.sp_get_print_param", {New OracleParameter("in_serie", "3CE5151ZW4")})
For Each row As DataRow In dt.Rows
hash.Add(row("NAME"), row("VALUE"))
Next
Dim mArray(hash.Count() - 1, 1) As String
Dim i As Integer = 0
For Each row As DictionaryEntry In hash
mArray(i, 0) = row.Key.ToString()
mArray(i, 1) = row.Value.ToString()
i = i + 1
Next