How to use a GetType and pass it to a function - vb.net

why doesn't passing a datatype of an object to a function work? and how do you get around it?
Dim MyObj as new CustomObj
Dim t As Type = MyObj.GetType
Call My_Fuction(Of t)
I'm saving serializable objects to file, then open them up later on, and then the code needs to find the UI based on the object datatype, so it can populate the UI from the object
Private Function My_Fuction(Of t As Base_Object)() As UserControl
Dim UI_Type As Type = GetType(UI_Common_Panel(Of t))
For Each Object_type As Type In Project_Solution.GetTypes()
For Each Itype As Type In Object_type.GetInterfaces()
If Itype Is UI_Type Then Return DirectCast(Activator.CreateInstance(Object_type), UI_Common_Panel(Of t))
Next
Next
Return Nothing
End Function

It is difficult to give a good answer because you have all custom classes in your question. If the problem you are trying to solve is to create a new control based on the object's Type, here is a way to do that using built-in controls:
Function GetControl(o As Object) As Control
If o.GetType Is GetType(Boolean) Then
Return New CheckBox With {.Checked = DirectCast(o, Boolean)}
ElseIf o.GetType Is GetType(Date) Then
Return New DateTimePicker With {.Value = DirectCast(o, Date)}
ElseIf o.GetType Is GetType(String) Then
Return New TextBox With {.Text = DirectCast(o, String)}
Else
Return New TextBox With {.Text = o.ToString}
End If
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim c As Control = GetControl("Hello, world!")
Me.Controls.Add(c)
c.Visible = True
Dim c2 As Control = GetControl(#05/04/2017#)
Me.Controls.Add(c2)
c2.Visible = True
c2.Top = 100
End Sub

Related

How to check elements from an array in a CheckListBox

i have a checklisbox with some value, let's say
"Apple"
"Peach"
"Lemon"
These values came from a dataset.
I have an array with Apple and Lemon: {"Apple", "Lemon"}.
How to check in the checklistbox each value read in this array?
EDIT: In my case, the checklistbox was populate using a dataset provided by a SQL query
In the following code sample, data from SQL-Server (database doesn't matter but this is what I used, what is important is the container the data is loaded into is loaded into a list.
Container to hold data
Public Class Category
Public Property Id() As Integer
Public Property Name() As String
Public Overrides Function ToString() As String
Return Name
End Function
End Class
Class to read data
Imports System.Data.SqlClient
Public Class SqlOperations
Private Shared ConnectionString As String =
"Data Source=.\SQLEXPRESS;Initial Catalog=NorthWind2020;Integrated Security=True"
Public Shared Function Categories() As List(Of Category)
Dim categoriesList = New List(Of Category)
Dim selectStatement = "SELECT CategoryID, CategoryName FROM Categories;"
Using cn As New SqlConnection With {.ConnectionString = ConnectionString}
Using cmd As New SqlCommand With {.Connection = cn}
cmd.CommandText = selectStatement
cn.Open()
Dim reader = cmd.ExecuteReader()
While reader.Read()
categoriesList.Add(New Category() With {.Id = reader.GetInt32(0), .Name = reader.GetString(1)})
End While
End Using
End Using
Return categoriesList
End Function
End Class
Extension method
Which can check or uncheck a value if found in the CheckedListBox and is case insensitive.
Public Module Extensions
<Runtime.CompilerServices.Extension>
Public Function SetCategory(sender As CheckedListBox, text As String, Optional checkedValue As Boolean = True) As Boolean
If String.IsNullOrWhiteSpace(text) Then
Return False
End If
Dim result = CType(sender.DataSource, List(Of Category)).
Select(Function(item, index) New With
{
Key .Column = item,
Key .Index = index
}).FirstOrDefault(Function(this) _
String.Equals(this.Column.Name, text, StringComparison.OrdinalIgnoreCase))
If result IsNot Nothing Then
sender.SetItemChecked(result.Index, checkedValue)
Return True
Else
Return False
End If
End Function
End Module
Form code
Public Class ExampleForm
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
CheckedListBox1.DataSource = SqlOperations.Categories
End Sub
Private Sub CheckCategoryButton_Click(sender As Object, e As EventArgs) Handles CheckCategoryButton.Click
CheckedListBox1.SetCategory(CategoryToCheckTextBox.Text, StateCheckBox.Checked)
End Sub
End Class
To check all at once
Private Sub CheckAllButton_Click(sender As Object, e As EventArgs) Handles CheckAllButton.Click
CType(CheckedListBox1.DataSource, List(Of Category)).
ForEach(Sub(cat) CheckedListBox1.SetCategory(cat.Name, True))
End Sub
You haven't mentioned exactly how the CheckedListBox gets populated by the DataSet, I've made the assumption that you add Strings directly to the Items collection.
This code will simply loop through the CheckedListBox and compare the values with the array, whatever the match result, the Checkbox is either ticked or cleared.
Dim theArray() As String = {"Apple", "Lemon"}
For counter As Integer = 0 To CheckedListBox1.Items.Count - 1
Dim currentItem As String = CheckedListBox1.Items(counter).ToString
Dim match As Boolean = theArray.Contains(currentItem.ToString)
CheckedListBox1.SetItemChecked(counter, match)
Next
Use the SetItemChecked method like this:
CheckedListBox1.SetItemChecked(CheckedListBox1.Items.IndexOf("your item goes here"), True)
Note that if the item does not exist, an exception will be thrown, so be sure to check for the item before calling the SetItemChecked() method. To do that, you can check for the return value of IndexOf(). It will be -1 if the item does not exist.

How to return specific properties of a custom vb.net object

I'm trying to generate a list of all of the TableName and FieldName properties for a custom object type called LxTextBox. I've gotten as far as generating a list of all of the LxTextBox names on my form, but I can't figure out a way to call the properties of the custom object... I've been looking into System.Reflection, but I haven't ever used it. Additionally, I'm returning the list to a RichTextBox while I'm testing this out, but ultimately, I need to return each objects properties as a data row. Example:
ObjectName Table Field
---------------------------------------
LxTextBox23 SomeTbl SomeFld
Here's my code to return the list - updated based on #OneFineDay...
Imports System.Collections.Generic
Imports Application.UDF.Controls
Public Class MeasurementsControl
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim textBoxList As New List(Of Control)
Dim customTbs = GetAllControls(Me)
Dim sb As New System.Text.StringBuilder
For index As Integer = 0 To customTbs.Count - 1
sb.Append(customTbs.Item(index).TableName & "." & customTbs.Item(Index).FieldName & System.Environment.NewLine)
Next
RichTextBox1.Text = sb.ToString
End Sub
Private Function GetAllControls(ByVal searchWithin As Control) As List(Of LxTextbox)
Dim returnList As List(Of LxTextbox) = Nothing
If searchWithin.HasChildren Then
returnList = searchWithin.Controls.OfType(Of LxTextbox).ToList
For Each ctrl As Control In searchWithin.Controls
returnList.AddRange(GetAllControls(ctrl))
Next
End If
Return returnList
End Function
End Class
I made the changes suggested and I'm throwing an error: OfType is not a member of System.Windows.Forms.Control.ControlCollection
FYI - Adding Imports System.Linq did not fix the error.
You are boxing it into a Control the object from where it derives, where you're custom properties cannot be found. You can cast it right from the control collection.
Dim customTbs = GetAllControls(Me)
'recursive function
Private Function GetAllControls(ByVal searchWithin As Control) As List(Of LxTextbox)
Dim returnList As List(Of LxTextbox) = Nothing
returnList = searchWithin.Controls.OfType(Of LxTextbox).ToList
If searchWithin.HasChildren Then
For Each ctrl As Control In searchWithin.Controls
Dim ctrls = GetAllControls(ctrl)
If Not ctrls Is Nothing Then returnList.AddRange(ctrls)
Next
End If
Return returnList
End Function

How to filter an object list based on an unknown property

I created a user control consisting of a text box and data grid.
(code not included on the sample below).
I need to filter the content of the data grid by a specific column.
The name of the column depends on the name of the object properties.
The object will vary, however each control will be bound to a specific object.
How I am using the control example:
Public Class Form1
Private ObjectList As List(Of Object)
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
ObjectList = GenerateRandomData()
AddHandler Me.MyUserControl.SelectionStatusChanged, AddressOf UpdateForm
AddHandler Me.MyUserControl.TextChanged, AddressOf UpdateList
Me.MyUserControl.DataSource = ObjectList
End Sub
Private Sub UpdateList()
Dim FilteredList As IEnumerable(Of Object) = From obj As Item In ObjectList
Where obj.Prop_1.StartsWith(Me.MyUserControl.Text)
Select obj
Me.MyUserControl.DataSource = FilteredList.ToList
End Sub
Private Sub UpdateForm()
If Me.MyUserControl.HasItemLoaded Then
Dim NewItem As Item = CType(Me.MyUserControl.SelectedItem, Item)
TextBox1.Text = NewItem.Prop_1
TextBox2.Text = NewItem.Prop_2
TextBox3.Text = NewItem.Prop_3
Return
End If
TextBox1.Text = ""
TextBox2.Text = ""
TextBox3.Text = ""
End Sub
Private Function GenerateRandomData() As List(Of Object)
Randomize()
Dim lst As New List(Of Object)
For i = 0 To 10000
Dim itm As New Item
Dim value As Integer = CInt(Int((999999 * Rnd()) + 1))
Dim value2 As Integer = CInt(Int((999999 * Rnd()) + 1))
itm.Prop_1 = value
itm.Prop_2 = "abc " & value & value2
itm.Prop_3 = value2 & value & " abc"
lst.Add(itm)
Next
Return lst
End Function
End Class
I would like to handle the work of UpdateList() inside the User control (move the code or equivalent result) but the problem is that the control is unaware of the object type so I cannot directly call Prop_1 or whatever is the name of the property from inside the user control.
So far I am accomplishing this by listening for events outside the user control but I will like to remove as much responsibility from the programmer.
Any ideas are welcome.
The following is designed around its SetList method that is used to set the source list (IEnumerable(Of T)) and the name of the property to filter on in the UpDateList method. It use Reflection to retrieve the desired property.
Public Class UserControl1
Private list As IEnumerable
Private filterPropInfo As Reflection.PropertyInfo
Public Sub SetList(Of T)(list As IEnumerable(Of T), filterPropertyName As String)
filterPropInfo = GetType(T).GetProperty(filterPropertyName, GetType(String))
If filterPropInfo Is Nothing Then
Throw New ArgumentException(String.Format("{0} is not a Public String Property on Type: {1}", filterPropertyName, GetType(T).FullName))
End If
Me.list = list
End Sub
Public Sub UpdateList()
Dim FilteredList As IEnumerable(Of Object) = From obj In list
Where CStr(filterPropInfo.GetValue(obj)).StartsWith(Me.Text)
Select obj
Me.DataGridView1.DataSource = FilteredList.ToList
End Sub
End Class
Example usage by calling the test method.
Public Class Form1
Sub test()
Dim l As New List(Of Item)
l.Add(New Item("fred"))
l.Add(New Item("freddy"))
l.Add(New Item("fredrick"))
l.Add(New Item("joe"))
UserControl11.[Text] = "fred"
UserControl11.SetList(l, "Field1")
UserControl11.UpdateList()
End Sub
End Class
Class Item
Public Property Field1 As String
Public Property Field2 As Int32
Public Sub New(f1 As String)
Me.Field1 = f1
End Sub
End Class

Using string as object name

I'm trying to use string as object name. Example I have an object and has a name = Label1. Can I do this?
Dim i As String = "Label1"
someVariable = i.Text
I'm using string as object name, is it possible?
You could iterate over all of the controls as #Christian Sauer said but you might run into problems if any controls are containers of controls. You'd need to do a recursive search to solve that. However, the ControlCollection actually has a Find() method that you can use. It returns an array of controls that match the name and optionally performs a recursive search.
''//Our final control
Dim someVariable As Control = Nothing
''//Search recursively for our control
Dim SearchedControls = Me.Controls.Find(key:="Label1", searchAllChildren:=True)
''//If we found one and only one control
If SearchedControls.Count = 1 Then
''//Set it to our variable
someVariable = SearchedControls(0)
Else
''//Whatever your logic dictates here
End If
This is not possible - but what you can do:
Dim i As String = "Label1"
Dim Obj as Label
for each elem in me.controls
if elem.Name = i then
Obj = elem
exit for
end if
next
someVariable = obj.Text
I am iterating over all WinForms control to find the label with the Name "Label1" - when found, i assign the label to a Variable.
This works, but can be quite dangerous, especially if you add controls
I know it's been answered, but this is from my library, and I use it all the time. It will iterate over all controls, and containers' controls recursively as #ChrisHaas suggested.
Public Function GetControlByName(ByRef parent As Control, ByVal name As String) As Control
For Each c As Control In parent.ChildControls
If c.Name = name Then
Return c
End If
Next
Return Nothing
End Function
<Extension()> _
Public Function ChildControls(ByVal parent As Control) As ArrayList
Return ChildControls(Of Control)(parent)
End Function
<Extension()> _
Public Function ChildControls(Of T)(ByVal parent As Control) As ArrayList
Dim result As New ArrayList()
For Each ctrl As Control In parent.Controls
If TypeOf ctrl Is T Then result.Add(ctrl)
result.AddRange(ChildControls(Of T)(ctrl))
Next
Return result
End Function
(It's been asked and answered before)
Loop Through Controls on Web User Control
I'm sure it's answered but some points to be clear it's control array and result so as to be sure it's corrected.
Private Sub btn1_click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn1.Click
Dim Ds As New DataSet
Dim str As String = ""
str = " SELECT TOP (10) t_Suppliers.STNo from t_Suppliers "
Ds = SqlHelper.ExecuteDataset(ConnectionString, CommandType.Text, str)
For i As Integer = 0 To Ds.Tables(0).Rows.Count - 1
Dim str1 As String = "lblInv" & i + 1
Dim OBj As New Label
Try
Dim SearchedControls() As Control = Me.Controls.Find(key:=str1, searchAllChildren:=True)
If SearchedControls.Length > 0 Then
SearchedControls(0).Text = Ds.Tables(0).Rows(i).Item("STNo").ToString
End If
Catch
End Try
Next
End Sub
I found the following solution on another site.
It works.
--Quote -
Dim TextBox As TextBox
Dim I As Integer = 2
Dim name As String = "TextBox" & I.ToString
TextBox = Me.Controls.Item(name)
TextBox.Text = "Something special"

Using Generic List(Of Form), Trouble gathering Object's Name Property

I have been very interested as of late in interfaces and the ability to further customize them beyond using them in their default state.
I have been researching IList(of T) specifically. The advantages of using generic lists as opposed to ArrayLists has astounded me. Here is a picture of a test. This is the site that goes into further explanation about the Test.
So, naturally I wanted to experiment. When I first iterate through the list with the ForNext method the code works fine. The second time I can't access the name of the Form in the list because it is disposed. Anyone have any insight how I can access the forms properties in the list.
Public Class frmMain
Dim Cabinet As List(Of Form) = New List(Of Form)
Dim FormA As New Form1
Dim FormB As New Form2
Dim FormC As New Form3
Private Sub frmMain_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles _Me.Load
Cabinet.Add(FormA)
Cabinet.Add(FormB)
Cabinet.Add(FormC)
End Sub
Sub displayForm(ByVal aForm As Form)
Dim myFormName As String = ""
Stopwatch.Start()
If aForm.IsDisposed = False Then
aForm.Show()
Else
myFormName = aForm.(How do I access this objects Name?)
aForm = New Form '<----- I would rather simply use aForm = New(aForm)
aForm.Name = myFormName
aForm.Show()
End If
Stopwatch.Stop()
Dim RealResult As Decimal = (Stopwatch.ElapsedMilliseconds / 1000)
Debug.WriteLine(RealResult)
Stopwatch.Reset()
End Sub
Private Sub btnForEach_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnForEach.Click
'Dim instance as List
'Dim action as Action(of T)
'instance.ForEach(action)
'action = delegate to a method that performs an action on the object passeed to it
Cabinet.ForEach(AddressOf displayForm)
End Sub
I really don't understand why if VB knows that this is a Generic list, which means it is knowledgable of the list's type, and the objects are all constrained to be forms; why I can't call a constructor on an item in the list. Ex. aForm = New aForm or aForm = New Cabinet.aForm
Tear this one open for me somebody. Thanks.
You can't construct a new instance of "aForm" because its isn't a type, it is an instance of type Form.
If you wanted to prevent the ObjectDisposedException, you could hide the form instead of closing it. Place the following code in each forms code behind:
Public Class Form1
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
Dim form = CType(sender, Form)
form.Visible = False
e.Cancel = True
End Sub
End Class
This is a bit hacky, however, but then you wouldn't need the code in the Else block.
Edit
You could try this instead:
Private Sub displayForm(ByVal aForm As Form)
Dim indexOfCab As Integer = Cabinet.IndexOf(aForm)
If indexOfCab <> -1 Then
If aForm.IsDisposed Then
aForm = CreateForm(aForm.GetType())
Cabinet(indexOfCab) = aForm
End If
aForm.Show()
End If
End Sub
Private Shared Function CreateForm(formType As Type) As Form
Return CType(Activator.CreateInstance(formType), Form)
End Function
You wouldn't need that big Select statement.
This is the only way I have been able to get it to work. I feel it is extremely inefficient however, and hope someone can set me on a path to a better way to do this. The below is what I'm trying to achieve.
Sub displayForm(ByVal aForm As Form)
Dim myFormName As String = ""
If Cabinet.Contains(aForm) Then
Dim indexOfCab As Integer = Cabinet.IndexOf(aForm)
Dim ObjForm As Form = Cabinet.Item(indexOfCab)
If aForm.IsDisposed Then
Select Case indexOfCab
Case 0
aForm = Nothing
aForm = New Form1
Cabinet.Item(indexOfCab) = aForm
Cabinet.Item(indexOfCab).Show()
Case 1
aForm = Nothing
aForm = New Form2
Cabinet.Item(indexOfCab) = aForm
aForm.Show()
Case 2
aForm = Nothing
aForm = New Form3
Cabinet.Item(indexOfCab) = aForm
Cabinet.Item(indexOfCab).Show()
End Select
Else
Cabinet.Item(indexOfCab).Show()
End If
End If
End Sub