Windows forms CheckedListBox issue - vb.net

I am working on a desktop application developed in vb.net. I am trying to select the items in a checkedlistbox depending on the values I get from database. Below is the code to populate the checkedlistboxes
Private Sub LoadDisapprovalList()
cblFedralReasons.Items.Clear()
cblStateReasons.Items.Clear()
cblFedralReasons.DataSource = Main.DataClient.DisapprovalReasonList_Get(FedralReason)
cblFedralReasons.DisplayMember = "DisapprovalReasonTypeDesc"
cblFedralReasons.ValueMember = "DisapprovalReasonTypeGenId"
cblStateReasons.DataSource = Main.DataClient.DisapprovalReasonList_Get(StateReason)
cblStateReasons.DisplayMember = "DisapprovalReasonTypeDesc"
cblStateReasons.ValueMember = "DisapprovalReasonTypeGenId"
End Sub
After that I am trying to select the items based on the values from database. Here is the code
Private Sub LoadApplicationDisapprovalReasons()
Dim lstApplicationDisapprovalReasons As New List(Of DataService.usp_ApplicationDisapprovalReason_Get_Result)
lstApplicationDisapprovalReasons = Main.DataClient.ApplicationDisapprovalReason_Get(_SeqID)
If lstApplicationDisapprovalReasons.Count > 0 Then
For Each item In lstApplicationDisapprovalReasons
Dim selectedDisapprovalId As Integer = item.DisapprovalReasonTypeGenId
Select Case item.DisapprovalReasonType
Case FedralReason
Dim selectedIndex = cblFedralReasons.Items.IndexOf(selectedDisapprovalId)
cblFedralReasons.SetItemCheckState(selectedIndex, CheckState.Checked)
Case StateReason
Dim selectedIndex = cblStateReasons.Items.IndexOf(selectedDisapprovalId)
cblStateReasons.SetItemCheckState(selectedIndex, CheckState.Checked)
End Select
Next
End If
End Sub
But the problem is cblFedralReasons.Items.IndexOf always returns -1. All the data from database is coming correctly but something weird happening with checkedlistbox which I couldn't understand.
EDIT:
Also when I try to get the text of an item by using the following code it returns me name of my collections instead of the text.
cblFedralReasons.items(1).tostring
It returns
DisapprovalReasonList
and not the text of that item!

I'll try to explain what I think about this:
If cblFedralReasons has as Datasource a List(Of DataService.usp_DisapprovalReasonList), if you search a selectedDisapprovalId vía IndexOf passing an Integer on the list.... that -1 value returned, its coherent.
IndexOf, on a collection, are internally doing a Equals comparison. So you are comparing different types: an Integer vs a DataService.usp_DisapprovalReasonList.
There are many ways to get the correct object from the collection.
One idea could be do an override of object.equals in your class:
Public Overrides Function Equals(ByVal p_oAnotherObject As Object) As Boolean
If TypeOf p_oAnotherObject Is DataService.usp_DisapprovalReasonList AndAlso Me.GetType.Equals(p_oAnotherObject.GetType) Then
Return Me.DisapprovalReasonTypeGenId.Equals(DirectCast(p_oAnotherObject, DataService.usp_DisapprovalReasonList).DisapprovalReasonTypeGenId)
Else
Return False
End If
End Function
Assuming you have a constructor accepting an ID, you now can do this:
cblFedralReasons.Items.IndexOf(New DataService.usp_DisapprovalReasonList(selectedDisapprovalId))
and then, you will get it.
Finally, cblFedralReasons.items(1).tostring, you are getting the default GetType.Name. Do this in your class, then:
Public Overrides Function ToString() As String
Return DisapprovalReasonTypeDesc
End Function
Hope I have explained.

Related

Combining two list, only records with 1 specific unique property

I'm combining two lists in visual basic. These lists are of a custom object. The only record I want to combine, are the once with a property doesn't match with any other object in the list so far. I've got it running. However, the first list is just 1.247 records. The second list however, is just short of 27.000.000 records. The last time I successfully merged the two list with this restriction, it took over 5 hours.
Usually I code in C#. I've had a similar problem there once, and solved it with the any function. It worked perfectly and really fast. So as you can see in the code, I tried that here too. However it takes way too long.
Private Function combineLists(list As List(Of Record), childrenlist As List(Of Record)) As List(Of Record) 'list is about 1.250 entries, childrenlist about 27.000.000
For Each r As Record In childrenlist
Dim dublicate As Boolean = list.Any(Function(record) record.materiaalnummerInfo = r.materiaalnummerInfo)
If Not dublicate Then
list.Add(r)
End If
Next
Return list
End Function
The object Record looks like this ( I wasn't sure how to make a custom object in VB, and this looks bad, but it worked):
Public Class Record
Dim materiaalnummer As String
Dim type As String 'Config or prefered
Dim materiaalstatus As String
Dim children As New List(Of String)
Public Property materiaalnummerInfo()
Get
Return materiaalnummer
End Get
Set(value)
materiaalnummer = value
End Set
End Property
Public Property typeInfo()
Get
Return type
End Get
Set(value)
type = value
End Set
End Property
Public Property materiaalstatusInfo()
Get
Return materiaalstatus
End Get
Set(value)
materiaalstatus = value
End Set
End Property
Public Property childrenInfo()
Get
Return children
End Get
Set(value)
children = value
End Set
End Property
End Class
I was hoping that someone could point me in the right direction to shorten the time needed. Thank you in advance.
I'm not 100% sure what you want the output to be such as all differences or just ones from the larger list etc but I would definitely try do it with LINQ! Basically sql for vb.net data so would something similar to this:
Dim differenceQuery = list.Except(childrenlist)
Console.WriteLine("The following lines are in list but not childrenlist")
' Execute the query.
For Each name As String In differenceQuery
Console.WriteLine(name)
Next
Also side-note i would suggest not calling one of the lists "list" as it is bad practice and is a in use name on the vb.net system
EDIT
Please try this then let me know what results come back.
Private Function combineLists(list As List(Of Record), childrenlist As List(Of Record)) As List(Of Record) 'list is about 1.250 entries, childrenlist about 27.000.000
list.AddRange(childrenlist) 'combines both lists
Dim result = From v In list Select v.materiaalnummerInfo Distinct.ToList
'result hopefully may be a list with all distinct values.
End Function
Or Don't combine them if you dont want to.

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.

Sum the counts with Lambda expressions

I have a List(of T) where T has a property that is a list of checkboxes, what I need is a Lambda expression that will count all the checked checkboxes in the list.
I tried with:
Me.list.Sum(Function(objT) objT.CheckBoxes.Where(Function(chk) chk.Checked).Count)
But it didn't do the trick. Any suggestions?
Dim count = (From box In objtCheckBoxes Where box.Checked Select box).Count()
Somehow you must tell VB that T has a property CheckBoxes. Therefore define an interface that the types you add to the list must implement
Public Interface ICheckBoxes
ReadOnly Property CheckBoxes() As List(Of CheckBox)
End Interface
Public Class ClassWithCkeckBoxes
Implements ICheckBoxes
Private m_CheckBoxes As List(Of CheckBox) = New List(Of CheckBox)
Public ReadOnly Property CheckBoxes() As List(Of CheckBox) Implements ICheckBoxes.CheckBoxes
Get
Return m_CheckBoxes
End Get
End Property
End Class
Specify that T must implement ICheckBoxes with the type parameter (Of T As ICheckBoxes)
Class CountCheckBoxes(Of T As ICheckBoxes)
Public Sub Test(ByVal list As List(Of T))
Dim count As Integer = list _
.SelectMany(Function(t) t.CheckBoxes) _
.Count(Function(checkBox) checkBox.Checked)
End Sub
End Class
SelectMany flattens the list. I.e., it converts the list of list of checkboxes into a simple list of checkboxes.
You'll need to use SelectMany. Something like this may work.
Me.list.SelectMany(Function(objT) objT.Checkboxes)
.Count(Function(chk) chk.Checked)
At the end there was a problem populating the list which translated in an always 0 answer. Fixing the method that populated the list gave me the correct answer, which means the lambda expression per se was right all along.
P.S. sorry for all those other answers, I did read them all so it wasn't time wasted guys ;)
Dim b As New List(Of Boolean)
b.AddRange({True, False, True})
Dim n = b.Sum(Function(x)
Return If(x, 1, 0)
End Function)
n now contains 2.

How can I copy an object of an unknown type in VB.net?

Rather than giving the very specific case (which I did earlier), let me give a general example. Let's say that I have a function, called callingFunction. It has one parameter, called parameter. Parameter is of an unknown type. Let us then say that I wish to copy this parameter, and return it as a new object. For example, in pseudo code, something along the lines of...
Function callingFunction(ByVal parameter As Object) As Object
Dim newObj As New Object
'newObj has the same value as parameter, but is a distinctly different object
'with a different reference
newObj = parameter
return newObj
End Function
EDIT: Additional Information
The first time I posted this question, I received only one response - I felt that perhaps I made the question too specific. I guess I will explain more, perhaps that will help. I have an ASP page with 10 tables on it. I am trying, using the VB code behind, to come up with a single solution to add new rows to any table. When the user clicks a button, a generic "add row" function should be called.
The difficulty lies in the fact that I have no guarantee of the contents of any table. A new row will have the same contents as the row above it, but given that there are 10 tables, 1 row could contain any number of objects - text boxes, check boxes, etc. So I want to create a generic object, make it of the same type as the row above it, then add it to a new cell, then to a new row, then to the table.
I've tested it thoroughly, and the only part my code is failing on lies in this dynamic generation of an object type. Hence why I asked about copying objects. Neither of the solutions posted so far work correctly, by the way. Thank you for your help so far, perhaps this additional information will make it easier to provide advice?
You can't do this in general. And it won't be a good idea, for example, if parameter is of a type which implements the singleton pattern. If parameter is of a type which supports copying, it should implement the ICloneable interface. So, your function could look like this:
Function MyFunc(ByVal parameter As Object) As Object
Dim cloneableObject As ICloneable = TryCast(parameter, ICloneable)
If Not cloneableObject Is Nothing Then
Return cloneableObject.Clone()
Else
Return Nothing
End If
End Function
You could implement something like this:
Dim p1 As Person = New Person("Tim")
Dim p2 As Object = CloneObject(p1)
Dim sameRef As Boolean = p2 Is p1 'false'
Private Function CloneObject(ByVal o As Object) As Object
Dim retObject As Object
Try
Dim objType As Type = o.GetType
Dim properties() As Reflection.PropertyInfo = objType.GetProperties
retObject = objType.InvokeMember("", System.Reflection.BindingFlags.CreateInstance, Nothing, o, Nothing)
For Each propertyInfo As PropertyInfo In properties
If (propertyInfo.CanWrite) Then
propertyInfo.SetValue(retObject, propertyInfo.GetValue(o, Nothing), Nothing)
End If
Next
Catch ex As Exception
retObject = o
End Try
Return retObject
End Function
Class Person
Private _name As String
Public Property Name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
Public Sub New()
End Sub
Public Sub New(ByVal name As String)
Me.Name = name
End Sub
End Class
Here's a simple class that will work for most objects (assumes at least .Net 2.0):
Public Class ObjectCloner
Public Shared Function Clone(Of T)(ByVal obj As T) As T
Using buffer As MemoryStream = New MemoryStream
Dim formatter As New BinaryFormatter
formatter.Serialize(buffer, obj)
buffer.Position = 0
Return DirectCast(formatter.Deserialize(buffer), T)
End Using
End Function
End Class

VB.Net List.Find. Pass values to predicate

Having a bit of trouble using the List.Find with a custom predicate
i have a function that does this
private function test ()
Dim test As Integer = keys.Find(AddressOf FindByOldKeyAndName).NewKey
here's the function for the predicate
Private Shared Function FindByOldKeyAndName(ByVal k As KeyObj) As Boolean
If k.OldKey = currentKey.OldKey And k.KeyName = currentKey.KeyName Then
Return True
Else
Return False
End If
End Function
by doing it this way means i have to have a shared "currentKey" object in the class, and i know there has to be a way to pass in the values i'm interested in of CurrentKey (namely, keyname, and oldkey)
ideally i'd like to call it by something like
keys.Find(AddressOf FindByOldKeyAndName(Name,OldVal))
however when i do this i get compiler errors.
How do i call this method and pass in the values?
You can cleanly solve this with a lambda expression, available in VS2008 and up. A silly example:
Sub Main()
Dim lst As New List(Of Integer)
lst.Add(1)
lst.Add(2)
Dim toFind = 2
Dim found = lst.Find(Function(value As Integer) value = toFind)
Console.WriteLine(found)
Console.ReadLine()
End Sub
For earlier versions you'll have to make "currentKey" a private field of your class. Check my code in this thread for a cleaner solution.
I have an object that manages a list of Unique Property Types.
Example:
obj.AddProperty(new PropertyClass(PropertyTypeEnum.Location,value))
obj.AddProperty(new PropertyClass(PropertyTypeEnum.CallingCard,value))
obj.AddProperty(new PropertyClass(PropertyTypeEnum.CallingCard,value))
//throws exception because property of type CallingCard already exists
Here is some code to check if properties already exist
Public Sub AddProperty(ByVal prop As PropertyClass)
If Properties.Count < 50 Then
'Lets verify this property does not exist
Dim existingProperty As PropertyClass = _
Properties.Find(Function(value As PropertyClass)
Return value.PropertyType = prop.PropertyType
End Function)
'if it does not exist, add it otherwise throw exception
If existingProperty Is Nothing Then
Properties.Add(prop)
Else
Throw New DuplicatePropertyException("Duplicate Property: " + _
prop.PropertyType.ToString())
End If
End If
End Sub
I haven't needed to try this in newer versions of VB.Net which might have a nicer way, but in older versions the only way that I know of would be to have a shared member in your class to set with the value before the call.
There's various samples on the net of people creating small utility classes to wrap this up to make it a little nicer.
I've found a blog with a better "real world" context example, with good variable names.
The key bit of code to Find the object in the list is this:
' Instantiate a List(Of Invoice).
Dim invoiceList As New List(Of Invoice)
' Add some invoices to List(Of Invoice).
invoiceList.Add(New Invoice(1, DateTime.Now, 22))
invoiceList.Add(New Invoice(2, DateTime.Now.AddDays(10), 24))
invoiceList.Add(New Invoice(3, DateTime.Now.AddDays(30), 22))
invoiceList.Add(New Invoice(4, DateTime.Now.AddDays(60), 36))
' Use a Predicate(Of T) to find an invoice by its invoice number.
Dim invoiceNumber As Integer = 1
Dim foundInvoice = invoiceList.Find(Function(invoice) invoice.InvoiceNumber = invoiceNumber)
For more examples, including a date search, refer to Mike McIntyre's Blog Post